mysql limit clause not working alongside php bindvalue [duplicate] - php

This question already has answers here:
PHP PDO bindParam was falling in a foreach
(2 answers)
Closed 4 years ago.
After attempting to create a pagination, I noticed that PHP's bindValue does not work when using alongside MySQL's ORDER BY or LIMIT clauses.
This is pretty strange considering it'll work anywhere else within MySQL (at least as far as I'm aware). For example, this works completely fine:
SELECT * FROM table WHERE foo = ? AND bar = ?
However, when attempting something like:
SELECT * FROM table ORDER BY date DESC LIMIT ?, ?
It will return an empty object.
This is how I'm executing my queries (please note that this is all simplified, and the functions that I'm using are all apart of a class):
// query function
function query($sql, $params){
if($query = $pdo->prepare($sql)){
$x = 1;
if(count($params)){
foreach($params as $param){
$query->bindValue($x, $param);
$x++;
}
}
if($query->execute()){
return $query->fetchAll(PDO::FETCH_OBJ);
} else {
return false;
}
}
}
// executing a query (this will return an empty object)
$query = query("SELECT * FROM table ORDER BY date DESC LIMIT ?, ?", array(0, 10));
var_dump($query);
Can't seem to figure this one out, so all help is appreciated,
Cheers.

You Params will be included as Strings. Use a constant to tell bindValue to bind the value as an int.
$query->bindParam(2, $x, PDO::PARAM_INT);

Related

PHP Get the class constructor dynamically and call it to create a new instance [duplicate]

This question already has answers here:
Convert PDO resultset to array of objects
(5 answers)
Closed 2 years ago.
I´m trying to get all records from the database and then instantiate a new object with the data from each one:
$stmt = $this->pdo->prepare('SELECT * FROM '.$this->table.' ORDER BY '.$order.' ASC');
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($results){
for ($i=0; $i<count($results); ++$i){
$strValues = '';
$values = array_values($results[$i]);
for ($j=0; $j<count($values); ++$j){//Extract the values to pass them as arguments to the constructor below
if ($j == 0) $strValues .= $values[$j];
else{
$strValues .= ',';
$strValues .= '"'.$values[$j].'"';
}
}
eval('$result['.$i.'] = new '.get_class($this->instance).'('.$strValues.');');
}
}
The problem is that the object type can vary depending on if I'm querying the users or labels databases, so the call to constructor may be new User(user_id, username, name, surname) or new Label(label_id, name, description, color). The code above runs on the ObjectMapper class, which on creation gets assigned an object type and stores an instance of it in the private instance variable. This way I can get the name of the required constructor with get_class($this->instance).
I finally managed to make it work using an eval as shown, but I've read that this is not a good practice and I would like to know of better and cleaner ways to do it.
This should do what you are trying to achieve. Do not use eval
PDO has plenty of useful fetch modes and they are described here https://phpdelusions.net/pdo/fetch_modes#FETCH_CLASS
$stmt = $this->pdo->prepare('SELECT * FROM '.$this->table.' ORDER BY '.$order.' ASC');
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_CLASS, get_class($this->instance));
P.S. Remember to properly whitelist the table name and $order variable.

Parametrized PDO query for LIMIT clause

I have seen similar questions answered already but I can't seem to apply the same solutions to my code.
$a=1;
$results = DB::query('SELECT posts.`postbody`, posts.`filepost`, posts.`likes`, posts.`posted_at`, users.`id`, posts.`id_of_post` FROM posts, users WHERE posts.`post_id` = users.`id` ORDER BY id_of_post DESC LIMIT :a', array(':a'=>$a));
class DB {
private static function connect() {
$pdo = new PDO('mysql:host=127.0.0.1;dbname=SocialNetwork;charset=utf8', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public static function query($query, $params = array()) {
$statement = self::connect()->prepare($query);
$statement->execute($params);
if (explode(' ', $query)[0] == 'SELECT') {
$data = $statement->fetchAll();
return $data;
}
}
}
For the record the following code works fine.
$results = DB::query('SELECT posts.`postbody`, posts.`filepost`, posts.`likes`, posts.`posted_at`, users.`id`, posts.`id_of_post` FROM posts, users WHERE posts.`post_id` = users.`id` ORDER BY id_of_post DESC LIMIT 1');
Not ideal, but you could do away with the PDO parameters.
$a = 1;
$sql = "SELECT stuff FROM table LIMIT {$a};";
Then run your query from the $sql string.
As stated in the previous answers if you do not define:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
You have to define the parameter to be binded as integer:
foreach($params as $key => $value){
if(is_numeric($value))
$statement->bindParam($key,$value,PDO::PARAM_INT);
else
$statement->bindParam($key,$value,PDO::PARAM_STR);
}
$statement->execute();
This is still not a perfect solution, but if you trust the key value pairs(ie they are from code, not user input) it's good enough.
In MySQL's LIMIT clause, it's an error to do this:
LIMIT '1'
Because LIMIT must take an integer, not a string.
If PDO is configured to emulate prepare() (by interpolating values into your SQL string), it's likely to make the interpolated value a quoted string, which will cause an error.
To avoid this, you must use a native integer as your bound variable and you just specify PDO::PARAM_INT.
$statement = self::connect()->prepare($query);
$statement->bindParam('a', $a, PDO::PARAM_INT);
$statement->execute();
That will let the driver know to avoid putting quotes around the interpolated value.
You can also avoid the error if you set the PDO attribute to disable emulated prepares. I always do this, because I don't trust "emulated prepare."
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
See also my tests I wrote up here: Parametrized PDO query and `LIMIT` clause - not working

How to use a quoted string variable in a MySqli prepared statement?

Sorry if this seems a really stupid question, but I'm struggling to get to grips with changing from Mysql to Mysqli and prepared statements.
So in mysql, I would have done this:
$q=('SELECT * FROM table WHERE field="'.$variable.'"');
$result = mysql_query($q);
I now know this is not good. So I now have the below:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
$stmt->execute();
Problem is that the query doesn't work. Say the ? is actually "tree". So the query becomes:
'SELECT * FROM table WHERE field=tree LIMIT 1'
If I tried to run that query in say phpmyadmin I get "Unknown column tree in where clause". Obviously if I put quotes around it then it works, hence the original query. So how can I get this to work if I can't use quotes, since then you are looking for the literal question mark?
For reference I am then using this code:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
As I can't use get_result() which is very annoying. I have PHP version 5.4, and even the mysqlnd driver, but can't enable it as I'm on a VPS and my host says it might affect other sites on that server. Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Any help would be appreciated!
This:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
is not equivalent to this:
SELECT * FROM table WHERE field=tree LIMIT 1
Prepared statement placeholders are not the same as copy and pasting in values. You are binding the value "tree" as a string here, the database will actually understand this. The ? is not simply being replaced by the bound value, the database understands the difference between your query structure with its placeholders and the values you're binding into them. Binding the parameter this way is equivalent to running:
SELECT * FROM table WHERE field='tree' LIMIT 1
Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Mysqli is not intended to be used as is. It is but a building material for the higher level library. When used wisely, it can give you data in one line:
$data = $db->getAll('SELECT * FROM table WHERE field=?s', $variable);
(BTW, the same goes for the old mysql ext as well)

Error while using PDO prepared statements and LIMIT in query [duplicate]

This question already has answers here:
How to apply bindValue method in LIMIT clause?
(11 answers)
Closed 7 years ago.
I'm using PDO in my application. But I have a problem while I'm working with prepared statements in a query that contains LIMIT. What's the problem?
Codes:
$start = 0;
$rows = 20;
$sql = "SELECT * FROM tbl_news ORDER BY date DESC LIMIT ?, ?";
$q = $db->prepare($sql);
$q->execute(array($start , $rows));
Error:
check the manual that corresponds to your MySQL server version for the right syntax to use near ''0', '20''
You can do like this:
$sql = SELECT * FROM tbl_news ORDER BY date DESC LIMIT :start, :rows";
$q = $db->prepare($sql);
$q->bindParam(':start', $start, PDO::PARAM_INT);
$q->bindParam(':rows',$rows, PDO::PARAM_INT);
Regarding to post LIMIT keyword on MySQL with prepared statement , the code below could solve my problem.
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
Thanks Álvaro G. Vicario and Maerlyn
It is a known bug which was fixed in 5.5.6 from memory.
From the article:
LIMIT doesn't allow variables in any context.
Its arguments must be integer constants.
Further Edit: (There is contention on the matter)
User variables are accepted arguments of LIMIT clause in prepared statements, and SQL syntax for prepared statements can be used in stored procedures.
Third Edit:
This link explains that these should work with prepared statements.
I just stumbled upon the same problem. For me, using my own statement class (extending PDOStatement) with my own execute() method fixed it.
This is the class:
class MyPDOStatement extends PDOStatement {
public function execute($input_parameters = null) {
if (is_array($input_parameters)) {
$i = 1;
foreach ($input_parameters as $p) {
// default to string datatype
$parameterType = PDO::PARAM_STR;
// now let's see if there is something more appropriate
if (is_bool($p)) {
$parameterType = PDO::PARAM_BOOL;
} elseif (is_null($p)) {
$parameterType = PDO::PARAM_NULL;
} elseif (is_int($p)) {
$parameterType = PDO::PARAM_INT;
}
// parameters passed to execute() are input-only parameters, so use
// bindValue()
$this->bindValue($i, $p, $parameterType);
$i++;
}
}
return parent::execute();
}
}
To tell PDO to use this statement class instead of the default one, do this:
$db = new PDO(...);
$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('MyPDOStatement'));
Now the code in the question will work:
$start = 0;
$rows = 20;
$sql = "SELECT * FROM tbl_news ORDER BY date DESC LIMIT ?, ?";
$q = $db->prepare($sql);
$q->execute(array($start , $rows));
The only thing you have to make shure is that the variables bound to the statement have the correct type, integer. If you have a numeric string, e.g. from the $_GET array, you can do something like this:
if (isset($_GET['start']) && is_numeric($_GET['start'])
&& is_int($_GET['start'] + 0) {
$start = (int) $_GET['start'];
}
I'm not shure if there is an easier way for the last thing, but at least it works fine for me.
date is a reserved word you have to wrap it with back-ticks

Setting PDO/MySQL LIMIT with Named Placeholders [duplicate]

This question already has answers here:
How can I pass an array of PDO parameters yet still specify their types?
(3 answers)
Closed 7 years ago.
I'm having an issue binding the LIMIT part of an SQL query. This is because the query is being passed as a string. I've seen another Q here that deals with binding parameters, nothing that deals with Named Placeholders in an array.
Here's my code:
public function getLatestWork($numberOfSlides, $type = 0) {
$params = array();
$params["numberOfSlides"] = (int) trim($numberOfSlides);
$params["type"] = $type;
$STH = $this->_db->prepare("SELECT slideID
FROM slides
WHERE visible = 'true'
AND type = :type
ORDER BY order
LIMIT :numberOfSlides;");
$STH->execute($params);
$result = $STH->fetchAll(PDO::FETCH_COLUMN);
return $result;
}
The error I'm getting is: Syntax error or access violation near ''20'' (20 is the value of $numberOfSlides).
How can I fix this?
The problem is that execute() quotes the numbers and treats as strings:
From the manual - An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.
<?php
public function getLatestWork($numberOfSlides=10, $type=0) {
$numberOfSlides = intval(trim($numberOfSlides));
$STH = $this->_db->prepare("SELECT slideID
FROM slides
WHERE visible = 'true'
AND type = :type
ORDER BY order
LIMIT :numberOfSlides;");
$STH->bindParam(':numberOfSlides', $numberOfSlides, PDO::PARAM_INT);
$STH->bindParam(':type', $type, PDO::PARAM_INT);
$STH->execute();
$result = $STH->fetchAll(PDO::FETCH_COLUMN);
return $result;
}
?>
I'd suggest binding the params and forcing their type:
$STH->bindParam(':numberOfSlides', $numberOfSlides, PDO::PARAM_INT);
$STH->execute();

Categories