Convert array take string - php

I would like to know how to convert my array to a string
$formats = $_POST['formats'];
$topics = $_POST['topics'];
for example, if I echo out the above, it just prints array. I want it to display the array as a string so that I could use it below:
$resources = "select * from resources where stage LIKE '%".$stage."%' and formats LIKE '%".$formats."%' and topics LIKE '%".$topics."%'";
I have been suggested to do something like this $formats = $_POST['formats'][0];
but i want to output the entire array as a string such "idea generation, business" would be equivilant to ["idea generation", business"]

Since it isn't possible to determine which database you're using to make that query happen, I'll suggest you to build your query string using prepared statements and paremeterizing your values to a PDO object based on what you may read in PHP.net documentation on the subject.
Binds a PHP variable to a corresponding named or question mark
placeholder in the SQL statement that was used to prepare the
statement. Unlike PDOStatement::bindValue(), the variable is bound as
a reference and will only be evaluated at the time that
PDOStatement::execute() is called.
As you'll see, that way you won't have to bother converting your arrays and variables to string before accessing them, plus, you grant security to your query statements.
So, instead of implodeing a string, you'll have something like this:
<?php
/* Execute a prepared statement by binding PHP variables */
$stage = $_POST['stage'];
$formats = $_POST['formats'];
$topics = $_POST['topics'];
$stmt = $db->prepare('select * from resources where stage LIKE % :stage % and formats LIKE % :formats % and topics LIKE % :topics %');
$stmt->bindParam(':stage', $stage);
$stmt->bindParam(':formats', $formats);
$stmt->bindParam(':topics', $topics);
$stmt->execute();
?>
EDIT: as you updated that you're using MySQLi, it'll be no different.
$stmt = $mysqli_db->prepare('select * from resources where stage LIKE % ? % and formats LIKE % ? % and topics LIKE % ? %');
// assuming all your params are strings
$stmt->bind_param('sss', $stage, $formats, $topics);
$stmt->execute();
As using mysqli, since it's an unbuffered sql query handler, you should store your results if you're looping simultaneous executions with $stmt->store_result();
Any doubts about how to use mysqli and pdo objects, methods and properties can be easily found in php.net documentation (linked above).
Of course, it's just a suggeston of better practices based on your apparent needs, but you can still use the implode function to achieve your string.

Take a look at PHP implode function:
http://php.net/manual/en/function.implode.php
For example, this will turn an array into a string, separating each element with a comma:
$string = implode(',', $array);

Related

Comma-separated values doesn't work in PHP MYSQL [duplicate]

This question already has answers here:
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Build SELECT query with dynamic number of LIKE conditions as a mysqli prepared statement
(2 answers)
Closed 11 months ago.
I'm currently facing a difficulty where putting a comma-separated values to a MySQL NOT IN doesn't give me the result I was hoping for. There must be something I'm missing as I'm unsure what to search for this particular problem. Running only the MySQL code works, but passing the parameter from another PHP function didn't.
Here's the code that's giving me a problem:
$uid = 1;
$selected_uids = '1,2';
$result = $db->retrieveFollowingWithCondition($uid, $selected_uids);
...then somewhere along the code...
public function retrieveFollowingWithCondition($uid, $selected_uids) {
$stmt = $this->conn->prepare("SELECT *
FROM `friendlist`
WHERE `uid` = ? AND `buddy_uid` NOT IN (?)
GROUP BY `buddy_uid`;");
$stmt->bind_param("is", $uid, $selected_uids);
...}
I've tested just putting '2' in $selected_uids and it actually works. But once there's comma involved, the code runs but the $selected_uids are still in the result. Not sure this is a bad practice or just needing a minor adjustment to the code. Anyway, I'm really looking forward to understand why it's not working for me.
By using s in bind_param you are telling PHP to treat the entire contents of $selected_uids as a string. Therefore, "1,2" is treated as ('1,2') instead of (1,2). Your problem is that bind_param doesn't support arrays, so support of IN queries is limited. There are a number of alternatives to get around this limitation, but since you are dealing with a list of ints, I would probably do a raw string concat.
// using is_numeric because is_int("1") === false
$filtered = array_filter('is_numeric', $selected_uids);
// You could also just call array_map('intval', $selected_uids);
// Depending on your needs.
if(!$filtered) {
return; // No valid values
}
$filteredStr = implode(',', $filtered);
$stmt = $this->conn->prepare("SELECT *
FROM `friendlist`
WHERE `uid` = ? AND `buddy_uid` NOT IN ($filteredStr)
GROUP BY `buddy_uid`;");
Should also be noted: if I were trying to use strings for an IN query, I would likely do the following:
$filtered = array_map([$this->conn, 'escape_string'], $queried);
$inQuery = '\'' . implode('\',\'', $filtered) . '\'';
I find that notation cleaner and easier than a dynamically generated bind_param format string.
You should bind every parameter in IN(...) separately, but method bind_param doesn't support multiple calls. There is a nice class that can do this and you can find it on PHP documentation pages:
Custom class for multiple bind_param

Is $_SESSION safe from sql injects?

I use PDO to access my MySQL database, and want to use IN. But sadly it don't seam to work with prepare, so I wrote this function
function is_numeric_array($array){
if(!is_array($array))
return is_numeric($array);
if(is_array($array))
foreach($array as $int)
if(!is_numeric($int))
return false;
return true;
}
Then used it like this
if(!is_numeric_array($_SESSION['story'])){
die("Error, array contains non-integers");
}
$query = "(";
for($i = 0; $i<count($_SESSION['story']); $i++)
$query .= $_SESSION['story'][$i].(count($_SESSION['story'])-1 != $i ? "," : "");
$query .= ")";
//Collect all data needed
$stories = openConnection() -> query("SELECT * FROM `stories` WHERE `id` IN {$query}") -> fetchAll();
I know it, looks ugly. But I don't want any SQL injects.
You don't really have to test for the input being numeric, because in MySQL, any string e.g. '123abc' in a numeric context (like being compared to an integer column id) implicitly takes only the digits and ignores the rest. A non-numeric string like 'abc' simply has the integer value 0 because there are no leading digits.
The point is, values are safe from SQL injection if you use query parameters. Whether the inputs come from $_SESSION or another source is irrelevant. $_SESSION is neither safe or unsafe with respect to SQL injection, it's how you pass the data to your query that matters.
I would also simplify the code to format the list of parameter placeholders:
$placeholders = implode(',', array_fill(1, count((array)$_SESSION['story']), '?'));
And forget about bindParam(), just pass the array to execute().
//Collect all data needed
$storyQuery = openConnection() -> prepare("SELECT * FROM `stories`
WHERE `id` IN ({$placeholders})");
$storyQuery -> execute((array)$_SESSION['story']);
$story = $storyQuery -> fetchAll();
Re your comment:
In PDO, you can use either named parameters like :id, or you can use positional parameters, which are always ? (but don't mix these two types in a given query, use one or the other).
Passing an array to execute() automatically binds the array elements to the parameters. A simple array (i.e. indexed by integers) is easy to bind to positional parameters.
If you use named parameters, you must pass an associative array where the keys of the array match the parameter names. The array keys may optionally be prefixed with : but it's not required.
If you're new to PDO, it really pays to read the documentation. There are code examples and everything!
$_SESSION is just a way to store data on server over a session. It's not direct related with SQL injection.
Even if it's a cookie or a session , the hash that I store is alphanumeric only, for security purposes. When I'm checking the cookie/session against any type of inject / modification , I use ctype_alnum:
if (ctype_alnum($_SESSION['value']))
// exec code
else
// trigger_error
This way, no matter who is setting the SESSION value (you or the client, if you give him the possibility to), there wont be any case in which non-alphanumeric chars like comma, quotes, double quotes or whatever will be inserted.

PHP/MySQL: Using array elements in WHERE clause using prepared statements

I want to make a "dynamic" WHERE clause in my query based on a array of strings. And I want to run the created query using Mysqi's prepared statements.
My code so far, PHP:
$searchArray = explode(' ', $search);
$searchNumber = count($searchArray);
$searchStr = "tags.tag LIKE ? ";
for($i=1; $i<=$searchNumber-1 ;$i++){
$searchStr .= "OR tags.tag LIKE ? ";
}
My query:
SELECT tag FROM tags WHERE $searchStr;
More PHP:
$stmt -> bind_param(str_repeat('s', count($searchArray)));
Now this obviously gives me an error since the bind_param part only contains half the details it need.
How should I proceed?
Are there any other (better) way of doing this?
Is it secure?
Regarding the security part of the question, prepared statements with placeholders are as secure as the validation mechanism involved in filling these placeholders with values up. In the case of mysqli prepared statements, the documentation says:
The markers are legal only in certain places in SQL statements. For example, they are allowed in the VALUES() list of an INSERT statement (to specify column values for a row), or in a comparison with a column in a WHERE clause to specify a comparison value.
However, they are not allowed for identifiers (such as table or column names), in the select list that names the columns to be returned by a SELECT statement, or to specify both operands of a binary operator such as the = equal sign. The latter restriction is necessary because it would be impossible to determine the parameter type. It's not allowed to compare marker with NULL by ? IS NULL too. In general, parameters are legal only in Data Manipulation Language (DML) statements, and not in Data Definition Language (DDL) statements.
This clearly excludes any possibility of modifying the general semantic of the query, which makes it much harder (but not impossible) to divert it from its original intent.
Regarding the dynamic part of your query, you could use str_repeat in the query condition building part, instead of doing a loop:
$searchStr = 'WHERE tags.tag LIKE ?' .
str_repeat($searchNumber - 1, ' OR tags.tag LIKE ?');
For the bind_param call, you should use call_user_func_array like so:
$bindArray[0] = str_repeat('s', $searchNumber);
array_walk($searchArray,function($k,&$v) use (&$bindArray) {$bindArray[] = &$v;});
call_user_func_array(array($stmt,'bind_param'), $bindArray);
Hopefully the above snippet should bind every value of the $bindArray with its corresponding placeholder in the query.
Addenum:
However, you should be wary of two things:
call_user_func_array expects an integer indexed array for its second parameter. I am not sure how it would behave with a dictionary.
mysqli_stmt_bind_param requires its parameters to be passed by reference.
For the first point, you only need to make sure that $bindArray uses integer indices, which is the case in the code above (or alternatively check that call_user_func_array doesn't choke on the array you're providing it).
For the second point, it will only be a problem if you intend to modify the data within $bindArray after calling bind_param (ie. through the call_user_func_array function), and before executing the query.
If you wish to do so - for instance by running the same query several times with different parameters' values in the same script, then you will have to use the same array ( $bindArray) for the following query execution, and update the array entries using the same keys. Copying another array over won't work, unless done by hand:
foreach($bindArray as $k => $v)
$bindArray[$k] = some_new_value();
or
foreach($bindArray as &$v)
$v = some_new_value();
The above would work because it would not break the references on the array entries that bind_param bound with the statement when it was called earlier. Likewise, the following should work because it does not change the references which have been set earlier up.
array_walk($bindArray, function($k,&$v){$v = some_new_value();});
A prepared statement needs to have a well-defined number of arguments; it can't have any element of dynamic functionality. That means you'll have to generate the specific statement that you need and prepare just that.
What you can do – in case your code actually gets called multiple times during the existence of the database connection - is make cache of those prepared statements, and index them by the number of arguments that you're taking. This would mean that the second time you call the function with three arguments, you already have the statement done. But as prepared statements don't survive the disconnect anyway, this really only makes sense if you do multiple queries in the same script run. (I'm deliberately leaving out persistent connections, because that opens up an entirely different can of worms.)
By the way, I'm not an MySQL expert, but would it not make a difference to not have the where conditions joined,but rather writing WHERE tags in (tag1, tag2, tag3, tag4)?
Solved it by the help of an answer found here.
$query = "SELECT * FROM tags WHERE tags.tag LIKE CONCAT('%',?,'%')" . str_repeat(" OR tags.tag LIKE CONCAT('%',?,'%')", $searchNumber - 1)
$stmt = $mysqli -> prepare($query);
$bind_names[] = str_repeat('s', $searchNumber);
for ($i = 0; $i < count($searchArray); $i++){
$bind_name = 'bind'.$i; //generate a name for variable bind1, bind2, bind3...
$$bind_name = $searchArray[$i]; //create a variable with this name and put value in it
$bind_names[] = & $$bind_name; //put a link to this variable in array
}
call_user_func_array(array($stmt, 'bind_param'), &$bind_names);
$stmt -> execute();

PDO bind parameters

I'm building simple query builder, and I have two questions:
Is it possible to secure mysql queries with normal functions to the similar level as it is done using ->execute(array(':param:' => ... ?
Is it possible to use many variables in one query, give them the same names (the ones after the semicolon), and then bind them one by one?
If I understand you correctly, you would like to know if it possible to replicate the functionality of bindParam with the standard mysql_* functions?
Short answer is no. Please do not use the mysql functions at all, use mysqli or PDO as these provide you with the true security when it comes to prepared statements. They can also provide much better query performance as the SQL is able to be pre-optimised for the database.
You will have to define each parameter separately (even if it is the same value). You could also pass a simple array to the execute() method call, but you do not then have the option to explicitly define the parameter types.
Within your function use some thing like this:
$name = "fred";
$statement = $pdo->prepare("SELECT id FROM contacts WHERE first_name = ? OR last_name = ?");
for ($x = 1; $x <= 2; $x++) {
$statement->bindParam($x, $name, PDO::PARAM_STR);
}
$statement->execute();

PDO quote method

Where and when do you use the quote method in PDO? I'm asking this in the light of the fact that in PDO, all quoting is done by the PDO object therefore no user input should be escaped/quoted etc. This makes one wonder why worry about a quote method if it's not gonna get used in a prepared statement anyway?
When using Prepared Statements with PDO::prepare() and PDOStatement::execute(), you don't have any quoting to do : this will be done automatically.
But, sometimes, you will not (or cannot) use prepared statements, and will have to write full SQL queries and execute them with PDO::exec() ; in those cases, you will have to make sure strings are quoted properly -- this is when the PDO::quote() method is useful.
While this may not be the only use-case it's the only one I've needed quote for. You can only pass values using PDO_Stmt::execute, so for example this query wouldn't work:
SELECT * FROM tbl WHERE :field = :value
quote comes in so that you can do this:
// Example: filter by a specific column
$columns = array("name", "location");
$column = isset($columns[$_GET["col"]]) ? $columns[$_GET["col"]] : $defaultCol;
$stmt = $pdo->prepare("SELECT * FROM tbl WHERE " . $pdo->quote($column) . " = :value");
$stmt->execute(array(":value" => $value));
$stmt = $pdo->prepare("SELECT * FROM tbl ORDER BY " . $pdo->quote($column) . " ASC");
and still expect $column to be filtered safely in the query.
The PDO system does not have (as far as I can find) any mechanism to bind an array variable in PHP into a set in SQL. That's a limitation of SQL prepared statements as well... thus you are left with the task of stitching together your own function for this purpose. For example, you have this:
$a = array(123, 'xyz', 789);
You want to end up with this:
$sql = "SELECT * FROM mytable WHERE item IN (123, 'xyz', 789)";
Using PDO::prepare() does not work because there's no method to bind the array variable $a into the set. You end up needing a loop where you individually quote each item in the array, then glue them together. In which case PDO::quote() is probably better than nothing, at least you get the character set details right.
Would be excellent if PDO supported a cleaner way to handle this. Don't forget, the empty set in SQL is a disgusting special case... which means any function you build for this purpose becomes more complex than you want it to be. Something like PDO::PARAM_SET as an option on the binding, with the individual driver deciding how to handle the empty set. Of course, that's no longer compatible with SQL prepared statements.
Happy if someone knows a way to avoid this difficulty.
A bit late anwser, but one situation where its useful is if you get a load of data out of your table which you're going to put back in later.
for example, i have a function which gets a load of text out of a table and writes it to a file. that text might later be inserted into another table. the quote() method makes all the quotes safe.
it's real easy:
$safeTextToFile = $DBH->quote($textFromDataBase);

Categories