Ошибка PHP PDO при попытке параметризовать SQL LIMIT: offset,: display?

Этот запрос возвращает 5 результатов в phpMyAdmin:

SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5 

И это возвращает count = 12 в phpMyAdmin (это нормально, потому что есть 12 записей):

 SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5 

Эта функция работала нормально, прежде чем я добавил две переменные (смещение, отображение), однако теперь это не сработает, и распечатка переменных дает мне смещение = 0, display = 5 (так что еще LIMIT 0,5 ).

 function getProducts($offset, $display) { $sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;"; $data = array((int)$offset, (int)$display); $rows = dbRowsCount($sql, $data); logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows."); if ($rows > 0) { dbQuery($sql, $data); return dbFetchAll(); } else { return null; } } 

Он не работает, потому что dbRowsCount(...) возвращал пустую строку (глупое PDOStatement::fetchColumn ), поэтому я изменил ее, чтобы вернуть счет с помощью PDO::FETCH_ASSOC и возвращает count = 0.

Вот функция, которая выполняет подсчет строк:

 function dbRowsCount($sql, $data) { global $db, $query; $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is'; if (preg_match($regex, $sql, $output) > 0) { $query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}"); logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}"); $query->setFetchMode(PDO::FETCH_ASSOC); if ($data != null) $query->execute($data); else $query->execute(); if (!$query) { echo "Oops! There was an error: PDOStatement returned false."; exit; } $result = $query->fetch(); return (int)$result["count"]; } else { logErrors("Regex did not match: ".$sql); } return -1; } 

Мой журнал ошибок дает мне этот результат из программы:

Вывод регулярного выражения: SELECT COUNT (*) AS count FROM tbl_product ГДЕ 1 ORDER BY last_update DESC LIMIT?,?;
getProducts (0,5) возвратил 0 строк.

Как вы можете видеть, SQL не был искажен, а входные переменные метода были равны 0 и 5, как ожидалось.

Кто-нибудь знает, что пошло не так?

Обновить

Следуя предложению, я попытался выполнить запрос напрямую, и он вернул правильный результат:

 function dbDebugTest() { global $db; $stmt = $db->query("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT 0,5;"); $result = $stmt->fetch(); $rows = (int)$result["count"]; logErrors("dbDebugTest() returned rows=".$rows); } 

Вывод:

 > dbDebugTest() returned rows=12 

Следуя другому предложению, я изменил! = Null на! == null, и я также распечатал массив $ data:

 logErrors("Data: ".implode(",",$data)); if ($data !== null) $query->execute($data); else $query->execute(); 

Вывод:

 > Data: 0,5 

Однако dbRowsCount ($ sql, $ data) по-прежнему возвращает 0 строк для этого запроса!

Обновление 2

Следуя советам по внедрению пользовательского класса PDOStatement, который позволит мне выводить запрос после привязки значений, я обнаружил, что функция остановится после $ query-> execute ($ data), и поэтому вывод не будет напечатан, хотя пользовательский класс работает для каждого другого запроса в моей программе.

Обновленный код:

 function dbRowsCount($sql, $data) { global $db, $query; $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is'; if (preg_match($regex, $sql, $output) > 0) { $query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}"); logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}"); $query->setFetchMode(PDO::FETCH_ASSOC); logErrors("Data: ".implode(",",$data)); $query->execute($data); logErrors("queryString:".$query->queryString); logErrors("_debugQuery():".$query->_debugQuery()); if (!$query) { echo "Oops! There was an error: PDOStatement returned false."; exit; } $result = $query->fetch(); return (int)$result["count"]; } else { logErrors("Regex did not match: ".$sql); } return -1; } 

Вывод:

Вывод регулярного выражения : SELECT COUNT ( ) AS count FROM tbl_product_category WHERE id = ?;
Данные: 5
queryString: SELECT COUNT ( ) AS count FROM tbl_product_category WHERE id = ?;
_debugQuery (): SELECT COUNT (*) AS count FROM tbl_product_category WHERE id = ?;

Вывод регулярного выражения: SELECT COUNT (*) AS count FROM tbl_product ГДЕ 1 ORDER BY last_update DESC LIMIT?,?;
Данные: 0,5
// функция остановлена ​​и _debugQuery не может быть выведен

Обновление 3

Поскольку я не мог получить собственный класс PDOStatement, чтобы дать мне некоторый результат, я подумал, что я переписал getProducts(...) чтобы вместо этого привязать параметры с именами-заполнителями.

 function getProducts($offset, $display) { $sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;"; $data = array(':offset'=>$offset, ':display'=>$display); $rows = dbRowsCount($sql, $data); logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows."); if ($rows > 0) { dbQuery($sql, $data); return dbFetchAll(); } else { return null; } } 

Вывод:

Вывод регулярного выражения: SELECT COUNT (*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT: offset,: display;
Данные: 0,5
// По-прежнему происходит сбой после $ query-> execute ($ data), поэтому logErrors("getProducts(".$offset."...)) не распечатывается

Обновление 4

Этот dbDebugTest ранее работал с объявлением предельных значений 0,5 непосредственно в строке SQL. Теперь я обновил его, чтобы правильно привязать параметры:

 function dbDebugTest($offset, $display) { logErrors("Beginning dbDebugTest()"); global $db; $stmt = $db->prepare("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT :offset,:display;"); $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); $stmt->bindParam(':display', $display, PDO::PARAM_INT); if ($stmt->execute()) { $result = $stmt->fetch(); $rows = (int)$result["count"]; logErrors("dbDebugTest() returned rows=".$rows); } else { logErrors("dbDebugTest() failed!"); } } 

Функция сработает, и только это выводится:

Начало dbDebugTest ()

Обновление 5

Следуя предложению об ошибках (они отключены по умолчанию), я сделал следующее:

 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

Это само по себе сделало работу dbDebugTest () из Update 4!

Начало dbDebugTest () dbDebugTest () возвращенных строк = 12

И теперь в моих журналах веб-сервера генерируется ошибка:

[warn] mod_fcgid: stderr: PHP Неустранимая ошибка:
Исключить исключение «PDOException» с сообщением «SQLSTATE [42000]:
Ошибка синтаксиса или нарушение доступа: 1064 У вас есть ошибка в синтаксисе SQL;
проверьте руководство, соответствующее версии сервера MySQL, для правильного использования синтаксиса
около '0', '5' 'в строке 1'
в /home/linweb09/b/example.com-1050548206/user/my_program/database/dal.php:36

Строка 36 относится к dbRowsCount(...) а строка – $query->execute($data) .

Таким образом, другой метод getProducts(...) прежнему не работает, потому что он использует этот метод привязки данных, а params превращаются в «0» и «5» (это ошибка?). Немного раздражает, но мне нужно будет создать новый метод в моем dal.php, чтобы я мог привязывать параметры более строгим способом – с помощью bindParam .

Особенно спасибо @ Travesty3 и @eggyal за помощь! Многое, очень ценится.

    На основе обновления 2 в вопросе, где выполнение останавливается после инструкции execute , похоже, что запрос не работает. Изучив некоторую документацию PDO , похоже, что для параметра обработки ошибок по умолчанию используется PDO :: ERRMODE_SILENT, что приведет к поведению, которое вы видите.

    Это, скорее всего, связано с тем, что числа в вашем предложении LIMIT помещаются в одинарные кавычки, когда они передаются как параметры, как это происходило в этом сообщении .

    Решение этой должности состояло в том, чтобы указать параметры как целые числа, используя метод bindValue . Поэтому вам, вероятно, придется сделать что-то подобное.

    И похоже, что вы также должны выполнять свои запросы с помощью блоков try-catch, чтобы уловить ошибку MySQL.


    Метод bindValue:

     if ($data !== null) { for ($i=0; $i<count($data); $i++) $query->bindValue($i+1, $data[$i], PDO::PARAM_INT); $query->execute($data); } else $query->execute(); 

    Вы проверяете, является ли $data NULL с помощью оператора равенства, а не идентификатора (см. Руководство по PHP для получения дополнительной информации о том, как значения NULL обрабатываются различными операторами сравнения). Вам нужно либо использовать тест идентификации === / !== , либо вызвать is_null() .

    Как упоминалось выше, Travesty3, чтобы проверить, пуст ли массив, используйте empty() .