I am trying to bind values to a query in PHP. I have done this successfully many times, but for some reason my code isn't working.
function get_movies($vars, $page) {
global $db;
$get_movies = $db->prepare('SELECT * FROM `movies` WHERE LOWER(genres) LIKE :genre AND `qualities` LIKE :quality AND `rating` >= :imdb_min AND `rating` <= :imdb_max AND `year` >= :year_min AND `year` <= :year_max ORDER BY id DESC');
$get_movies->bindValue(':genre', $vars['genre']);
$get_movies->bindValue(':quality', $vars['quality']);
$get_movies->bindValue(':imdb_min', $vars['imdb_min']);
$get_movies->bindValue(':imdb_max', $vars['imdb_max']);
$get_movies->bindValue(':year_min', $vars['year_min']);
$get_movies->bindValue(':year_max', $vars['year_max']);
try {
$get_movies->execute();
$movies = $get_movies->fetchAll(); // list of all movies fitting parameters
$movie_offset = ($page - 1) * VIDEOS_PER_PAGE;
$movies = array_slice($movies, $movie_offset, VIDEOS_PER_PAGE);
return $movies;
} catch (Exception $e) {
throw $e;
return false;
}
}
The above code does not work. No exception is thrown, but it returns 0 results. However, if I built the query manually (ex: replacing each :key with the $vars['key'] and preparing the statement from the resulting string) the query returns results perfectly fine.
Any tips would be greatly appreciated.
Edit:
Here's the $vars array passed to the $get_movies function.
$vars = array(
'genre' => "Action",
'quality' => 1080,
'imdb_min' => 0.1,
'imdb_max' => 10.0,
'year_min' => 2000,
'year_max' => 2019
);
When I use pdo, this is how I do my query
Eg
$stmt=db_conn->prepare ("SELECT * FROM table WHERE id = ? AND name = ? AND = uptime = ?");
$stmt->bindParam (1, $firstvariable);
$stmt->bindParam (2, $secondvariable);
$stmt->bindParam (3, $thirdvariable);
$stmt->execute();
$result=$stmt->fetchAll();
It Will bind respectfully to the blind parameters (?)
Try and ignore the movie offset and movies variables for now and fetch the results using foreach loop.
Try this & see. Hope it helps you.
I managed to fix the problem. When executing the query manually, the " characters surrounding strings in the LIKE comparison are interpreted as indicators that the value in between them is a string. When binding a value with " characters surrounding the string, the characters are included in the value itself. This is why no exception was thrown and the query returned 0 results. Thank you for the help.
Related
I'm using this code and I'm beyond frustration:
try {
$dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem
PDO::PARAM_NULL, null, '', all of them fail and throw this error:
Fatal error: Cannot pass parameter 2 by reference in /opt/...
You need to use bindValue, not bindParam
bindParam takes a variable by reference, and doesn't pull in a value at the time of calling bindParam. I found this in a comment on the PHP docs:
bindValue(':param', null, PDO::PARAM_INT);
P.S. You may be tempted to do this bindValue(':param', null, PDO::PARAM_NULL); but it did not work for everybody (thank you Will Shaver for reporting.)
When using bindParam() you must pass in a variable, not a constant. So before that line you need to create a variable and set it to null
$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);
You would get the same error message if you tried:
$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
When using INTEGER columns (that can be NULL) in MySQL, PDO has some (to me) unexpected behaviour.
If you use $stmt->execute(Array), you have to specify the literal NULL and cannot give NULL by variable reference.
So this won't work:
// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null
But this will work:
// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise
Tried this on MySQL 5.5.15 with PHP 5.4.1
For those who still have problems (Cannot pass parameter 2 by reference), define a variable with null value, not just pass null to PDO:
bindValue(':param', $n = null, PDO::PARAM_INT);
Hope this helps.
I had the same problem and I found this solution working with bindParam :
bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
If you want to insert NULL only when the value is empty or '', but insert the value when it is available.
A) Receives the form data using POST method, and calls function insert with those values.
insert( $_POST['productId'], // Will be set to NULL if empty
$_POST['productName'] ); // Will be to NULL if empty
B) Evaluates if a field was not filled up by the user, and inserts NULL if that's the case.
public function insert( $productId, $productName )
{
$sql = "INSERT INTO products ( productId, productName )
VALUES ( :productId, :productName )";
//IMPORTANT: Repace $db with your PDO instance
$query = $db->prepare($sql);
//Works with INT, FLOAT, ETC.
$query->bindValue(':productId', !empty($productId) ? $productId : NULL, PDO::PARAM_INT);
//Works with strings.
$query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);
$query->execute();
}
For instance, if the user doesn't input anything on the productName field of the form, then $productName will be SET but EMPTY. So, you need check if it is empty(), and if it is, then insert NULL.
Tested on PHP 5.5.17
Good luck,
Several answers have given examples of what you should do. But they haven't really explained why you should do one of those things.
The bindParam method is meant to be used with something like a loop (or just repeated statements). It binds a variable reference. So something like
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
$stmt->bindParam(':v1', $i, PDO::PARAM_INT);
for ($i = 0; $i < 10; $i++) {
$stmt->execute();
}
Would insert values 0 through 9 in a table.
That's obviously a very simple example that could be implemented in other, more efficient ways. You could have more complex logic here. But the basic idea is that you bind a reference to a variable and then you can change the value of the variable.
You can get around the need for a reference by creating a variable before calling bindParam. But in your case, you don't particularly want to bind to a variable reference. You just want to bind a value. So go ahead and do exactly that with bindValue.
You can mostly just use bindValue. But to show why both methods exist, let's rewrite the previous example to use bindValue instead of bindParam:
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
for ($i = 0; $i < 10; $i++) {
$stmt->bindValue(':v1', $i, PDO::PARAM_INT);
$stmt->execute();
}
This will work, but you have to call bindValue on every iteration of the loop whereas you only needed to call bindParam once. But you aren't doing anything like that, so you can just
$stmt->bindValue(':v1', null, PDO::PARAM_INT);
And everything will work, as stated in the accepted answer. Because you want to bind a value, not a variable reference.
Based on the other answers but with a little more clarity on how to actually use this solution.
If for example you have an empty string for a time value but you want to save it as a null:
if($endtime == ""){
$db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
}else{
$db->bind("endtime",$endtime);
}
Notice that for time values you would use PARAM_STR, as times are stored as strings.
So you just need to add an extra If statement that properly changes your variable to NULL before you call bindParam(). Here is an example that I figured out for my situation (I was stuck on this for days trying to INSERT a new DB record with a NULL value for one column):
if ($this->companyid == 'NULL' || $this->companyid == NULL) {
$this->companyid = NULL;
$this->companyname = NULL;
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
} else {
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
}
Try This.
$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
In my case I am using:
SQLite,
prepared statements with placeholders to handle unknown number of fields,
AJAX request sent by user where everything is a string and there is no such thing like NULL value and
I desperately need to insert NULLs as that does not violates foreign key constrains (acceptable value).
Suppose, now user sends with post: $_POST[field1] with value value1 which can be the empty string "" or "null" or "NULL".
First I make the statement:
$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");
where {$sColumns} is sth like field1, field2, ... and {$sValues} are my placeholders ?, ?, ....
Then, I collect my $_POST data related with the column names in an array $values and replace with NULLs:
for($i = 0; $i < \count($values); $i++)
if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
$values[$i] = null;
Now, I can execute:
$stmt->execute($values);
and among other bypass foreign key constrains.
If on the other hand, an empty string does makes more sense then you have to check if that field is part of a foreign key or not (more complicated).
I'm using bindParam to bind the return value of stored procedure once the statement is executed
But i'm getting zero , i've specified output variable of stored procedure as BIGINT
and i'm binding parameter like below
$sql = "{:retval = CALL sp_testProc()}";
$stmt->bindParam('retval', $proc_pass_val, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT, 4);
bindparam is taking Length of data type as last parameter, i'm passing 4 here, but it returns zero, don't know why
Could anybody help me on this
Thanks in Advance
This is what I've done to make it work, Hope this helps someone.
Note: Procedure defined in MSSQL server
Here I want email into a field inorder to get that in an array, you can omit this line select #myemail as student_email; and you can get the value of #myemail into $myemail
My Procedure:
Alter proc [dbo].[sp_test_success](
#Id int=1,
#myemail varchar(250)=null output
)
AS
BEGIN
select #myemail=rtrim(email) from Student where StudentId=#Id;
select #myemail as student_email;-- i put this to get myemail into array
END
Code:
$dbh = new PDO('sqlsrv:Server=Server;Database=database', 'UID', 'Pwd');
$stmt = $dbh->prepare("{CALL sp_test_success(#Id=:Id,#myemail=:myemail)}");
$Id = 4;
$stmt->bindParam('Id', $Id, PDO::PARAM_INT);
$stmt->bindParam('myemail', $myemail, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 500);
$stmt->execute();
$results = array();
do {
$rows= $stmt->fetch();// default fetch type PDO::FETCH_BOTH
$results[]= $rows;
} while ($stmt->nextRowset());
echo "<pre>"; print_r($results);
print "procedure returned $myemail\n"; exit;
In many places in our PHP code, (working with postgres if it matters)
we have stuff like:
$q = "SELECT DISTINCT a.id FROM alarms.current a, entities e, installations i ";
$q .= "WHERE i.\"entityId\"=e.id AND a.installationid=i.id AND ";
$q .= "e.id=".$entityId;
$stmt = $db->query($q);
$stmt->bindColumn("id", $alarmId);
if ($stmt->fetch(PDO::FETCH_ASSOC))
....etc
Now according to my reading of the docs, if you want your variables updated from their bound columns you ought to use PDO::FETCH_BOUND. But we don't, and no-one has complained about the performance as far as I'm aware.
Can anyone throw any light on why this apparently faulty code actually apparently works?
While the example in the PHP documentation for bindColumn uses PDO::FETCH_BOUND, which does suggest that this fetch style is necessary in order to use bindColumn, it does not explicitly state that this is a requirement. It only says
PDOStatement::bindColumn() arranges to have a particular variable bound to a given column in the result-set from a query. Each call to PDOStatement::fetch() or PDOStatement::fetchAll() will update all the variables that are bound to columns.
After some testing I determined that this will occur regardless of the fetch style that is used. I think the fact that the fetch call in your code is not actually fetched into a variable really just means that an associative array is created and assigned to nothing, while the side effect of the fetch populates the $alarmId variable.
Continuing from #DontPanic's comments, here is how I prefer to use bound parameters:
/**
* #param $id
*
* #return array|false
*/
public function retrieveImage($id)
{
$conn = $this->dbh->getInstance('LocalMSSQL');
$stmt = $conn->prepare("SELECT inputType, blob FROM images WHERE id = ?");
$stmt->bindValue(1, $id, PDO::PARAM_INT);
$stmt->execute();
$resultSet = [
'inputType' => null,
'blob' => null,
];
$stmt->bindColumn(1, $resultSet['inputType'], PDO::PARAM_STR);
$stmt->bindColumn(2, $resultSet['blob'], PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
if ($stmt->fetch()) {
return $resultSet;
} else {
return false;
}
}
I am trying to run a simple SELECT query. here is the query
$sql1=$conn->prepare("SELECT q.quoteid, q.customername, q.customersurname, q.timestamp, o.name
FROM quotes as q, occasions as o
WHERE q.occasionid=o.occasionid
AND companyid=1
AND q.day=:day
AND q.month=:month
AND q.year=:year
AND staffid='-1'
AND (q.complete != 'W' OR q.complete != 'Y')
AND q.online=0");
$sql1->execute($exearray);
/* here $exearray contain following value */
Array ( [:day] => 24 [:month] => 1 [:year] => 2014 )
Its not even showing any error.
If I pass static value 1 in month it is showing data.
I run this query directly on DB its working.
So I think there is no error in query.
I am running this query locally on MAMP.
have you tried to parse your month's data to string before passing to the pdo???
$exearray = array(':day'=>24, ':month'=>(string)1, ':year'=>2014);
once i faced this situation and i did this and it worked for me.
i always have this problem while i trying to use LIMIT command in mysql. i just parse my value to string or pass it through statically.
and it doesn't relevant to your MAMP. i have this problem on LAMP and WAMP too.
apparently it's a common problem which it's in PDO libraries.
and for the record don't forget to use intval() while you pass your month's variable directly to your query.
You need to fetch your result, execute only returns a boolean (true or false) whether query execution succeeded or not.
Example:
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();
/* Fetch all of the remaining rows in the result set */
print("Fetch all of the remaining rows in the result set:\n");
$result = $sth->fetchAll();
print_r($result);
from: http://php.net/manual/en/pdostatement.fetchall.php
Perhaps doing it the "long-way" with the parameters, and/or changing the query to use a join would help.
Also, the try/catch on PDOException might provide more information.
Lastly, note the $cmd->fetch(PDO::FETCH_ASSOC) invoked for iterating through the recordset.
try {
$db = new PDO("mysql:host=localhost;charset=utf8", "root", "root");
$cmd = $db->prepare("
SELECT q.quoteid, q.customername, q.customersurname,
q.timestamp, o.name
FROM quotes q
LEFT JOIN occasions o on q.occasionid = o.occasionid
WHERE companyid=1
AND q.day = :day
AND q.month = :month
AND q.year = :year
AND staffid = '-1'
AND (q.complete != 'W' OR q.complete != 'Y')
AND q.online = 0
");
$cmd->bindParam(':day', 24, PDO::PARAM_INT);
$cmd->bindParam(':month', 1, PDO::PARAM_INT);
$cmd->bindParam(':year', 2014, PDO::PARAM_INT);
if ($cmd->execute()) {
while ($row = $cmd->fetch(PDO::FETCH_ASSOC)) {
echo $row['quoteid']."|".$row['customername']."|".$row['name']."<br/>";
}
} else {
echo "$cmd->execute() returned false.";
}
} catch (PDOException $e) { echo $e->getMessage(); return; }
In table month column type was Varchar.
I change that to integer and now it is working.
p.s. I have tried using (string) before variable and even double quotes and single quotes on variable value but it didn't work.
Thank you everyone. :)
I'm using this code and I'm beyond frustration:
try {
$dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem
PDO::PARAM_NULL, null, '', all of them fail and throw this error:
Fatal error: Cannot pass parameter 2 by reference in /opt/...
You need to use bindValue, not bindParam
bindParam takes a variable by reference, and doesn't pull in a value at the time of calling bindParam. I found this in a comment on the PHP docs:
bindValue(':param', null, PDO::PARAM_INT);
P.S. You may be tempted to do this bindValue(':param', null, PDO::PARAM_NULL); but it did not work for everybody (thank you Will Shaver for reporting.)
When using bindParam() you must pass in a variable, not a constant. So before that line you need to create a variable and set it to null
$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);
You would get the same error message if you tried:
$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
When using INTEGER columns (that can be NULL) in MySQL, PDO has some (to me) unexpected behaviour.
If you use $stmt->execute(Array), you have to specify the literal NULL and cannot give NULL by variable reference.
So this won't work:
// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null
But this will work:
// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise
Tried this on MySQL 5.5.15 with PHP 5.4.1
For those who still have problems (Cannot pass parameter 2 by reference), define a variable with null value, not just pass null to PDO:
bindValue(':param', $n = null, PDO::PARAM_INT);
Hope this helps.
I had the same problem and I found this solution working with bindParam :
bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
If you want to insert NULL only when the value is empty or '', but insert the value when it is available.
A) Receives the form data using POST method, and calls function insert with those values.
insert( $_POST['productId'], // Will be set to NULL if empty
$_POST['productName'] ); // Will be to NULL if empty
B) Evaluates if a field was not filled up by the user, and inserts NULL if that's the case.
public function insert( $productId, $productName )
{
$sql = "INSERT INTO products ( productId, productName )
VALUES ( :productId, :productName )";
//IMPORTANT: Repace $db with your PDO instance
$query = $db->prepare($sql);
//Works with INT, FLOAT, ETC.
$query->bindValue(':productId', !empty($productId) ? $productId : NULL, PDO::PARAM_INT);
//Works with strings.
$query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);
$query->execute();
}
For instance, if the user doesn't input anything on the productName field of the form, then $productName will be SET but EMPTY. So, you need check if it is empty(), and if it is, then insert NULL.
Tested on PHP 5.5.17
Good luck,
Several answers have given examples of what you should do. But they haven't really explained why you should do one of those things.
The bindParam method is meant to be used with something like a loop (or just repeated statements). It binds a variable reference. So something like
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
$stmt->bindParam(':v1', $i, PDO::PARAM_INT);
for ($i = 0; $i < 10; $i++) {
$stmt->execute();
}
Would insert values 0 through 9 in a table.
That's obviously a very simple example that could be implemented in other, more efficient ways. You could have more complex logic here. But the basic idea is that you bind a reference to a variable and then you can change the value of the variable.
You can get around the need for a reference by creating a variable before calling bindParam. But in your case, you don't particularly want to bind to a variable reference. You just want to bind a value. So go ahead and do exactly that with bindValue.
You can mostly just use bindValue. But to show why both methods exist, let's rewrite the previous example to use bindValue instead of bindParam:
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
for ($i = 0; $i < 10; $i++) {
$stmt->bindValue(':v1', $i, PDO::PARAM_INT);
$stmt->execute();
}
This will work, but you have to call bindValue on every iteration of the loop whereas you only needed to call bindParam once. But you aren't doing anything like that, so you can just
$stmt->bindValue(':v1', null, PDO::PARAM_INT);
And everything will work, as stated in the accepted answer. Because you want to bind a value, not a variable reference.
Based on the other answers but with a little more clarity on how to actually use this solution.
If for example you have an empty string for a time value but you want to save it as a null:
if($endtime == ""){
$db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
}else{
$db->bind("endtime",$endtime);
}
Notice that for time values you would use PARAM_STR, as times are stored as strings.
So you just need to add an extra If statement that properly changes your variable to NULL before you call bindParam(). Here is an example that I figured out for my situation (I was stuck on this for days trying to INSERT a new DB record with a NULL value for one column):
if ($this->companyid == 'NULL' || $this->companyid == NULL) {
$this->companyid = NULL;
$this->companyname = NULL;
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
} else {
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
}
Try This.
$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
In my case I am using:
SQLite,
prepared statements with placeholders to handle unknown number of fields,
AJAX request sent by user where everything is a string and there is no such thing like NULL value and
I desperately need to insert NULLs as that does not violates foreign key constrains (acceptable value).
Suppose, now user sends with post: $_POST[field1] with value value1 which can be the empty string "" or "null" or "NULL".
First I make the statement:
$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");
where {$sColumns} is sth like field1, field2, ... and {$sValues} are my placeholders ?, ?, ....
Then, I collect my $_POST data related with the column names in an array $values and replace with NULLs:
for($i = 0; $i < \count($values); $i++)
if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
$values[$i] = null;
Now, I can execute:
$stmt->execute($values);
and among other bypass foreign key constrains.
If on the other hand, an empty string does makes more sense then you have to check if that field is part of a foreign key or not (more complicated).