避免了原始的MySQL扩展(二)

在本系列的第一部分中,我们看到了什么是错误的与原来的MySQL API,以及我们如何能够迁移到新的,功能丰富的MySQLi的API。在第二部分中,我们将探讨的的PDO扩展到发现它提供的功能。

替代#2:PDO – PHP数据对象

PDO扩展支持12个驱动器,它连接到一个不同的关系型数据库,开发人员不需要学习一些不同的API。它通过抽象出数据库的交互调用后面一个共同的接口,使开发人员能够利用一个一致的接口,对于不同的数据库。当然,这给它的MySQL“和”MySQLi扩展仅限于只有一个数据库的一个主要优势。

PDO基础

连接到一个数据库中,通过PDO是一个快速简便的PDO的构造函数的参数传递过程中所需的详细信息。然后,我们访问对象的公共方法和属性来发送查询和检索数据。

01<?php
02$dsn 'mysql:host=localhost;dbname=database_name';
03$username 'user';
04$password 'password';
05 
06$db new PDO($dsn$username$password);
07 
08$numRows $db->exec("INSERT INTO table VALUES ('val1', 'val2', 'val3')");
09 
10$result $db->query('SELECT col1, col2, col3 FROM table', PDO::FETCH_ASSOC);
11foreach ($result as $row) {
12    echo "{$row['col1']} - {$row['col2']} - {$row['col3']}\n";
13}
14 
15$result->closeCursor();
16 
17unset($db);

首先连接到数据库,DSN,用户名和密码作为参数传递到PDO类。DSN是一个连接字符串,从数据库到数据库中,但通常包含的数据库驱动程序的名称,名称,主机,有时的端口号。

两个基本的查询,然后执行在本例中,首先使用exec()的方法和第二使用的查询()方法。这两种方法之间的重要区别是成功的返回值,应使用exec()的非选择语句(INSERT,UPDATE或DELETE),并返回受影响的行数,应使用选择语句查询()成功后,并返回一个结果集对象。他们都返回false,如果有一个失败。

我们可以操作的方式,返回的数据是通过查询()的第二个参数默认值是PDO :: FETCH_BOTH返回复制的数据在一个阵列中的每一行,一个是联想的关键是数据的列名,列值的价值(PDO :: FETCH_ASSOC),另一个将是一个整数索引的数组(PDO :: FETCH_NUM)。因为这是通常并不需要,它是建议你指定一个合适的抓取模式,以节省资源。

然后,我们调用清理的结果对象内所使用的存储资源,一旦他们并不需要任何更多closeCursor()方法此外,我们取消设置我们的PDO对象,以释放使用的任何资源时,我们知道,该脚本将不再需要任何与数据库进行交互。

特点PDO

喜欢的MySQLi的API,一些功能被引入的PDO扩展,开发人员可以利用。现在,我们已经介绍了使用PDO的绝对基础知识,我们就可以着手寻找一些功能,包括编制报表,交易,改变PDO的默认行为,抽象的后果。

准备的语句

与MySQLi的一样,,PDO还支持准备报表,因此我们将参数绑定到我们的查询,以防止注入攻击对数据库。准备好的语句也使客户端和服务器端的缓存,从而加快了准备好的查询执行时间需要不同的值绑定到它的执行。,PDO然而,有一对夫妇MySQLi的参数化查询,是值得研究的优势。

第一个优势是,PDO支持命名参数的使用,使我们有能力给他们一个有意义的名字,在我们的查询,以确定占位符。这可以帮助我们跟踪的参数需要被绑定到一个查询时,有大量参与,而不是有序列的命名(或位置)的占位符使用问号,这是取决于顺序装订。

第二个优点是使用bindValue()方法,我们有更大的灵活性,我们准备好的查询绑定值这使我们能够绕过的限制,只能够绑定变量,然后被评估时调用的execute()方法,与库MySQLi的bind_param得到()方法。

让我们来仔细看看如何,我们可以明确地绑定变量和值通过一个例子来给我们准备的查询。

01<?php
02$insQuery $db->prepare('INSERT INTO table VALUES (:col1, :col2, :col3)');
03$insQuery->bindParam('col1'$val1, PDO::PARAM_INT);
04$insQuery->bindParam('col2'$val2, PDO::PARAM_STR);
05$insQuery->bindParam('col3'$val3, PDO::PARAM_INT);
06$insQuery->execute();
07 
08$selQuery $db->prepare('SELECT col2, col3, FROM table WHERE col1 LIKE :val');
09$selQuery->bindValue('val'"%{$val}%", PDO::PARAM_STR);
10$result $selQuery->execute();
11 
12while ($row $result->fetch(PDO::FETCH_ASSOC)) {
13    echo "{$row['col2']} - {$row['col3']}\n";
14}

命名占位符开始用冒号,然后在PHP中的变量使用相同的命名约定。将参数绑定到准备好的查询时,我们必须提供bindParam()bindValue()方法至少有两个参数,一个可选的第三个参数。

第一个参数是占位符(这是区分大小写)的名称,第二个参数是我们要绑定到查询中的变量或值,并且可选的第三个参数是类型绑定变量/值(默认情况下是PDO :: PARAM_STR,但我始终指定清晰的类型)。bindParam()方法,也使我们能够指定一个可选的第四个和第五个参数,数据类型,长度和任何额外的驱动程序选项。

bindParam()bindValue()方法是正交的,所以其中一个或两个时,可以使用绑定值到一个查询。这是一个不与命名和未命名的占位符的情况下,然而,其中只有一个或其它的可用于在一个单一的准备好的查询。

隐式绑定是我们放弃使用bindParam()bindValue()方法,而通过在一个数组格式的execute()方法的参数绑定如果正在使用指定的占位符,然后将需要一个关联数组被传递给execute()方法 ;位置占位符的使用,那么索引数组可以被传递给execute()方法

下面是我们如何能够在短期格式的参数化查询使用未命名占位符:

1<?php
2$updQuery $db->prepare('UPDATE table SET col1 = ?, col2 = ? WHERE col3 = ?');
3$updQuery->execute(array('val1, 'val2', 'val3'));
4 
5if ($updQuery->rowCount() !== 0) {
6    echo 'Success';
7}

我们开始准备我们的查询,并把未命名的占位符的位置,然后调用一个数组,包含的值绑定到我们准备好的查询的execute()方法。这个阵列被通过可以包含(或两者)的变量和字符串。这短暂的手工方法的缺点是,我们无法指定类型的参数被绑定到我们准备好的查询。接下来我们的问题,如果使用的行数()方法,其中将包含从以前的操作影响的行数的返回值进行了更新任何行提供的行数不为零,那么我们就认为它是成功的。

最后一种类型的绑定,我们来看看准备好的查询是不是出于安全目的,而是数据获取。这是我们的bindColumn()方法使用的变量绑定列名bindColumn()bindParam() /bindValue()方法可以用于在一个准备好的查询,给我们的灵活性,通过直接分配结果的变量获取数据,同时注入攻击免疫。

01<?php
02$preQuery $db->prepare('SELECT col2, col3 FROM table WHERE col1 = :val');
03$preQuery->bindParam('val'$value, PDO::PARAM_STR);
04$preQuery->execute();
05 
06$preQuery->bindColumn('col2'$OUTcol2);
07$preQuery->bindColumn('col3'$OUTcol3);
08 
09while ($result $preQuery->fetch(PDO::FETCH_BOUND)) {
10    echo "{$OUTcol2} - {$OUTcol3}\n";
11}

首先,我们准备和值绑定到我们的查询。这是执行,我们调用bindColumn()方法的第一个参数是列名(可以指定数值),而第二个参数是变量绑定列的值。对于这一点,我用我自己的命名约定,以帮助区分我创建的变量(与已知的安全值),从那些含有值从数据库中。这有助于我知道哪些可能含有被污染的数据,因此将需要进行转义后输出,以防止XSS攻击。

然后,我们通过循环获取的数据行的行,取(PDO :: FETCH_BOUND的方法在我们的结果集的变量被绑定到上述指定的列的值($ OUTcol2OUTcol3 $) 。虽然价值$结果仍然是正确的(遍历仍然有行),然后循环将继续执行。

MySQLi的API也提供了相同的功能(与上述类似的语法)使用bind_result()方法。

交易

PDO还支持事务,但是他们在一个稍微不同的方式创建的mysqli API。正如在前面的文章中,交易工作时的ACID属性,用于保持数据的完整性(一个 CID)在多个关系数据库的表内。如果一个语句的失败,在执行过程中,我们都能够以滚动回所有的报表的影响之前的改变是永久的承诺(一个ÇID),这是由于的隔离性质的每笔交易(AC  ð)之前被认为是成功的。这是特别重要的,当报表依赖于一个别人的成功,所以他们必须要么全部成功,要么全部失败(ACI ð)。

我们可以再次看到交易的形式进行重复的插入表具有独特的的约束键设置后,其中一列后:

01<?php
02$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
03 
04try
05{
06    $db->beginTransaction();
07 
08    $db->exec("INSERT INTO table VALUES (NULL, 'col1', 'col2')");
09    $db->exec("INSERT INTO table VALUES (NULL, 'col1', 'col2')");
10 
11    $db->commit();
12}
13catch(PDOException $e) {
14    $db->rollBack();
15}

首先,我们必须将抛出异常时,他们造成的错误处理行为。这是因为PDO的默认错误处理设置不会触发异常,这将意味着我们的catch块将永远不会被执行。

要开始一个事务,我们调用beginTransaction()方法,然后试图执行两个破坏性的查询(在我们的数据库中的数据将被永久使用INSERT / UPDATE / DELETE语句修改)。然后,我们调用了commit()方法时,尝试提交事务,然后返回查询处理返回到自动提交。以上当然会违反数据完整性规则在桌子上,造成的catch块执行的事务被回滚。这意味着,没有新的数据将被插入到数据库中。

操作默认的行为

更改PDO的默认行为可以通过构造方法的类的实例化过程中,或在预先存在的对象后创建的类的实例。通过使用PDO的构造方法,我们可以改变任何数量的设置,通过他们的阵列格式的第四个参数(可选)。想要操纵一个预先存在的对象的行为时,我们可以使用setAttribute()的方法。

1<?php
2$dsn 'mysql:host=localhost;dbname=database_name';
3$username 'user';
4$password 'password';
5$options array(PDO::ATTR_PERSISTENT => TRUE,
6                 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
7$pdo new PDO($dsn$username$password$options);

上面的代码将启用持久连接和更改默认的获取模式从FETCH_BOTHFETCH_ASSOC的持久性连接设置,但在少数的设置之前必须更改对象的创建,以便采取影响。另一方面的默认抓取行为对一个预先存在的对象时是可变的,使在任何时间改变其设置的执行过程中的一个脚本。

可变设置后,对象的setAttribute()方法,该对象的默认行为是其使用,其余的更新。我们也可以临时更改一些设置过程中的一些操作方法调用,这样的一个例子是准备()方法,这使我们能够准备查询(作为一个可选的第二个参数)指定额外的驱动程序选项时。这些加在一起为我们提供了额外的灵活性,可自由调整我们的PDO对象在整个脚本的行为。

希望我们的PDO对象作出反应,脚本执行过程中的错误,改变错误处理设置是一种常见的发生。这可以通过构造函数或setAttribute()的方法,有三种模式:SILENT(默认),警告,和EXCEPTION虽然我们总是能够查看错误信息,使用的errorCode()的errorinfo()方法,错误报告设置使我们能够选择,如果遇到的错误是:完全沉默(PDO :: ERRMODE_SILENT)的,如果这是一个警告(PDO :: ERRMODE_WARNING),或总是抛出(PDO :: ERRMODE_EXCEPTION)。

抽象后果

然而,有一个PDO抽象层,提供一些缺点。其中之一是每个数据库之间的兼容性问题,其中PDO必须尝试使用​​的功能,可对所有数据库。这方面的一个例子,可以看出PDO_MYSQL驱动程序,查询的默认设置为无缓冲由于它不支持其他驱动程序。这意味着,这个抽象层的最佳用量,可能需要单独的设置要改变切换时,从数据库到数据库。

什么样的方法,我们选择实施使用PDO时,我们也必须小心。虽然大部分的功能PDO提供通过它的方法已被标准化工作在所有支持的数据库,但仍有不完全移植到了,因为他们根本不支持所有的数据库的方法。其中之一是引号()方法,它不工作,的PDO_ODBC的驱动程序。通用的方法应该总是使用,以实现便携性。

进一步的警告,要提防它写了不兼容的SQL代码。这是因为PDO,虽然它有能力使用多个数据库,是不是查询的抽象层。编写依赖于数据库的SQL代码的一个典型的例子是,当使用反引号字符,可以支持你的MySQL数据库,但没有PDO可以与其他数据库。其他数据库将有自己的定义,为逃避无效的表名和列名,如PostgreSQL和Oracle使用双引号,或Microsoft SQL Server使用方括号。

收评

在这篇文章中,我们讨论了基本的PDO和操纵它的默认行为,以及与探索预处理语句进行消毒输入(显式和隐式绑定),同时展示他们的另一种用法。我们也期待在创造交易及其各自的行为(通过ACID属性)。但仍然有许多额外的功能,PDO,我们并没有在本文中讨论的机会。不要忘记检查出PHP.net手册更多信息PDO

我们结束了两部分的系列文章的原因,我们应该避免的MySQL API,以及我们如何能避免它通过引入两个备选方案。我希望我们现在可以更好地摆脱原来的MySQL API赞成或MySQLi的,比较突出的PDO扩展!

本文固定链接: http://www.wangxionghui.com/201302965.html | 武汉SEO|湖北网站建设

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值