This is my function:
function countRows($sql, $parameters){
$db = new PDO("mysql:host=localhost;dbname=cms","root");
$result = $db->prepare($sql);
foreach ($parameters as $key => $parameter) {
$result->bindParam($key, $parameter);
}
$result->execute();
$number_of_rows = $result->fetchAll();
return count($number_of_rows);
}
With $parameters array like this it works just fine:
$parameters=array("key"=>"parameter");
But when the array has more keys and variables it just will give result 0
For example with array like this it gives me 0 rows, when it should be 3
$parameters=array("key"=>"parameter", "key2"=>"parameter2");
Edit:
Example of an query:
"SELECT * FROM users WHERE username=:username AND password=:password"
Example of an $parameters for this query:
$parameters = array(":username"=>$username, ":password"=>$password);
When i run it with one column (like only username or only password) it runs fine, with both it returns always 0.
The problem is that you're using bindParam() for a variable that changes its value:
foreach ($parameters as $key => $parameter) {
$result->bindParam($key, $parameter);
}
You have to understand that bindParam() binds the variable by reference, not the value the variable has at the time you do the bind.
You're binding the variable $parameter to both parameters in your query. The value isn't sent until you call execute(). So it sends the value that the variable has after the loop is finished, which is the password.
I tested this by enabling the general query log on my MySQL server, and observing what gets logged:
140905 10:09:45 49 Connect root#192.168.56.1 on test
49 Prepare SELECT * FROM users WHERE username=? AND password=?
49 Execute SELECT * FROM users WHERE username='password' AND password='password'
49 Close stmt
49 Quit
You can solve this in either of two ways:
Use bindValue() instead of bindParam(). This copies the value of $parameter at the time you do the bind.
Alternatively, you don't need to do a loop for binding parameters one at a time all, just pass the array to execute():
$result->execute($parameters);
I don't know why so many developers believe they're required to write code to bind parameters one at a time. It's easier to pass the array of parameters directly to execute().
Your parameters should look like array
$parameters = [
"key" => "parameter",
"key2 " => "parameter2"
];
Then You missed the password
$db = new PDO("mysql:host=localhost;dbname=cms","root","your_password");
My idea take a look on PDOStatement::bindParam Docs
Related
Table Items:
Source
$query = "SELECT * FROM `tbl_items` WHERE `name` LIKE '%cell%' AND `is_active`=1";
// the query is generated dynamically so i do have a dynamic params too
$params = [
'name' => '%cell%',
'is_active' => 1,
];
$prepared = $db->prepare($query);
foreach($params as $key => $val)
{
if(is_int($val))
{
$prepared->bindParam(':'.$key, $val, PDO::PARAM_INT);
}
else
{
$prepared->bindParam(':'.$key, $val);
}
}
upon execution it returns empty result ..
Not Working:
multiple parameters .. (except i manually bind the parameters)
$prepared->bindParam(':name', $params['name']);
$prepared->bindParam(':is_active', $params['is_active']);
Working:
single parameter .. (it returns exactly what i needed)
Now my question, what could be the reason why it returns empty when looping the bindParam() ?
bindParam takes its value by reference. Meaning, at the time you do execute(), it takes the then current value of whatever is assigned to $val. Which obviously is probably not what you expect at the end of the loop.
Use bindValue instead, which binds the value immediately, instead of a variable reference.
I have recently asked some questions about security against SQL injection vulnerabilities. I decided to make a function that would do a sql query using a prepared statement so I didn't have to write out so many lines of code for every query:
function secure_sql($query, $values, $datatypes){
global $link;
$stmt = mysqli_prepare($link, $query);
foreach($values as &$value){
$value = mysqli_real_escape_string($link, $value);
mysqli_stmt_bind_param($stmt, substr($datatypes, 0), $value);
$datatypes = substr($datatypes, -(strlen($datatypes)-1));
}
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, implode(", ", $values));
$results = mysqli_stmt_get_result($stmt);
return $results;
}
where query is the prepared query (with ?s), $values is an array of all the variables replacing the placeholder ?s (in order) and $datatypes is a string containing all the data types for the variables. $link is a database connection.
So I have two main questions.
1) It is not working. I think this must be because of implode maybe not being used correctly in this context. What would I use instead? I can't use call_user_func_array because I also need to have $stmt as an argument. I have tried using array_unshift to add $stmt to the beginning of the argument, but it doesn't work.
2) If I do get it to work, what could be done to improve it? I am still a PHP and SQL beginner.
EDIT: I have solved the problem now. Thank you all for your helpful comments and answers!
Instead of directly giving you the code I came up with, I feel that explaining it is necessary, seeing how some of your ways of thinking are rather incorrect.
Firstly, the use of mysqli_stmt_bind_param() confuses me - your second argument expression (substr($datatypes, 0)) returns a value of all the datatypes, yet you are only binding one. What I think you meant to put is:
mysqli_stmt_bind_param($stmt, $datatypes[0] /* <-- retrieves the first character */, $value);
But more importantly, you should only call mysqli_stmt_bind_param() once, which gives you some bigger difficulties... To omit the foreach-loop, how about call_user_func_array()? (Actually, it's very possible to keep $stmt as an argument):
call_user_func_array("mysqli_stmt_bind_param", array_merge(array($stmt, $datatypes), $values));
//calls mysqli_stmt_bind_param($stmt, $datatypes, $values[0], $values[1]... values[n]);
If you're confused by the thing above, look at it in this way:
call_user_func_array //a call to mysqli_stmt_bind_param, with all the appropriate parameters
(
"mysqli_stmt_bind_param", //the function to call
array_merge //an array consisting of the statement, the datatype and all the values
(
array($stmt, $datatypes), //statement and datatype-parameters
$values //all the values
)
);
This does, however, require your $values-array to consist of references, as the mysqli_stmt_bind_param expects your values to be so. If you still want to pass them as values into your function, you could add this, and later pass $ref_values into the call_user_func_array() function:
foreach ($values as &$value) $ref_values[] = &$value;
Now we come to the use of implode(), which also is incorrect. To reference from the PHP-manual:
Implode (Return Value):
Returns a string containing a string representation of all the array elements in the same order, with the glue string between each element.
mysqli_stmt_bind_result (Second-Nth Parameter):
The variable to be bound.
So what you're attempting to do here is to make a string returned by implode() a variable, which makes no sense. Although luckily, mysqli_stmt_get_result() returns an object which is fetched after execution, meaning that the bind_result-function isn't needed. So try removing that line.
In all, I would re-write the code like this:
function secure_sql($query, $values, $datatypes) {
global $link;
$stmt = mysqli_prepare($link, $query);
foreach ($values as &$value) $ref_values[] = &$value;
call_user_func_array("mysqli_stmt_bind_param", array_merge(array($stmt, $datatypes), $ref_values));
mysqli_stmt_execute($stmt);
return mysqli_stmt_get_result($stmt);
}
To me, it sounds like that should work, but if it doesn't, tell me (I might be missing something or thinking incorrectly somewhere).
To answer the second question, it all looks good, except that I wouldn't advice you to use mysqli_-functions, as they will get removed eventually (as said in the PHP-manual). If you're planning to use objects instead, most of it is similar when it comes to my changes (apart from the fact that you need to use object-properties and object-methods with them instead...), except the call_user_func_array() function. Luckily though, calling a method with it is possible as well, by specifying an array as the first parameter, consisting of the object and the method name (call_user_func_array($prepared_obj, "bind_param") ...)).
Edit: Considering how necessary it is, I made a function that does the same thing, but works on an mysqli object instead:
function secure_sql($query, $values, $datatypes) {
global $mysqli_object; //declared with $mysql_object = new mysqli(...)
$stmt = $mysqli_object->prepare($query);
foreach ($values as &$value) $ref_values[] = &$value;
call_user_func_array(array($stmt, "bind_param"), array_merge(array($datatypes), $ref_values));
$stmt->execute();
return $stmt->get_result();
}
As I am writing a script, I am typing $db->prepare()'s and $stmt->bindParam()'s constantly. I am looking for a way to consolidate it all in a function. This is what I have so far.
$sql = "SELECT (name, email) FROM users WHERE VALUES (:name, :email)"
$values = array(':name' => 'my_name', ':email' => 'blahblah#example.com', );
function db_query($sql, $values) {
global $db; //Database object
$stmt = $db->prepare($sql);
foreach($values as $placeholder => $value) {
$stmt->bindParam($placeholder, $value);
}
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
Would this be sufficient for most queries? Is this a secure way to do this?
I just want the query to run and return whatever it returns (NULL, values, error, etc...).
Thanks.
Your code will not work as expected because bindParam() binds the placeholder name (first argument) to the variable reference present in the second argument.
Using your example, this would result in all parameters set to blahblah#example.com as it is the last $value in the loop.
As mentioned in the comments, simply use $stmt->execute($values). See http://php.net/manual/en/pdostatement.execute.php
If you really want to continue with your loop, use PDOStatement::bindValue() instead.
I'm trying to figure out how to convert my history script from mysql_query() to PDO. I have a form with 4 input fields which you can randomly select. Which means there can be 0, 1, 2, 3, 4 fields selected depending on which info you're trying to get.
I've tried to query db like this:
$q = $db->prepare('SELECT date,
name,
action
FROM history
WHERE name = :name
AND action = :action');
$q->bindParam(':name', $Name, PDO::PARAM_STR, 20);
$q->bindParam(':action', $Action, $PDO::PARAM_STR, 20);
$q->execute();
But this doesn't work if I don't have any fields selected and want the whole history shown.
With mysql_query() I'd just do this:
mysql_query('SELECT date,
name,
action
FROM history
$Name
$Action');
Which means if there's no $Name or $Action they're simply not included in the query.
Should I just copy/paste the old query into $q = $db-query('')? But that kind of defeats the purpose of using PDO.
You could always assign default values to the params which conform to the column names.
That way your query will in the default case end up as where column = column and when there is a value present it will be where column = value.
EDIT:
Of course, my logic was slightly flawed, since bindParam does not work that way. Instead, you should incrementally build your statement according to the params set.
/* Start with the most general case for the sql query.
* The where part always evaluates to true and will thus
* always return all rows and exists only to make appending
* further conditions easier.
*/
$q = 'SELECT date, name, action FROM history WHERE 1';
/* Prepare a params array in any way you wish. A loop might be more
* efficient if it is possible, but since in this example you have
* only 2 variables, it didn't seem necessary
*/
$params = array();
if (! empty($Name)) {
$params['name'] = $Name;
}
if (! empty($Action)) {
$params['action'] = $Action;
}
/* When the params array is populated, complete the sql statement by
* appending the param names joined with ANDs
*/
foreach ($params as $key => $value) {
$q .= sprintf(' AND `%s` = :%s', $key, $key);
}
/* When the query is complete, we can prepare it */
$stmt = $db->prepare($q);
/* Then bind the values to the prepared statement
*/
foreach ($params as $key => $value) {
// Using bindValue because bindParam binds a reference, which is
// only evaluated at the point of execute
$stmt->bindValue(':'.$key, $value);
}
/* Now we're ready to execute */
$stmt->execute();
In this example, the empty check could've been done in the loop where we complete the sql statement, but that would've given you a less general example.
This example also leaves out the type param to bindValue, but that would be easily implemented, e.g. by changing the array value to an object or array having the type as a member, or by duck typing inside the assigning loop.
The query building could in this form easily be put in a function that would work for all your database querying needs, as long as you provide it the initial (general case) query along with the params array.
I modified this code from somewhere but I am not sure if I am doing it correctly,
I use method to insert data into database,
# insert or update data
public function query($query, $params=array())
{
try
{
$stmt = $this->connection->prepare($query);
$params = is_array($params) ? $params : array($params);
$stmt->execute($params);
return true;
}
catch (PDOException $e)
{
# call the get_error function
$this->get_error($e);
}
}
Then I just need to call it like this,
$sql = "
INSERT root_countries_cities_towns (
tcc_names,
cny_numberic,
tcc_created
)VALUES(
?,
?,
NOW()
)";
$pdo->query($sql,array('UK','000'));
It works fine perfectly! but I don't understand what this line does - can someone explain please?
$params = is_array($params) ? $params : array($params);
I thought I have to use bindParam to bind the parameters first, but it seems that I don;t have to anymore with is method - is it safe and secure then??
Does it meant that I don't have to prepare the query in this way anymore?
$sql = "
INSERT root_countries_cities_towns (
tcc_names,
cny_numberic,
tcc_created
)VALUES(
:name,
:numberic,
NOW()
)";
and forget about this binding?
$stmt = bindParam(':name','UK', PDO::PARAM_STR);
$stmt = bindParam(':numberic','000', PDO::PARAM_STR);
Thanks.
I guess that's pretty much PHP syntax question rather than PDO one.
$params = is_array($params) ? $params : array($params);
is a shortland (called ternary operator)) for
if (is_array($params)) {
$params = $params;
} else {
$params = array($params);
}
which I'd rather wrote as
if (!is_array($params)) $params = array($params);
which is pretty self-explanatory and can be read almost in plain English:
if $params is not an array, let's make it array with one value of former $params
That's why I hate ternary operator (and lambdas) and always avoid it's use. It makes pretty readable code into a mess. Just out of programmer's laziness.
To answer your other questions,
Does it meant that I don't have to prepare the query in this way anymore?
Who said that? You're preparing it all right in your code, check it again.
and forget about this binding?
that's true. execute($params) is just another way to bind variables.
the line
$params = is_array($params) ? $params : array($params);
is simply checking if the $params variable is an array, and if so, it creates an array with the original $params value as its only element, and assigns the array to $params.
This would allow you to provide a single variable to the query method, or an array of variables if the query has multiple placeholders.
The reason it doesn't use bindParam is because the values are being passed to the execute() method. With PDO you have multiple methods available for binding data to placeholders:
bindParam
bindValue
execute($values)
The big advantage for the bindParam method is if you are looping over an array of data, you can call bindParam once, to bind the placeholder to a specific variable name (even if that variable isn't defined yet) and it will get the current value of the specified variable each time the statement is executed.
The first example transforms the contents of your $params into an array, if it wasn't already an array (for example if only one parameter was passed and it was passed as an individual item instead of as an array of length 1).
The two examples work just as well, except that for the first one, parameters introduced with the array $params are injected where ? are found in the SQL query, whereas in the second one, the formatting of the parameters is actually done by name (you bind a parameter name as found in the sql to an actual parameter).
You should use the first one, it's easier to write.
Passing an array to PDOStatement::execute() passes each entry in the array through PDOStatement::bindParam() (or maybe bindValues()) using defaults (bind type, etc).
Basically, it's as safe as pre-binding.
The advantage to using bindParam is that it binds to the variable reference. This means you can change the value of the variable without re-binding and execute the statement with new values. This is especially useful in a loop, eg
$vals = array('foo', 'bar', 'baz');
$stmt->bindParam(1, $val);
foreach ($vals as $val) {
$stmt->execute(); // Executes once for each value in $vals
}
I don't understand what this line does - can someone explain please?
That line converts a non-array into an array.
For example
$params = 'foo';
$params = is_array($params) ? $params : array($params);
$params == array('foo');
I imagine it's to facilitate situations where you have only one placeholder and one value to bind as PDOStatement::execute() can only be passed an array.
Try this class. I use PDO a lot and this is what I use all the time for my projects.PHP PDO Class on GitHub