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.