Предположим, что я должен был выполнить подготовленное заявление следующим образом:
$qry->prepare('UPDATE table_name SET column1 = ? string_column = ? WHERE column3 = ? AND column4 = ?'); $qry->bind_param('sbid', $string, $blob, $int, $double); $int = 'non int value'; /* gives 0 in the database */ $blob = 'some string'; $string = 'another string'; $double = $double; $qry->execute(); $qry->close();
Скажем так, я только хотел выполнить запрос один раз, я просто использовал подготовленный оператор во имя безопасности. Из того, что я читал, – это больше накладных расходов, чтобы использовать подготовленные запросы только один раз, и это сводится к снижению производительности для преимуществ безопасности. Сказанное – какова будет разница производительности / безопасности при выполнении одного и того же запроса в один раз.
$int = (int) $int; $blob = "'" .mysql_real_escape_string($blob) ."'"; $string = "'" .mysql_real_escape_string($blob) ."'"; $double = (double) $double; $db->query("UPDATE SET column1 = $int, column2 = $blob WHERE column3 = $string AND column4 = $double ");
PS. Мне неинтересно, как подготовленные утверждения улучшают производительность, но о безопасности и разнице скоростей для одного запроса.
Этого очень много. Некоторые случайные точки
или:
IN ( )
) часто упускается из виду:
Мой любимый:
Поэтому выбор стиля часто приходится делать в каждом конкретном случае. Мы использовали способ инкапсуляции всего доступа к БД, включая управление параметрами, в стандартизованную библиотеку, которая просто require()
ed, поэтому вы можете вставлять замену с помощью подготовленных запросов, экранов или любого другого, что вы хотите, и ваша RDBMS поддерживает.
Спасибо за большой вопрос.
По сути, вы можете использовать оба метода одновременно.
Большинство людей путают идею подготовленного заявления в целом с [очень ограниченной] реализацией, предлагаемой крупными СУБД. В то время как последнее может быть поставлено под сомнение, первое – это единственный способ.
Взгляните на пример. Давайте запустим ваш запрос с помощью safeMysql :
$sql = "UPDATE SET column1 = ?i, column2 = ?s WHERE column3 = ?s AND column4 = ?s"; $db->query($sql, $string, $blob, $int, $double);
Он выполняет очень строковое форматирование, как ваш код, но делает его внутренне . Зачем? Потому что неважно, как это реализовано внутренне (с помощью встроенного подготовленного оператора или ручного форматирования), но важно использовать подготовленный оператор для сборки вашего запроса в любом случае.
Есть некоторые существенные моменты в отношении подготовленных заявлений, которые игнорируются большинством людей:
он делает форматирование всегда полным (хотя в вашем примере вы делаете правильные вещи и делаете полное форматирование, все равно очень легко перейти к неполному, например:
$colname = "`$colname`";
ваше форматирование всегда правильное. он не позволит вам сделать что-то вроде
$colname = "`" .mysql_real_escape_string($colname) ."`";
который будет бесполезен и приведет вас к инъекции
он сделает форматирование обязательным . С сборкой запроса ваш текущий способ очень легко упустить переменную или две.
Это реальные преимущества подготовленных заявлений, которые гарантируют безопасность и, таким образом, делают их настолько популярными. Хотя эта вещь с подготовкой на стороне сервера, хотя и довольно умная, является лишь конкретной реализацией.
Также, принимая идею подготовленного заявления в качестве руководства, можно создать местозаполнитель для всего, что может быть добавлено в запрос (например, идентификатор или массив), что делает его действительно безопасным и удобным в использовании.
Помня все, что нужно сделать, нужно реализовать саму идею подготовленного заявления в своей библиотеке доступа к БД, чтобы сделать код безопасным и коротким.
Всего несколько примеров из safeMysql:
$name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']); $data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2)); $data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit); $ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag); $data = $db->getAll("SELECT * FROM table WHERE category IN (?a)",$ids); $data = array('offers_in' => $in, 'offers_out' => $out); $sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u"; $db->query($sql,$pid,$data,$data);
Просто попробуйте то же самое с обычным mysql (i) и посмотрите, сколько кода вам потребуется.
Вы можете заметить, что при использовании готовых операторов вы должны пометить их типом, потому что существует больше типов, чем просто простая строка, и это единственный надежный способ сообщить драйверу, как отформатировать вашу переменную.
Я считаю, что они одинаково безопасны с точки зрения безопасности, но использование prepare
не только делает ваш SQL безопасным, но и делает вас безопасным. Вы не можете доверять себе, чтобы вручную бежать и преобразовывать в правильный тип все время. Если вы напишете 10 000 различных SQL-запросов, вы, как правило, забудете избежать одного или двух.
Итак, в заключение, prepare
– лучшая привычка бороться с инъекцией SQL. Помещение PHP-переменной непосредственно в SQL-запрос заставляет меня чувствовать себя неловко, когда спать по ночам.