PHP PDO - Bind number of variables dynamically - php

I'm trying to add a search feature on my site there are 4 different inputs the user can use although they might not use all 4. I'm appending to my sql query depending on which inputs they fill in;
$query = "SELECT * FROM cars WHERE status = 2 ";
if($_GET['ref']){
$query .= " AND ref = :ref";
}
if($_GET['doors']){
$query .= " AND doors = :doors";
}
if($_GET['wheels']){
$query .= " AND wheels = :wheels";
}
if($_GET['location']){
$query .= " AND location = :location";
}
$query .= ")";
$adverts = Singlequery ($query, array(
'ref' => $_GET['ref'],
'doors' => $_GET['doors'],
'wheels' => $_GET['wheels'],
'location' => $_GET['location']
), $conn);
This is my query I'm using -
function query($query, $bindings, $conn)
{
$stmt = $conn->prepare($query);
$stmt->execute($bindings);
return $stmt;
}
I'm getting the error -
PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
I think its expecting all 4 inputs to be used and therefore wants 4 bound variables.

First of all you have a missing parenthesis in your query. You are closing it but not opening.
You should create your variable array also in if clauses as :
$query = "SELECT * FROM cars WHERE status = 2 ";
$data=array();
if($_GET['ref']){
$query .= " AND ref = :ref";
$data['ref']=$_GET['ref'];
}
if($_GET['doors']){
$query .= " AND doors = :doors";
$data['doors']=$_GET['doors'];
}
if($_GET['wheels']){
$query .= " AND wheels = :wheels";
$data['wheels']=$_GET['wheels'];
}
if($_GET['location']){
$query .= " AND location = :location";
$data['location']=$_GET['location'];
}
$adverts = Singlequery($query, $data, $conn);

Try to use ? instead the :alias format.

Maybe you can try to say "if this variable is empty, set it to Null" Then the variables that stay empty will be Null instead of undefined.

Could it be because you always feed it an array of size four as to bind, while your query string sometimes won't have as many due to the if statements, hence the error message?

Related

PDO Named parameters inside JSON

I have data that should be escaped inside a JSON formatted string, so I'm using PDO's named parameters and PDO::Prepare to bind them.
Because JSON with it's apostrophes causes errors in the MySQL query, i have to use single quotes around it - although this causes the PDO::Prepare to ignore the named parameters inside the JSON, so it fails with SQLSTATE[HY093]: Invalid parameter number: parameter was not defined.
Any ideas how to work around this?
function send($_data) {
global $_SESSION;
global $dbApi;
#These are temporary debug variables:
$_SESSION['room_id'] = 1;
$id = 124;
$json = '"' . $id . '": {"user_id": ":email","data": ":data"}';
$query = "UPDATE `room_index` " .
"SET `data` = JSON_ARRAY_INSERT(`data`, '$[0]', '" . $json . "') " .
"WHERE `id` = :room_id";
$dbApi->query($query, array(':email' => $_SESSION['email'],
':data' => $_data,
':room_id' => $_SESSION['room_id']));
}
To explain the code a bit, :email ($_SESSION['email']) doesn't have to be a parameter, but it's cleaner this way. The main issue is :data ($_data) - that is user input straight from a textarea via JS.
$dbApi is a class with a proper query function, that looks like this:
function query($_query, $_params = array()) {
global $_DB; # <- Database connection object
$query = $_DB->prepare($_query);
if (! $query)
echo $_DB->errorInfo();
try {
$query->execute($_params);
} catch (PDOException $e) {
die( $e->getMessage() );
}
return $query;
}
There are 2 issues with the code.
1. JSON_INSERT is more appropriate.
As I'm inserting a named object into another object (the top level document), the JSON_INSERT offers such a syntax straight away
2. Using JSON_OBJECT instead of manually writing the JSON syntax
As my main issue was, that PDO doesn't replace single, or double quoted parameters, the solution was using JSON_OBJECT, which doesn't require double quotes as they are automatically generated later (my assumption) - but after PDO replaces the variables and also places single quotes around them.
New, tested code outputting valid JSON:
#Temporary, to avoid other unrelated issues
$_SESSION['room_id'] = 1;
$_SESSION['email'] = 'email#email.com';
$id = 123;
$json = 'JSON_OBJECT("user_id", :email, "data", :data)';
$query = "UPDATE `room_index` " .
"SET `data` = JSON_INSERT(`data`, :id, $json) " .
"WHERE `id` = :room_id";
$dbApi->query($query, [':id' => "$." . $id,
':email' => $_SESSION['email'],
':data' => $_data,
':room_id' => $_SESSION['room_id']]);

Failed to run query: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined

I am a newbie to php and been trying to get rid of this error for a while now. I believe i have the correct amount of parameters but cant figure out where the problem is. This is my php code below.. any answer is much appreciated. thanks
require ("common.php");
$mySessionName = $_SESSION['user']['UserName'];
if(!empty($_POST)){
$sql = "INSERT INTO compliance_requirement(ComplianceName, ComplianceGoal, ComplianceDescription, ComplianceStartDate, ComplianceEndDate, UserName)''VALUES (:compName, :compGoal, :compDes, :compStart, :compEnd, :$mySessionName)";
$query_paramsm = array(':ComplianceName' => $_POST['compName'], ':ComplianceGoal'=>$_POST['compGoal'], 'ComplianceDescription' => $_POST['compDes'], 'ComplianceStartDate'=>$_POST['compStart'], 'ComplianceEndDate'=>$_POST['compEnd'], 'UserName'=>$_POST['UserName']);
try{
$stmt = $db->prepare($sql);
$result = $stmt->execute($query_paramsm);
}
catch(PDOException $ex)
{
die("Failed to run query: " . $ex->getMessage());
}
header("Location: compliance.php");
}
?>
Lots of issues.
1. You have '' in the middle of your query
2.
$sql = "VALUES (:compName, ...";
$query_paramsm = array(':ComplianceName' => $_POST['compName'] ...);
compName and ComplianceName are two very different strings.
The variable you have in the query :compName, :compGoal, :compDes, :compStart, :compEnd, :$mySessionName
have not the same name as in your param array :
array(':ComplianceName' => $_POST['compName'], ':ComplianceGoal'=>$_POST['compGoal'], 'ComplianceDescription' => $_POST['compDes'], 'ComplianceStartDate'=>$_POST['compStart'], 'ComplianceEndDate'=>$_POST['compEnd'], 'UserName'=>$_POST['UserName'])
look :
:compName <> :ComplianceName
:compGoal <> :ComplianceGoal
...

MySQL PDO count function returning incorrect with multiple WHERE criteria

As long as I only specify one item in the WHERE clause, this function works fine. If there's more than one, it always returns 0.
Here's the function:
function count_rows($table, $where=array()){
$sql = "SELECT COUNT(*) FROM `$table`";
if(!empty($where)){
$sql .= " WHERE (";
foreach($where as $key=>$value){
$sql .="`". $key . "`=:w_" . $key . " AND ";
}
$sql = substr($sql, 0, -4);
$sql .= ") ";
}
$stmt = $this->conn->prepare($sql);
foreach($where as $key=>$value){
$stmt->bindParam(":w_".$key, $value);
}
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();
$this->stmt = $stmt;
return $this->stmt->fetchColumn();
}
For example, the following returns the number of rows where list_id is set to $list_id:
$email_count = count_rows("emails", array("list_id"=>$list_id));
Using two criteria in the WHERE clause, however, causes it to return 0 no matter what:
$optout_count = count_rows("emails", array("list_id"=>$list_id, "optout"=>"1"));
I've tried with and without the parentheses around the WHERE clause, and the debug function I use shows the query properly. I have also tried putting quotes around the values in the array. Any help would be appreciated.
PDOStatement::bindParam bind a reference to a variable. Unlike PDOStatement::bindValue(), the variable is bound as a reference and will only be evaluated at the time that PDOStatement::execute() is called.
Solution:
Just change
$stmt->bindParam(":w_".$key, $value);
to
$stmt->bindValue(":w_".$key, $value);

PDO Dynamic Query Building

I have been old school using mysql_query and starting out now using PDO. Which is great!
But in my old scripts I had build a dynamic query builder, and i'm having a tough time porting that over using PDO.
If anyone can give me some direction that would be great!
Here is the theory of it.
I have an array of
the DB Fields and Values (upon insert).
Create the query string to product a valid PDO transaction
Here is a portion of what i'm trying to do.
public $dbFields; // This is an array of the fields plus VALUES
public function select($where, $limit) {
// This is what I **had** before
$query = "SELECT ". implode(", ", $this->dbFields) ." FROM ". $this->table." WHERE ". $where ." ". $limit."";
// Now i need to convert that to PDO
$this->connection->beginTransaction();
# START Query
$select = $this->connection->prepare("SELECT {$this->fieldNames} FROM {$this->table}");
// I need to BIND my params and values, but i'm not sure the best route to take when I have a WHERE clause that includes, "AND" / "OR" operators.
# EXECUTE the query
$select->execute();
$this->connection->commit();
}
This is what I HAD before
$results = $db->select("userId = 111 OR userId = 222");
But what i'm thinking I need to do is use something more like
$results = $db->select(array("userId"=>111, "userId"=>222));
I know this is a tall order, and I hope it makes sense in what i'm trying to do, but any help in trying to build these queries would be greatly appreciated.
You'll need a separate $params parameter to your select method. I took the liberty of providing defaults for the method parameters. Like #userXxxx notes, you don't need a transaction just to do a SELECT.
<?php
class db {
public $connection; //PDO
public $dbFields; // This is an array of the fields plus VALUES
public function select($where = '1', $params = array(), $limit = '', $fetchStyle = PDO::FETCH_ASSOC) { //fetchArgs, etc
$fields = implode(', ', $this->dbFields);
//create query
$query = "SELECT $fields FROM {$this->table} WHERE $where $limit";
//prepare statement
$stmt = $this->connection->query($query);
$stmt->execute($params);
return $stmt->fetchAll($fetchStyle);
}
//...
}
$where = 'userId IN(:userId1, :userId2)';
$params = array(':userId1' => 111, ':userId2' => 2222);
$db->select($where, $params);
Notes:
If you really want, you can add additional method parameters to match up with all the flexibility of PDOStatement::fetchAll.
I'm not sure what you mean about $dbFields being "fields plus VALUES". Can you explain?
[Edit]
You might want to take a look at the docs/examples for PDOStatement::execute, since that seemed to be where your confusion was rooted--in particular, the $input_parameters method parameter.
What about this?
public function select($where, $limit) {
$query = "SELECT ". implode(", ", $this->dbFields) ." FROM ". $this->table." WHERE ". $where ." ". $limit."";
$this->connection->query($query);
}
//Use what you had before:
$results = $db->select("userId = 111 OR userId = 222");
Not sure why you want to use transaction (for all-or-nothing basis or catching exceptions and rollback) or prepared queries (for sending multiple queries)...

I don't understand how "?" is being used here

So, I have this PHP code:
$tabid = getTabid($module);
if($tabid==9)
$tabid="9,16";
$sql = "select * from field ";
$sql.= " where field.tabid in(?) and";
Now, how exactly does the ? work here? I vaguely understand that in PHP, ?: is a ternary operator, but the colon isn't being used here, and ? is part of a Postgresql query anyway.
The final query looks a bit like this:
select * from field where field.tabid in('9,16')
So, the question mark is replaced by the contents of $tabid, how does that happen?
The issue is that ('9,16') is not accepted by Postgres as an integer, it needs to be written like (9,16), so how do I do that? How do I remove the apostrophes?
Thanks a lot for the help, have a good day!
edit: More code was requested:
$sql.= " field.displaytype in (1,2,3) and field.presence in (0,2)";
followed by if statements, I think this is the relevant one:
if($tabid == 9 || $tabid==16)
{
$sql.= " and field.fieldname not in('notime','duration_minutes','duration_hours')";
}
$sql.= " group by field.fieldlabel order by block,sequence";
$params = array($tabid);
//Running the query.
$result = $adb->pquery($sql, $params);
Oh, I think I see now, I think it is a place holder, a part of the pquery function:
function pquery($sql, $params, $dieOnError=false, $msg='') {
Stuff
$sql = $this->convert2Sql($sql, $params);
}
Now, this is where it seems to get fun, here's part of the convert2Sql function:
function convert2Sql($ps, $vals) {
for($index = 0; $index < count($vals); $index++) {
if(is_string($vals[$index])) {
if($vals[$index] == '') {
$vals[$index] = "NULL";
}
else {
$vals[$index] = "'".$this->sql_escape_string($vals[$index]). "'";
}
}
}
$sql = preg_replace_callback("/('[^']*')|(\"[^\"]*\")|([?])/", array(new PreparedQMark2SqlValue($vals),"call"), $ps);
return $sql;
}
The problem I think lies in the
$vals[$index] = "'".$this->sql_escape_string($vals[$index]). "'"; line.
The sql_escape_string($str) function just returns pg_escape_string($str).
Sorry for the super long edit, but I still haven't been able to get past I am afraid, thanks for all the help!
Edit 2: I fixed the problem, all it took was changin $tabid = "9,16" to $tabid = array(9,16). I have no idea why, oh and I also had to remove the group by statement because Postgresql requires every field to be placed in that statement.
it is a positional parameter for a prepared statement
See: http://php.net/manual/en/function.pg-prepare.php
You don't actually 'remove' the quotes, you have to pass SQL array of ints instead of a string value into the parameter when doing pg_execute
An example:
// Assume that $values[] is an array containing the values you are interested in.
$values = array(1, 4, 5, 8);
// To select a variable number of arguments using pg_query() you can use:
$valuelist = implode(', ', $values);
// You may therefore assume that the following will work.
$query = 'SELECT * FROM table1 WHERE col1 IN ($1)';
$result = pg_query_params($query, array($valuelist))
or die(pg_last_error());
// Produces error message: 'ERROR: invalid input syntax for integer'
// It only works when a SINGLE value specified.
Instead you must use the following approach:
$valuelist = '{' . implode(', ', $values . '}'
$query = 'SELECT * FROM table1 WHERE col1 = ANY ($1)';
$result = pg_query_params($query, array($valuelist));

Categories