48

有人告诉我,我最好使用PDOMySQL 转义,而不是mysql_real_escape_string.

也许我正在度过一个脑残的日子(或者可能是因为我没有想象力是一个天生的程序员,而且在 PHP 方面我仍然处于新手阶段),但是有查看了 PHP 手册并阅读了 PDO 上的条目,我仍然不清楚 PDO 到底是什么以及为什么它比使用mysql_real_escape_string. 这可能是因为我还没有真正掌握 OOP 的复杂性(我假设它与 OOP 有关),但除了变量和数组值前面似乎有一个冒号之外,我仍然不确定它实际上是什么以及您如何使用它(以及为什么它比mysql_real_escape_string. (这也可能与我对什么是“类”并没有清楚的了解有关,所以当我阅读“PDO 类”时,我真的一点也不聪明)。

在阅读了 MySQL 网站的“开发者专区”位上的一两篇文章后,我仍然不清楚。由于目前我什至无法弄清楚它是什么,我认为现在使用它可能有点超出我的能力,但我仍然有兴趣扩大我的教育范围并找出如何改进事情。

谁能用“简单的英语”向我解释 PDO 是什么(或指出我用简单的英语写的关于该主题的东西的方向),以及您将如何使用它?

4

6 回答 6

58

由于当前的答案很详细,而您的问题更针对一般概述,我会试一试:

PDO 类旨在封装与数据库交互所需的所有功能。他们通过定义“方法”(函数的OO客厅)和“属性”(变量的OO客厅)来做到这一点。您可以将它们用作您现在用于与数据库通信的所有“标准”功能的完全替代品。

因此,与其调用一系列 'mysql_doSomething()' 函数,将它们的结果存储在您自己的变量中,不如从 PDO 类中“实例化”一个对象('class' = 抽象定义,'object' = 具体的、可用的实例类)并调用该对象上的方法来做同样的事情。

例如,如果没有 PDO,您将执行以下操作:

// 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;
}

虽然这将是使用 PDO 的等价物:

// 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();

因此,乍一看,除了语法之外,没有太大区别。但是 PDO 版本有一些优点,最大的一个是数据库独立性:

如果您需要与 PostgreSQL 数据库通信,则只需在实例化调用中更改mysql:为。使用旧方法,您必须检查所有代码,将所有 'mysql_doSomething()' 函数替换为对应的 'pg_doSomthing()' 函数(始终检查参数处理中的潜在差异)。许多其他受支持的数据库引擎也是如此。pgsql:new PDO()

因此,回到您的问题,PDO 基本上只是为您提供了实现相同目标的不同方式,同时提供了一些捷径/改进/优势。例如,转义将以您正在使用的数据库引擎所需的正确方式自动发生。参数替换(防止 SQL 注入,未在示例中显示)也更容易,使其不易出错。

您应该阅读一些 OOP 基础知识以了解其他优势。

于 2009-11-16T14:51:35.040 回答
40

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

一个查询有多个部分

可以这样想:当您向数据库发出查询时,您是在告诉它几件不同的事情。例如,一件事可能是“我希望你做一个选择”。另一个可能是“将其限制为用户名是以下值的行”。

如果您将查询构建为字符串并将其传递给数据库,那么它在获得完整的字符串之前不知道任何一部分。你可以这样做:

'SELECT * FROM transactions WHERE username=$username'

当它得到那个字符串时,它必须解析它并决定“这是一个SELECT带一个WHERE”。

把零件弄混

假设一个恶意用户输入他们的用户名billysmith OR 1=1。如果您不小心,可能会将其放入字符串中,从而导致:

'SELECT * FROM transactions WHERE username=billysmith OR 1=1'

...这将返回所有用户的所有交易,因为 1 始终等于 1。哎呀,你被黑了!

看看发生了什么?数据库不知道您的查询中会出现哪些部分,所以它只是解析了字符串。WHERE有一个OR, 有两个条件可以满足它并不奇怪。

保持零件笔直

如果它知道会发生什么,即只有一个条件,恶意用户就不可能欺骗它SELECTWHERE

通过准备好的陈述,您可以给它正确的期望。您可以告诉数据库“我要向您发送一个SELECT,并且它将被限制为行WHERE username =,我将要给您一个字符串。仅此而已 - 查询没有其他部分。您准备好了吗? 好的,这里是要与用户名进行比较的字符串。”

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

准备好的报表的其他好处

除了安全优势之外,准备好的语句还有一些速度优势:

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

有关另一种解释,请参阅 Theo在此处的回答。

于 2009-11-16T13:34:35.820 回答
16

与 mysql_real_escape_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::PARAM_INT)。

其次,对我来说,PDO 参数化查询更容易阅读。我宁愿读:

SELECT name FROM user WHERE id = ? AND admin = ? 

SELECT name FROM user WHERE id = mysql_real_escape_string($id) AND admin = mysql_real_escape_string($admin);

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

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

对比

SELECT * FROM user WHERE name = ?

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

于 2009-11-16T14:20:59.640 回答
14

想象一下你写的东西是这样的:

$query = 'SELECT * FROM table WHERE id = ' . mysql_real_escape_string($id);

这不会使您免于注射,因为 $id 可能是1 OR 1=1并且您将从表中获取所有记录。您必须将 $id 转换为正确的数据类型(在这种情况下为 int )

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

于 2009-11-16T13:15:01.553 回答
3

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

于 2009-11-16T13:25:23.263 回答
3

为什么 PDO 比 mysql_real_escape_string 更适合转义 MySQL 查询/查询字符串?

仅仅因为“逃跑”是没有意义的。
而且,是不同的无比的事情。

逃避的唯一问题是每个人都认为它是错误的,认为它是某种“保护”。
每个人都说“我逃避了我的变量”,意思是“我保护了我的查询”。单独逃跑与保护完全无关

如果我转义并引用了我的数据,可以大致实现保护,但它并不适用于所有地方,例如标识符(顺便说一下,还有 PDO)。

所以,答案是:

  • PDO 在对绑定值进行转义时,不仅适用于转义,还适用于引用 - 这就是它更好的原因。
  • “逃避”不是“保护”的同义词。“转义+引用”大致是。
  • 但对于某些查询部分,这两种方法都不适用。
于 2012-04-12T13:29:48.670 回答