PHP PDO query works without parameters but not with - php

I have a very simple query that works when I don't use parameters. With parameters, it returns nothing. Someone else posted the same issue over here:
Query with input parameters doesn't work whereas direct query works
However no one has answered it. Below is my code.
require_once('database.class.php');
class Plan extends Database {
public function getBenefitAmounts($plan_id, $group_id, $level) {
$sql = 'SELECT DISTINCT benefit FROM rates WHERE plan_id = :plan AND group_id IS NULL AND `level` = :lvl';
$params = array(':plan'=>896, ':lvl'=>1);
$this->sqlQuery($sql, $params);
// $sql = 'SELECT DISTINCT benefit FROM rates WHERE plan_id = 896 AND group_id IS NULL AND `level` = 1';
// $this->sqlQuery($sql);
$results = $this->sth->fetchAll(PDO::FETCH_COLUMN);
$options = '';
foreach ($results as $value) {
$options .= '<option value="' . $value . '">$' . $value . '</option>';
}
return $options;
}
}
In the database class:
public function sqlQuery($sql, $values_to_bind=null) {
$this->sth = $this->pdo->prepare($sql);
if (isset($values_to_bind)) {
foreach ($values_to_bind as $param => $value) {
$this->sth->bindValue($param, $value);
}
}
$success = $this->sth->execute();
if (!$success) {
$arr = $this->$sth->errorInfo();
print_r($arr);
}
}
The code commented out of the first code snippet works just fine, but with parameters, it returns nothing. The getBenefitAmounts function is called from another PHP file which is called using a JQuery get.

Did you try to add the third param which is optional for bindValue(). It can be something like PDO::PARAM_INT, PDO::PARAM_STR etc. Just try to debug and see if it helps.

I don't know why do you like so much try...catch if you use with no sense code. Because this technique: } catch (PDOException $e) { throw new PDOException($e);} means the same as } catch (PDOException $e) {;} like you ask php to do nothing if catch. Why do you asked to catch if you do nothing in case it happens?
Now my guess about how to fix your code:
public function sqlQuery($sql, $values_to_bind=null) {
$this->sth = $this->pdo->prepare($sql);
if (isset($values_to_bind)) {
foreach ($values_to_bind as $param => $value) {
$this->sth->bindValue($param, $value);
}
}
$success = $this->sth->execute();
if (!$success) {
$arr = $this->$sth->errorInfo();
print_r($arr);
}
}
By the way you use $this->sth = $this->pdo->prepare($sql); that means your sqlQuery function is a method of some class that you didn't show to us. and your first piece of code is somewhere outside that class? it would be better if you post full version of code, not just lines you think are involved.
and here you can switch to regular way:
//$results = $this->sth->fetchAll(PDO::FETCH_COLUMN); //you don't need it
$options = '';
while ($row = $this->sth->fetch(PDO::FETCH_ASSOC)) {
$options .= '<option value="' . $row['benefit'] . '">$' . $row['benefit'] . '</option>';
}

Why not bind the parameters using bindParam?
$plan = 896;
$lvl = 1;
$sth = $dbh->prepare("SELECT DISTINCT benefit FROM rates WHERE plan_id = :plan AND group_id IS NULL AND `level` = :lvl");
$sth->bindParam(":plan", $plan);
$sth->bindParam(":lvl", $lvl);
$sth->execute();
$r = $sth->fetchAll();

Related

Cannot bind multiple parameters using call_user_func_array()

I know they're many, many questions already about this, but I have no idea why mine doesn't work.
If I just use the following, my code works fine:
$this->stmt->bind_param("ii", $params[0], $params[1]);
But if I use the call_user_func_array, it breaks. One suggestion I got was passing the $parameters array by reference, but adding an & before the variable also broke my code...
Any help is greatly recieved!
Here's my code:
DB class:
function selectQuery($sql, $paramTypes = false, $params = false) {
//Prepare statement
$this->stmt = $this->conn->prepare($sql);
if($this->stmt === false) {
//We have an error
echo 'Wrong SQL: ' . $sql . ' Error: ' . $this->conn->error;
}
//This part doesn't work...
// if ($params) {
// //Bind an unknown number of parameters
// $parameters = array_merge(array($paramTypes), $params);
// call_user_func_array(array($this->stmt, 'bind_param'), $parameters);
// }
//This works.
$this->stmt->bind_param("ii", $params[0], $params[1]);
//Execute statement
$this->stmt->execute();
if ($this->stmt->error) {
echo $this->stmt->error;
return false;
}
//Get the results
$result = $this->stmt->get_result();
$data = $result->fetch_all(MYSQLI_ASSOC);
//Close statement
$this->stmt->close();
//Return the results
return $data;
}
Test page:
<?php
require_once('DatabaseAccess.php');
$db = new DB();
$sql = "SELECT * FROM table WHERE id = ? OR id = ?";
echo "Fetching data....<br>";
$result = $db->selectQuery($sql, "ii", array(1, 2));
foreach($result as $r) {
echo "<pre>".print_r($r, 1)."</pre>";
}
?>
Decided to add more information:
I'll be using this function to pass in the parameter types and parameters, but the amount will vary. When I looked up how to do this everyone suggested the call_user_func thing, but each time I try it (tried a few different ways) it won't work. Read through many threads, but it never seems to work. If I just use the bind_params function directly it works and I get the correct data returned.
Using the call_user_func thing I was getting the no data for the ? mysqli error, which is when I tried passing by reference and the code just broke completely...
Put it before call_user_func_array()
$res = array();
foreach($parameters as $key => $value) {
$res[$key] = &$parameters[$key];
}

PDO - executed query with binded parameters yields no results?

I am trying to create a PHP array of random "fruits" from a database.
The database class that I am using:
class Db
{
private static $_instance = null;
private $_pdo;
private function __construct()
{
try {
$this->_pdo = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME .'', DB_USER, DB_PASS);
} catch (PDOException $e) {
die($e->getMessage());
}
}
public static function getInstance()
{
if (!isset(self::$_instance)) {
self::$_instance = new Db();
}
return self::$_instance;
}
public function prepare($sql)
{
return $this->_pdo->prepare($sql);
}
}
The class that is using the database to fetch "fruits" to create an array of a given size of random entries by using 3 seperate queries to calculate and retrieve "x" number of random items form the database.
class FruitBasket
{
private $_fruitArray = array(),
$_inputCode,
$_db;
public function __construct($input = null)
{
$this->_inputCode = $input;
$this->_db = Db::getInstance();
var_dump($this->_db);
}
public function pickFruit($count)
{
$doubleCount = $count * 2;//double the count used in calculation with the random number
$fruitIDs = ''; //the choosen fruits (id's)
$i = 0;
//#1 get total count of fruits table
$sql = "SELECT COUNT(*) FROM `fruits`";
if ($query = $this->_db->prepare($sql)) {
if ($query->execute()) {
$allFruits = $query->fetch(PDO::FETCH_NUM);
} else {
print_r("ERROR QUERY DID NOT EXECUTE #1");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #1");
}
//#2 calculate random number to pull from all of id's
$sql = "SELECT id FROM `fruits` WHERE RAND()* ? < ? ORDER BY RAND() LIMIT 0, ? ";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $allFruits[0], PDO::PARAM_INT);
$query->bindParam(2, $doubleCount, PDO::PARAM_INT);
$query->bindParam(3, $count, PDO::PARAM_INT);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
if ($i == 0) {
$fruitIDs .= "'" . $row['id'] . "'";
} else {
$fruitIDs .= ", '" . $row['id'] . "'";
}
$i++;
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #2");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #2");
}
//#3 get the fruits
$sql="SELECT NAME FROM `fruits` WHERE `id` IN( ? )";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $fruitIDs, PDO::PARAM_STR);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$this->_fruitArray[] = $row['name'];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #3");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #3");
}
return $this->_fruitArray;
}
}
The table that I am attempting has a bunch of "fruits" in it, an example of how the table is structured:
==================================
| ID | NAME |
==================================
| 01 | Apple |
==================================
I am attempting to test this all out by using the following:
echo "<pre>";
echo "TESTING FRUIT ARRAY:</br></br>";
$basket = new FruitBasket();
echo"</br></br> PRINT_R: </br></br>";
print_r($basket->pickFruit(10));
echo "</br></br> VARDUMP: </br></br>";
var_dump($basket->pickFruit(10));
The sql query prepares and executes properly, I can do a vardump of the prepares and the binds and they return TRUE. Nothing is returned on the last query however.
In the first query that executes Doing a print statement of $allFruits shows the correct total count from the table.
The second query seems to be working properly,the string $fruitIDs, gets random id's from the table, I can echo this out and confirm that indeed the correct number of ID's are returned.
The problem occurs (I think) with the third query:
Nothing is returned form this query. The prepare statement returns true on a var dump as does the execute, however there is no results!
If I manually take the ID's that are output from query#2 and run it myself in mysql, the correct "fruit" names are returned.
Am I binding the variables incorrectly? I read the pages from the PHP manual but clearly I am doing something wrong.
Please help! :)
Thanks to the links and input provided by Your common sense, using the following:
Reference - frequently asked questions about PDO
and
Can I bind an array to an IN() condition?
I was able to resolve this by changing my query as follows:
//#2 calculate random number to pull from all of id's
$sql = "SELECT id FROM `fruits` WHERE RAND()* ? < ? ORDER BY RAND() LIMIT 0, ? ";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $allFruits[0], PDO::PARAM_INT);
$query->bindParam(2, $doubleCount, PDO::PARAM_INT);
$query->bindParam(3, $count, PDO::PARAM_INT);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$fruitIDs[] = $row['id'];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #2"); }
} else {
print_r("ERROR CHECK SQL SYNTAX #2");
}
//#3 get the fruits
$inQuery = implode(',', array_fill(0, count($fruitIDs), '?'));
$sql="SELECT NAME FROM `fruits` WHERE `id` IN($inQuery)";
if ($query = $this->_db->prepare($sql)) {
if ($query->execute($fruitIDs)) {
while ($row = $query->fetch(PDO::FETCH_NUM)) {
$this->_fruitArray[] = $row[0];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #3");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #3");
}
return $this->_fruitArray;
}
I do not fully understand the security benefits or ramifications of binding the parameters or simply including them in the actual execute() but for now the query is performing as intended, so thank you for the input!

How to use prepared statements (named parameters) on a php Class

I want to know how to use named parameters in a prepared statement with pdo class, so the call to pdo look something like following.
$query = $bdd->prepare('SELECT * FROM table WHERE login = :login AND pww = :pww');
$query->execute(array('login' => $login, 'pww' => $pww));
And I want to integrate this on a class regardless of the number of parameters.
Currently, I have this code
require_once 'constants.php';
class Mysql extends PDO {
private $con;
public function __construct() {
try {
$this->con = parent::__construct(DB_DSN, DB_USER, DB_PASS);
if ($this->getAttribute(PDO::ATTR_DRIVER_NAME) == DB_TYPE)
$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
return $this->con;
} catch (PDOException $e) {
die('Error:' . $e->getMessage());
}
}
public function select($reqSelect) {
try {
$this->con = parent::beginTransaction();
$result = parent::prepare($reqSelect);
$result->execute();
//$this->con = parent::commit();
$this->con = parent::rollBack();
return $result;
$result->closeCursor();
} catch (Exception $e) {
die('Error:' . $e->getMessage());
}
}
public function selectAll($reqSelect) {
$result = parent::prepare($reqSelect);
$result->execute();
$resultat = $result->fetchAll();
return $resultat;
$result->closeCursor();
}
}
And for parameters, I use somethings like ( which is wrong and vulnerable to injection )
require_once 'classes/Mysql.class.php';
$mysql = new Mysql();
$sql = 'SELECT * FROM articles WHERE id = '.$_GET['id'].' LIMIT 1';
$data = $mysql->select($sql);
Thanks.
So it's seems that I have figured it out, the trick was adding an optional parameter to the function, you use it whenever you need to work with prepared statements (named parameters).
So the function is something like
public function selectAll($reqSelect, $param = null) {
$result = parent::prepare($reqSelect);
//Check whether the parameter was passed or not
if (is_null($param)) {
$result->execute();
$resultat = $result->fetchAll();
return $resultat;
} else {
//Binding the parameters
$result->execute($param);
$resultat = $result->fetchAll();
return $resultat;
}
$result->closeCursor();
}
And for applying it, it goes like
//First param, the SQL. Here we have named parameters, so we need them to get bind
$sql = 'SELECT * FROM articles WHERE publish = :number';
//Second param, the parameters that will get bind with the named ones
$param = array(':number' => 1);
$query = $mysql->selectAll($sql, $param);
foreach ($query as $row) {
extract($row);
echo $title . '<br />';
}
I don't know if this, is considered the best practice, secured or even correct. if I'm mistaken feel free to correct me.

Prepared mysqli select statement on longtext field is coming back empty

I've got a database query function that works well -- except that I'm running into what's apparently a known issue with mysqli prepared statements and longtext fields. What happens is that the longtext field always comes up empty even though running the query through phpMyAdmin works fine. According to http://www.workinginboxershorts.com/php-mysqli-returns-empty-variables-from-longtext-column, switching the datatype to text solves the problem. However, in my case I'd really prefer to leave the field as longtext as I can foresee times when that extra space would be valuable.
I'm using parameterized queries, which evidently is the problem. Here's my function:
// Bind results to an array
// $stmt = sql query, $out = array to be returned
function stmt_bind_assoc (&$stmt, &$out) {
$data = mysqli_stmt_result_metadata($stmt);
$fields = array();
$out = array();
$fields[0] = $stmt;
$count = 1;
while($field = mysqli_fetch_field($data)) {
$fields[$count] = &$out[$field->name];
$count++;
}
call_user_func_array('mysqli_stmt_bind_result', $fields);
}
// DB Query
// $query = SQL query, $params = array of parameters, $rs = whether or not a resultset is expected, $newid = whether or not to retrieve the new ID value;
// $onedimensionkey = key required to convert array into simple one dimensional array
function db_query($query, $params, $rs = true, $newid = false, $onedimensionkey = false) {
$link = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
if (!$link) {
print 'Error connecting to MySQL Server. Errorcode: ' . mysqli_connect_error();
exit;
}
// Prepare the query and split the parameters array into bound values
if ($sql_stmt = mysqli_prepare($link, $query)) {
if ($params) {
$types = '';
$new_params = array();
$params_ref = array();
// Split the params array into types string and parameters sub-array
foreach ($params as $param) {
$types .= $param['type'];
$new_params[] = $param['value'];
}
// Cycle the new parameters array to make it an array by reference
foreach ($new_params as $key => $parameter) {
$params_ref[] = &$new_params[$key];
}
call_user_func_array('mysqli_stmt_bind_param', array_merge(array($sql_stmt, $types), $params_ref));
}
}
else {
print 'Error: ' . mysqli_error($link);
exit();
}
// Execute the query
mysqli_stmt_execute($sql_stmt);
// If there are results to retrive, do so
if ($rs) {
$results = array();
$rows = array();
$row = array();
stmt_bind_assoc($sql_stmt, $results);
while (mysqli_stmt_fetch($sql_stmt)) {
foreach ($results as $key => $value) {
$row[$key] = $value;
}
$rows[] = $row;
}
if ($onedimensionkey) {
$i = 0;
foreach ($rows as $row) {
$simplearray[$i] = $row[$onedimensionkey];
$i++;
}
return $simplearray;
}
else {
return $rows;
}
}
// If there are no results but we need the new ID, return it
elseif ($newid) {
return mysqli_insert_id($link);
}
// Close objects
mysqli_stmt_close($sql_stmt);
mysqli_close($link);
}
According to the link that I posted there is a workaround involving the order in which things are done, but either I'm handling my query in a completely different manner than the example or I'm simply not understanding something important.
Thanks to anyone who can help!
EDIT: Thanks to Corina's answer, I've solved this -- for anyone else who runs into the problem, you will simply need to add the following after the mysql_stmt_execute command:
// Execute the query
mysqli_stmt_execute($sql_stmt);
// Store results
mysqli_stmt_store_result($sql_stmt);
I managed to solve the same issue by calling mysqli_stmt_store_result before binding the data.
Someone had the same problem and shared the answer on the php.net website:
Apparently, if you have longtext present, you HAVE to call
store_result before using bind_result.
http://bugs.php.net/bug.php?id=47928

using mysqli to extract custom data

I am using a function in php for all select queries so that i can dynamically retrieve data from my database ..... I just wanted to know that is my code secure and efficient or if their is a better way to do this, if so please point me to the right direction...thanks
class mysql {
private $conn;
function __construct(){
$this->conn= new mysqli(DB_SERVER, DB_USER, DB_PASSWORD, DB_NAME);
if( mysqli_connect_errno() )
{
trigger_error('Error connecting to host. '.$this->connections[$connection_id]->error, E_USER_ERROR);
}
}
function extracting_data($table, $fields,$condition,$order,$limit){
$query="SELECT ".$fields."
FROM ".$table."
WHERE id =".$this->sql_quote($condition)."
ORDER BY ".$order."
LIMIT ".$limit." ";
//echo $query;
if($stmt = $this->conn->prepare($query)) {
$stmt->execute();
$row = array_pad(array(), $stmt->field_count, '');
$params = array();
foreach($row as $k=>$v) {
$params[] = &$row[$k];
}
call_user_func_array(array($stmt,'bind_result'),$params);
$result = array();
while($stmt->fetch()) {
foreach ($row as $b=>$elem) {
$vals[$b]=$row[$b];
}
$result[]=$vals;
}
$stmt->close();
return $result;
}
}
function sql_quote( $value )
{
if( get_magic_quotes_gpc() )
{
$value = stripslashes( $value );
}
//check if this function exists
if( function_exists( "mysql_real_escape_string" ) )
{
$value = mysql_real_escape_string( $value );
}
//for PHP version < 4.3.0 use addslashes
else
{
$value = addslashes( $value );
}
return $value;
}
}
Now to call the function I am using ::>
$connection=New mysql();
$extract=$connection->extracting_data("tablename","id,name,points","$_GET['id']","date desc","0,10");
The function returns a multi-dimensional array in $result and stores it in $extract ,depending on the data I want to extract..
Any improvements or other suggestions would be appreciated ...
Instead of binding the results and having to do loads of looping, you could simply use mysqli::query() and mysqli_result::fetch_all().
if($stmt = $this->conn->query($query)) {
$result = $stmt->fetch_all(MYSQLI_ASSOC);
$stmt->close();
return $result;
}
You would be better off binding your input variables rather than building up an SQL string containing them, but that may not be feasible using your current approach.
Edit
Sorry, I was an idiot and didn't notice that fetch_all() is only in PHP >= 5.3. You can still do this though which is simpler:
if($stmt = $this->conn->query($query)) {
$result = array();
while ($row = $stmt->fetch_assoc()) {
$result[] = $row;
}
$stmt->close();
return $result;
}
You should watch where the parameters for your function come from. If they come from an unreliable source, then it's very insecure.
If someone passes something like 1 ; DROP TABLE tablename ; SELECT * FROM dual WHERE 1 in the $condition parameter, you'll get the Little Bobby Tables scenario.
Your query will look like the following:
SELECT id, name, points
FROM tablename
WHERE id
ORDER BY
DATE DESC
LIMIT 0, 10
The id here will be casted to BOOLEAN, and the query will select all ids except 0 and NULL.
Is it really what you want?
You probably want to change your $condition to 'id = $id' or something like that.
Do you really need this level of abstraction: generating queries from uknown tables with unknown fields but with predefined SELECT / FROM / ORDER BY / LIMIT stucture?

Categories