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.

Обновление: после изменения типа параметра от целого числа до строки:

MySQLi Log:

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

PDO Log:

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

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

1 ответ

Решение

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

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

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

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

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

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

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

Другие вопросы по тегам