Here is a snapshot of my code:
$fetchPictures = $PDO->prepare(\"SELECT *
FROM pictures
WHERE album = :albumId
ORDER BY id ASC
LIMIT :s
bindValue offset and limit using PDO::PARAM_INT and it will work
The simplest solution would be to switch the emulation mode off. You can do it by simply adding the following line
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Also, this mode can be set as a constructor parameter when creating a PDO connection. It could be a better solution as some report their driver doesn't support the setAttribute()
function.
It will not only solve your problem with binding, but also let you send values directly into execute()
, which will make your code dramatically shorter. Assuming the emulation mode has been already set, the whole affair will take as much as half a dozen lines of code
$skip = isset($_GET['skip']) ? (int)trim($_GET['skip']) : 0;
$sql = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$stmt = $PDO->prepare($sql);
$stmt->execute([$_GET['albumid'], $skip, $max]);
$pictures = $stmt->fetchAll(PDO::FETCH_ASSOC);
There is alot going on between different versions of PHP and the oddities of PDO.
I tried 3 or 4 methods here but could not get LIMIT working.
My suggestion is to use string formatting / concatination WITH an intval() filter:
$sql = 'SELECT * FROM `table` LIMIT ' . intval($limitstart) . ' , ' . intval($num).';';
It is very important to use intval() to prevent SQL injection, particularly if you are getting your limit from $_GET or the like. If you do that this is the easiest way to get LIMIT working.
There is alot of talk about 'The problem with LIMIT in PDO' but my thought here is that PDO params were never ment to be used for LIMIT since they will alway be integers a quick filter works. Still, it is a bit misleading since the philosophy has always been to not do any SQL injection filtering yourself but rather 'Have PDO handle it'.
Looking at the bug report, the following might work:
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);
but are you sure your incoming data is correct? Because in the error message, there seems to be only one quote after the number (as opposed to the whole number being enclosed in quotes). This could also be an error with your incoming data. Can you do a print_r($_GET);
to find out?
Since nobody has explained why this is happening, I'm adding an answer. The reason it is behaving this was is because you are using trim()
. If you look at the PHP manual for trim
, the return type is string
. You are then trying to pass this as PDO::PARAM_INT
. A few ways to get around this are:
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
to make sure you are passing an integer.intval()
(int)
is_int()
There are plenty more ways, but this is basically the root cause.
PDO::ATTR_EMULATE_PREPARES
gave me the
Driver does not support this function: This driver doesn't support setting attributes' error.
My workaround was to set a $limit
variable as a string, then combine it in the prepare statement as in the following example:
$limit = ' LIMIT ' . $from . ', ' . $max_results;
$stmt = $pdo->prepare( 'SELECT * FROM users WHERE company_id = :cid ORDER BY name ASC' . $limit . ';' );
try {
$stmt->execute( array( ':cid' => $company_id ) );
...
}
catch ( Exception $e ) {
...
}