Mysql dynamic sql statement with PDO and parameters - mysql

I want to create a dynamic sql statement and run it using PDO. My problem is that i have some parameters and i cannot think of a way to pass the parameters.
Ex :
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= " AND name = ?";
if (!empty($status)) $query .= " AND status = ?"
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->bindParam (1,$name);
$db_stmt->bindParam (2,$status);
My parameters does not get binded and i don't know how many parameters i have to bind, unless i write the same if statements but with bindParam instructions.
I tryed with mysql_real_escape_string instead bindParam to PDO but for some reason my parameters are added empty.
Any idea on how can i build a dynamic query and bind parameters to PDO ?
Edit 1 :
$arr = array();
if (!empty($name)){
$query .= " AND `name` like :NAME";
$arr['NAME'] = $name;
}
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->execute($arr);
How can i write a "like" statement ? I tried
$query .= " AND `name` like :NAME" . "%";
and is not working.

What I usually do is the following:
$query = "Select * from `tbl_task` where 1=1";
$arr = array();
if (!empty($name))
{
$query .= " AND `name` = :NAME";
$arr['NAME'] = $name;
}
if (!empty($status))
{
$query .= " AND `status` = :STATUS";
$arr['STATUS'] = $status;
}
$this->db->beginTransaction();
try
{
$tmp = $this->db->prepare($query);
$tmp->execute($arr);
$this->db->commit();
}
catch(PDOException $ex)
{
$this->db->rollBack();
$this->log->error($ex->getMessage());
}

You can't add SQL code as a parameter; only data will do. You'll have to force these bits into $query. They won't be escaped then so they shouldn't contain user-submitted data.

What I usually do is the following:
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= $db->parse(" AND name = ?s", $name);
if (!empty($status)) $query .= $db->parse(" AND status = ?s",$status);
$data = $this->db->getAll($query);
the idea is in having a function to parse placeholders in arbitrary query part instead of whole query.
I don't bother with native prepared statements though. They pollute PHP scripts with heaps of useless code with not a single benefit.
To answer updated question
as you've been told, you can't bind arbitrary query part. But a literal only.
So, make your literal looks like foo% and then bind it usual way.

Related

What's the best way to fetch an array

Alright, so I believe that there is a better way that I can fetch an array from the database, here's the code right now that I have.
$id = 1;
$userquery = mysql_query("SELECT * FROM login WHERE id='$id'");
while($row = mysql_fetch_array($userquery, MYSQL_ASSOC)) {
$username = $row['username'];
$password = $row['password'];
$email = $row['email'];
}
So If I am not wrong, you want a better way to get all the returned rows from mysql in a single statement, instead of using the while loop.
If thats the case, then I must say mysql_ drivers do not provide any such functionality, which means that you have to manually loop through them using foreach or while.
BUT, since mysql_ is already depricated, you are in luck! you can actually switch to a much better and newer mysqli_ or the PDO drivers, both of which DO actually have functions to get all the returned rows.
For mysqli_: mysqli_result::fetch_all
For PDO : PDOStatement::fetchAll
Eg.
mysqli_fetch_all($result,MYSQLI_ASSOC);
// The second argument defines what type of array should be produced
// by the function. `MYSQLI_ASSOC`,`MYSQLI_NUM`,`MYSQLI_BOTH`.
Like the comments already told you: PHP's mysql driver is deprecated. And you should use prepared statements and parameters.
for example in PDO your code would look something like this:
//connection string:
$pdo= new PDO('mysql:host=localhost;dbname=my_db', 'my_user', 'my_password');
//don't emulate prepares, we want "real" ones:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//use exception-mode if you want to use exception-handling:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$id = 1;
//it's always better to strictly use backticks for db-names (db, tables, fields):
$sql = "SELECT * FROM `login` WHERE `id` = :id";
try
{
//create your prepared statement:
$stmt = $pdo->prepare($sql);
//bind a parameter and explicitly use a parameter of the type integer (in this case):
$stmt->bindParam(":id", $id, PDO::PARAM_INT);
//execute the query
$stmt->execute();
}
catch(PDOException $e)
{
exit("PDO Exception caught: " . $e->getMessage());
}
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
$username = $row['username'];
$password = $row['password'];
$email = $row['email'];
}
here you go: your PHP-MySQL routine is save against SQL-injections now and no longer uses deprecated PHP-functions! it's kinda state of the art ;)

PDO and rowcount

I'm trying to make the PDO query below working, but somehow it doesn't.
This is how far i mine brain exploded!
Is there somebody who can say what i'm doint wrong?
$dbh = new PDO("mysql:host=localhost;dbname=databasename", "user", "pass");
$stmt = $dbh->prepare("SELECT * FROM `something` WHERE `email`=':email' and `name`=':name'");
$stmt->bindParam(':email', 'mail#mail.nl',PDO::PARAM_STR);
$stmt->bindParam(':name', 'name',PDO::PARAM_INT);
$stmt->execute();
if($stmt->rowCount() == 1)
{
echo "row count 1";
}
else if ($stmt->rowCount() == 0)
{
echo "row count 0";
}
else if ($stmt->rowCount() > 1)
{
echo "row count greater then 1";
}
Single quotes (') denote string literals in SQL, so the prevent PDO from parsing inside them. If you want to use bind variables, you should remove the quotes (don't worry, PDO will properly handle them as strings):
$stmt = $dbh->prepare
("SELECT * FROM `something` WHERE `email`=:email and `name`=:name");

Preparing SQL query

In my PHP document, I got a SQL query looking like this:
if(isset($_GET['id']))
{
$id = $_GET['id'];
$q = "SELECT * FROM `objekt_t` WHERE `id`='" . $id . "'";
$row = mysqli_query($con, $q) or die(mysqli_error($con));
while($r = mysqli_fetch_assoc($row))
{
$objekt = $r;
}
}
I realize this is very unsafe practice concerning SQL injections and such, so I've been looking into prepared SQL querys, using bound parameters. Looking at bobby-tables.com I see this example query:
$stmt = $db->prepare('update people set name = ? where id = ?');
$stmt->bind_param('si',$name,$id);
$stmt->execute();
I do not understand how I should modify my current query to match the safer one using bound parameters. Any help is appreciated.
Just the same way
$mysqli = new mysqli("localhost", "my_user", "my_password", "db");
if(isset($_GET['id']))
{
$id = $_GET['id'];
$q = "SELECT some_field FROM `objekt_t` WHERE `id`= ?";
if ($stmt = $mysqli->prepare($q)) {
$stmt->bind_param("i", $id);
$stmt->execute();
$stmt->bind_result($result);
$stmt->fetch();
}
}
Now $result variable contains the resuts of your query.
prepared statements transmit raw data to the query so that SQL injection is not possible. There is no need to escape for real_escape_String or any other formatting functions, as this does it for you.
Example:
$db = new mysqli ("host","user","password","database");
$statement = $db->prepare("SELECT test FROM test WHERE Username=?");
$statement->bind_param('s',$_POST['Username']);
$statement->execute();
$statement->bind_result($resultCol);
$statement->fetch();
$statement->close();
I am basically binding my $_POST data directly to the query because the data is being sent as raw, so even if the query contained a form of injection, as the $_POST['username']; the query will run as normal.
IN terms of the procedure and OOP style, it's down to preference, I personlly prefer the OOP style over the other options as it's more readable.
Working with numbers:
$ID= 5;
$db = new mysqli ("host","user","password","database");
$statement = $db->prepare("SELECT test FROM test WHERE ID=?");
$statement->bind_param('i',$ID);
$statement->execute();
$statement->bind_result($resultCol);
$statement->fetch();
$statement->close();
Or you can work with exact values directly within the statement:
$db = new mysqli ("host","user","password","database");
$statement = $db->prepare("SELECT test FROM test WHERE ID='5'");
$statement->execute();
$statement->bind_result($resultCol);
$statement->fetch();
$statement->close();
You can do it like this:
$stmt = $mysqli->prepare('SELECT * FROM objekt_t WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// $row is an associative array
}

Why am I getting error SQLSTATE[HY093]: Invalid parameter number: ? How can I fix it?

Based on this question How to insert array into mysql using PDO and bindParam?
I'm trying to insert values of an array into mysql via PDO.
I'm having a hard time of it, because I keep getting the following error.
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
for this line $stmt->execute();
I'm guessing the problem has something to do with this line
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR); Specifically 'val$count', but I'm not sure exactly what is going wrong.
QUESTION: What am I doing wrong? How can I fix this?
Anyway here is the code I'm using along with the sample array.
$lastInsertValue=87;
$qid[0][0]=1;
$qid[0][1]=1;
$qid[1][0]=2;
$qid[1][1]="null";
$qid[2][0]=3;
$qid[2][1]=0;
$array_count = count($qid);
if (isset($lastInsertValue))
{
try
{
$dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
$stqid=array();
$a=0;
for ($i=0; $i<$array_count; $i++)
{
$stqid[$a]=$lastInsertValue;
$a++;
$stqid[$a]=$qid[$i][0];
$a++;
$stqid[$a]=$qid[$i][1];
$a++;
}
$sql = "INSERT INTO qresults (instance, qid, result) VALUES ( :val0, :val1, :val2)";
$count = 0;
$stmt = $dbh->prepare($sql);
foreach ($stqid as $val)
{
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR);
$count++;
}
$stmt->execute();
$dbh = null;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
Yikes, so many issues.
Your array building is so verbose. Try this
$stqid = array();
foreach ($qid as $qidArr) {
$stqid[] = $lastInsertValue; // no idea why you repeat this
$stqid[] = $qidArr[0];
$stqid[] = $qidArr[1];
}
Use positional placeholders if you're simply relying on number of arguments
$sql = 'INSERT INTO ... VALUES (?, ?, ?)';
bindParam uses references which you are overwriting with each loop iteration. You would want to use bindValue() instead
Your query only has 3 placeholders but your $stqid array has 9 items. This is the source of your error.
You have single quotes around a variable, which will be treated as the variable name $count (not the value), try concatenating the variable to the string. Give this a try:
$stmt->bindParam(':val' . $count, $val,PDO::PARAM_STR);
for ($i=0; $i<$array_count; $i++)
{
$stqid[$a]=$lastInsertValue;
$a++;
$stqid[$a]=$qid[$i][0];
$a++;
$stqid[$a]=$qid[$i][1];
$a++;
}
so in case $i = 2, it will add $stqid[6], $stqid[7], $stqid[8] so
foreach ($stqid as $val)
{
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR);
$count++;
}
will give you :val0 to :val8
In your query you have only :val0 to :val2.
Also having multiple values in one field in database is bad. Don't do it. Try to redesign your DB differently
EDIT: bad math in the morning... sorry

use a single return from a sql query

I'm using PHP to make a very specific sql query. For example sake, I have the user's ID number, but I need their name. So I do a sql query from that table with the ID number in order to return the name.
$result = mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db);
Now I want to use that. What's the most succinct way to go about making that result into a variable ths I can use?
edit:
I'm hoping that this is not the answer:
$rowCheck = mysql_num_rows($result);
if ($rowCheck > '0') {
while ($row = mysql_fetch_assoc($result)){
foreach ($row as $val){
$username = $val;
}
}
}
I have used something like this to keep it short in the past:
list($name) = mysql_fetch_row(mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db));
echo $name;
In my opinion, the best way to fetch any SQL result is through mysql_fetch_assoc(). To use it, you would do something like this:
$result = mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db);
while ($row = mysql_fetch_assoc($result)) {
echo $row['name']; // You get an array with each column returned from your query.
}
Still, MySQL extension has been replaced for MySQLi, which is acknowledged to be faster and more practical. It has both OOP and structural bindings, and takes more into account your server settings.
$result = mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db);
if (!$result) {
echo 'Could not run query: ' . mysql_error();
exit;
}
$name = mysql_fetch_row($result)[0];
You should use MySQLi as bellow:
$db = new MySQLi($host,$user,$pass,$db);
$query = $db->query('SELECT name FROM users WHERE userID='.$thisuserid);
$result = $query->fetch_object();
echo $result->name;
If you use SELECT * so you also can access via $result->{field_name}