Php 为什么PDO比MySQL\u real\u escape\u string更适合转义MySQL查询/查询字符串?

Php 为什么PDO比MySQL\u real\u escape\u string更适合转义MySQL查询/查询字符串?,php,pdo,escaping,mysql-real-escape-string,Php,Pdo,Escaping,Mysql Real Escape String,有人告诉我,我最好使用PDO进行MySQL转义,而不是MySQL\u real\u escape\u string 也许我今天脑子不好(或者可能是因为我完全不是一个天生的程序员,在PHP方面我还处于新手阶段),但是在阅读了PHP手册之后,我仍然不清楚PDO到底是什么,为什么它比使用mysql\u real\u escape\u string更好。这可能是因为我还没有真正掌握OOP的复杂性(我假设这与OOP有关),但除了变量和数组值前面似乎有一个冒号之外,我仍然不确定它实际上是什么以及如何使用它(

有人告诉我,我最好使用
PDO
进行MySQL转义,而不是
MySQL\u real\u escape\u string

也许我今天脑子不好(或者可能是因为我完全不是一个天生的程序员,在PHP方面我还处于新手阶段),但是在阅读了PHP手册之后,我仍然不清楚PDO到底是什么,为什么它比使用
mysql\u real\u escape\u string更好。这可能是因为我还没有真正掌握OOP的复杂性(我假设这与OOP有关),但除了变量和数组值前面似乎有一个冒号之外,我仍然不确定它实际上是什么以及如何使用它(以及为什么它比
mysql\u real\u escape\u string
更好)。(这也可能与我对“类”不是很清楚这一事实有关,所以当我阅读“PDO类”时,我一点也不明白)

在MySQL网站的“开发者专区”上读过一两篇文章后,我还是不太清楚。因为我现在甚至不知道它是什么,我想现在使用它可能有点超出我的能力范围,但我仍然有兴趣扩大我的教育范围,找出我可以改进的地方


有没有人能用“通俗英语”向我解释什么是PDO(或者用通俗英语向我指出某个主题的方向),以及你将如何使用它?

想象一下你写的东西大致如下:

$query = 'SELECT * FROM table WHERE id = ' . mysql_real_escape_string($id);
这不会使您免于注入,因为$id可以是
1或1=1
,您将从表中获取所有记录。您必须将$id转换为正确的数据类型(在这种情况下为int)


pdo还有另一个优点,那就是数据库后端的可交换性。

除了防止SQL注入之外,pdo还允许您准备一个查询并多次执行。如果您的查询被执行多次(例如在一个循环中),这种方法应该更有效(我说“应该是”,因为在旧版本的MySQL上似乎并不总是这样)。prepare/bind方法也与我使用过的其他语言更加一致。

我对PDO不是非常熟悉,但“prepared语句”之间有区别和转义字符串。转义是关于从查询中删除不允许的字符串,而准备好的语句是关于告诉数据库所期望的查询类型

一个查询有多个部分 可以这样想:当您向数据库发出查询时,您会告诉它几件独立的事情。例如,一件事可能是“我希望您进行选择”。另一件事可能是“将其限制到用户名为以下值的行”

如果将查询构建为字符串并将其交给数据库,则在获得完整的字符串之前,它不知道其中任何一部分。您可以执行以下操作:

'SELECT * FROM transactions WHERE username=$username'
当它得到这个字符串时,它必须解析它并决定“这是一个
选择
和一个
WHERE

把零件弄混了 假设一个恶意用户将其用户名输入为
billysmith或1=1
。如果您不小心,可能会将其放入字符串中,导致:

'SELECT * FROM transactions WHERE username=billysmith OR 1=1'
…这将返回所有用户的所有事务,因为1总是等于1。哎呀,你被黑了

看到发生了什么吗?数据库不知道您的查询中应该包含哪些部分,所以它只解析了字符串。
中的
有一个
,有两个条件可以满足它,这并不奇怪

保持零件笔直 如果它只知道会发生什么,即
选择
位置
只有一个条件,恶意用户就不会欺骗它

通过一个准备好的语句,您可以给出正确的期望“我将向您发送一个
SELECT
,它将被限制在
行中,其中username=
是我将要给您的字符串。仅此而已-查询没有其他部分。您准备好了吗?好的,这是要与用户名进行比较的字符串。”

按照这种预期,数据库不会被愚弄:它只会返回
username
列包含实际字符串“billysmith或1=1”的行。如果没有人拥有该用户名,它将不会返回任何内容

编制报表的其他好处 除了安全方面的好处外,预先准备好的报表还有几个速度方面的好处:

  • 它们可以使用不同的参数进行重用,这应该比从头开始构建新查询要快,因为数据库已经基本上知道您要请求什么,它已经构建了它的“查询计划”
  • 一些数据库(我认为Postgres就是其中之一)会在收到准备好的语句后立即开始制定查询计划——在您实际发送参数以使用它之前。因此,即使在第一次查询时,您也可能会看到加速

另一种解释请参见Theo的答案。

与mysql\u real\u escape\u string不同,PDO允许您强制执行数据类型

<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
?>

第三,您不必确保正确引用参数。PDO会处理这些问题。例如,mysql_real_query_string:

SELECT * FROM user WHERE name = 'mysql_real_escape_string($name)' //note quotes around param
vs


最后,PDO允许您将应用程序移植到不同的数据库,而无需更改PHP数据调用。

随着当前答案的深入,而您的问题更倾向于概述,我将尝试一下:

PDO类旨在封装与数据库交互所需的所有功能。它们通过定义“方法”(函数为OO palor)和“属性”(变量为OO palor)来实现这一点。您可以将它们用作所有“sta”的完全替代品
SELECT name FROM user WHERE id = mysql_real_escape_string($id) AND admin = mysql_real_escape_string($admin);
SELECT * FROM user WHERE name = 'mysql_real_escape_string($name)' //note quotes around param
SELECT * FROM user WHERE name = ?
// Get a db connection
$connection = mysql_connect('someHost/someDB', 'userName', 'password');
// Prepare a query
$query = "SELECT * FROM someTable WHERE something = " . mysql_real_escape_string($comparison) . "'";
// Issue a query
$db_result = mysql_query($query);
// Fetch the results
$results = array();
while ($row = mysql_fetch_array($db_result)) {
  $results[] = $row;
}
// Instantiate new PDO object (will create connection on the fly)
$db = new PDO('mysql:dbname=someDB;host=someHost');
// Prepare a query (will escape on the fly)
$statement = $db->prepare('SELECT * FROM someTable WHERE something = :comparison');
// $statement is now a PDOStatement object, with its own methods to use it, e.g.
// execute the query, passing in the parameters to replace
$statement->execute(array(':comparison' => $comparison));
// fetch results as array
$results = $statement->fetchAll();