PDO отправляет необработанный запрос в MySQL, в то время как Mysqli отправляет подготовленный запрос, оба дают тот же результат

Я начал понимать, как подготовленный оператор работает при использовании MySQLi и PDO, для первого шага я включил мониторинг запросов MySQL, как указано здесь: Как просмотреть текущие запросы MySQL?. Затем я создал следующий тест:

Использование mysqli:

$stmt = $mysqli->prepare("SELECT * FROM users WHERE username =?")) {
$stmt->bind_param("i", $user);
$user = "''1''";

журналы сервера:

  130802 23:39:39   175 Connect   ****@localhost on testdb
    175 Prepare   SELECT * FROM users WHERE username =?
    175 Execute   SELECT * FROM users WHERE username =0
    175 Quit

Использование PDO:

  $user = "''1''";
  $sql = 'SELECT * FROM user WHERE uid =?';
  $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  $sth->bindParam(1, $user, PDO::PARAM_INT);

Журналы сервера:

  130802 23:41:42   176 Connect   ****@localhost on testdb
    176 Query SELECT * FROM user WHERE uid ='\'\'1\'\''
    176 Quit

Однако оба обеспечивают одинаковый результат:

uid: 0
username: admin
role: admin

Примечание: uid = 0 является правильным, потому что intval("''1''") = 0

Что здесь важно:

Как запрос PDO получает тот же результат, когда он отправляет другой запрос в MySQL?

SELECT * FROM user WHERE uid ='\'\'1\'\''

Я нашел только одно указание из руководства PHP: http://www.php.net/manual/en/pdo.prepare.php

Примечание:

Эмулированные подготовленные операторы не связывают с базой данных сервера, поэтому PDO:: prepare() не проверяет утверждение.

Но я не уверен, как MySQL справляется с этим запросом и заменяет '\'\'1\'\'' на 0. В этом случае запросы мониторинга не будут точными при использовании PDO, в то же время использование PDO лучше знать точные запросы, отправляемые в MySQL, но не MySQLi.

Update: после изменения типа параметра frm integer в строку:

Журнал MySQLi:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

Журнал PDO:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

Это означает, что MySQLi и PDO избегают данных перед отправкой в ​​MySQL при использовании строки, в то время как для целых чисел mysqli применяет intval() или что-то вроде этого перед отправкой запроса, также как и ответил Билл, который является правильным.

Ответ 1

Ваш PDO настроен для эмуляции подготовленных запросов, тогда как mysqli использует истинные подготовленные запросы.

Подготовленный запрос связывает строку ''1'' как целочисленное значение параметра. PHP принуждает его к целому числу, используя что-то вроде intval(). Любая строка с нечисловыми ведущими символами интерпретируется как 0 PHP, поэтому значение параметра, отправленное после подготовки, является значением 0.

Поддельный подготовленный запрос использует строчную интерполяцию (вместо привязки) для добавления строки ''1'' в SQL-запрос до того, как MySQL проанализирует его. Но результат подобен, потому что SQL также обрабатывает строку с нечисловыми ведущими символами в целочисленном контексте как значение 0.

Единственное различие заключается в том, что заканчивается в общем журнале запросов, когда параметр привязан перед подготовкой и после его подготовки.

Вы также можете заставить PDO использовать реальные подготовленные запросы, поэтому в этом случае он должен действовать так же, как mysqli:

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

PS: Это может показаться хорошей причиной, по которой принято начинать значения id в 1 вместо 0.