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.