详解PHP中的PDO类


Posted in PHP onJuly 06, 2015

 简介

咱一起来看看PDO类。PDO是PHP Data Objects的缩写,它被描述为“在PHP中访问数据库的轻量级,兼容性的接口”。尽管它的名字不咋好听,但PDO是一个在PHP中访问数据库会让人喜爱的方式。
与MySQLi的不同

MySQLi和PDO很相似,都有两方面主要区别:

1.MySQLi只能访问MySQL,但PDO可以访问12种不同的数据库

2.PDO没有普通函数调用(mysqli_*functions)
开始步骤

首先,你得确定一下你的PHP是否安装了PDO插件。你可以用$test=new PDO()的结果来测试。如果提示说是参数不匹配,那证明已经安装了PDO插件,如果说是对象不存在,你得先确认一下在pho.ini中php_pdo_yourssqlserverhere.extis是否被注释掉了。如果没有这句话,那你得安装PDO了,这里就不??铝恕?/p>

连接

现在我们确认服务器已经工作,开始连接数据库吧:
 

$dsn = 'mysql:dbname=demo;host=localhost;port=3306';
$username = 'root';
$password = 'password_here';
try {
 $db = new PDO($dsn, $username, $password); // also allows an extra parameter of configuration
} catch(PDOException $e) {
 die('Could not connect to the database:<br/>' . $e);
}

除$dsn之外,所有语句和变量都能自我解释。DSN指的是数据源名称,有多种输入类型。最常见的一种是我们刚刚用的这种,PHP官网解释了 其他可用的DSN 。

你可以省去DSN的其他额外参数,只需要在数据库驱动后面带个冒号,比如(mysql:)。在这种情况下PDO将会尝试连接到本地数据库。就像当你使用MySQLi时需要在查询中指定数据库名称一样。

最后一件你需要注意的事情就是我们用try-catch块包裹了我们的初始化对象。PDO连接失败的时候将会抛出PDOException异常而不是查询失败的时候。如果你愿意你可以使用下面代码$db=line来选择异常的模式。
 

$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

或者你可以直接在PDO初始化时传递参数:
 

$db = new PDO($dsn, $username, $password, array (
 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
));

我们现在使用的是错误的方式——在失败时候简单返回false,我们是没有理由不对异常进行处理的。

基本查询

在PDO中使用query和exec两种方法使得对数据库查询变得非常简单。如果你想得到查询结果的行数exec是非常好用的,因此对SELECT查询语句是非常有用的。

现在我们通过下面的一个例子查看这两种方法:
 

$statement = <<<SQL
 SELECT *
 FROM `foods`
 WHERE `healthy` = 0
SQL;
 
$foods = $db->query($statement);

假设我们的查询是正确的,$foods现在是一个PDO Statement对象,我们可以用它获取到我们的结果或者是查看这次查询中一共查到多少条结果集。
行数

不足的是,PDO没有提供一个统一的方法去计算返回的行数。PDO Statement包含了一个叫做rowCount的方法,但是这个方法却不能保证在每一个SQL驱动中起作用(幸运的是,它能够在Mysql数据库中起作用)。

如果你的SQL驱动不支持这个方法,你也有2个选择:使用二次查询(SELECT COUNT(*))或者使用简单的count($foods)得到行数。

幸运的是对我们的MySQL例子,我们可以使用下面的简单方法来输出正确的行数。
 

echo $foods->rowCount();

遍历结果集

打印出这些可口的食物一点也不困难:
 

foreach($foods->FetchAll() as $food) {
 echo $food['name'] . '<br />';
}

唯一需要注意的是PDO也支持Fetch方法,这个方法只会返回第一条结果,这对于只需要查询一条结果集是非常有用的。
转义用户输入(的特殊字符)

你可曾听说过(mysqli_)real_escape_string,这是用于确保用户输入安全数据。PDO提供了一个方法叫做quote,这个方法可以把输入字符串中带有引号的地方进行特殊字符转义。
 

$input: this is's' a '''pretty dange'rous str'ing

在转义后,最终得到下面结果:
 

$db->quote($input): 'this is\'s\' a \'\'\'pretty dange\'rous str\'ing'
exec()

正如上面提到的,你可以使用exec()方法实现UPDATE,DELETE和INSERT 操作,执行后它会返回受影响行数的数量:
 

$statement = <<<SQL
 DELETE FROM `foods`
 WHERE `healthy` = 1;
SQL;
 
echo $db->exec($statement); // outputs number of deleted rows

预处理语句

尽管exec方法和查询在PHP中仍然被大量使用和支持,但是PHP官网上还是要求大家用预处理语句的方式来替代。为什么呢?主要是因为:它更安全。预处理语句不会直接在实际查询中插入参数,这就避免了许多潜在的SQL注入。

然而出于某种原因,PDO实际上并没有真正的使用预处理,它是在模拟预处理方式,在将语句传给SQL服务器之前会把参数数据插入到语句中,这使得某些系统容易受到SQL注入。

如果你的SQL服务器不真正的支持预处理,我们可以很容易的通过如下方式在PDO初始化时传参来修复这个问题:
 

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

接下来开始我们的第一个预处理语句吧:
 

$statement = $db->prepare('SELECT * FROM foods WHERE `name`=? AND `healthy`=?');
$statement2 = $db->prepare('SELECT * FROM foods WHERE `name`=:name AND `healthy`=:healthy)';

正如你所见,有两种创建参数的方法,命名的与匿名的(不可以同时出现在一个语句中)。然后你可以使用bindValue来敲进你的输入:
 

$statement->bindValue(1, 'Cake');
$statement->bindValue(2, true);
 
$statement2->bindValue(':name', 'Pie');
$statement2->bindValue(':healthy', false);

注意使用命名参数的时候你要包含进冒号(:)。PDO还有一个bindParam方法,可以通过引用绑定数值,也就是说它只在语句执行的时候查找相应数值。

现在剩下的唯一要做的事情,就是执行我们的语句:
 

$statement->execute();
$statement2->execute();
 
// Get our results:
$cake = $statement->Fetch();
$pie = $statement2->Fetch();

为了避免只使用bindValue带来的代码碎片,你可以用数组给execute方法作为参数,像这样:
 

$statement->execute(array(1 => 'Cake', 2 => true));
$statement2->execute(array(':name' => 'Pie', ':healthy' => false));

事务

前面我们已经描述过了什么是事务:

一个事务就是执行一组查询,但是并不保存他们的影响到数据库中。这样做的好处是如果你执行了4条相互依赖的插入语句,当有一条失败后,你可以回滚使得其他的数据不能够插入到数据库中,确保相互依赖的字段能够正确的插入。你需要确保你使用的数据库引擎支持事务。
开启事务

你可以很简单的使用beginTransaction()方法开启一个事务:
 

$db->beginTransaction();
 
$db->inTransaction(); // true!

然后你可以继续执行你的数据库操作语句,在最后提交事务:
 

$db->commit();

还有类似MySQLi中的rollBack()方法,但是它并不是回滚所有的类型(例如在MySQL中使用DROP TABLE),这个方法并不是真正的可靠,我建议尽量避免依赖此方法。

其他有用的选项

有几个选项你可以考虑用一下。这些可以作为你的对象初始化时候的第四个参数输入。

$options = array($option1 => $value1, $option[..]);
$db = new PDO($dsn, $username, $password, $options);

PDO::ATTR_DEFAULT_FETCH_MODE

你可以选择PDO将返回的是什么类型的结果集,如PDO::FETCH_ASSOC,会允许你使用$result['column_name'],或者PDO::FETCH_OBJ,会返回一个匿名对象,以便你使用$result->column_name

你还可以将结果放入一个特定的类(模型),可以通过给每一个单独的查询设置一个读取模式,就像这样:
 

$query = $db->query('SELECT * FROM `foods`');
$foods = $query->fetchAll(PDO::FETCH_CLASS, 'Food');
PDO::ATTR_ERRMODE

- 所有读取模式

上面我们已经解释过这一条了,但喜欢TryCatch的人需要用到:PDO::ERRMODE_EXCEPTION。如果不论什么原因你想抛出PHP警告,就使用PDO::ERRMODE_WARNING。

PDO::ATTR_TIMEOUT

当你为载入时间而着急时,你可以使用此属性来为你的查询指定一个超时时间,单位是秒. 注意,如果超过你设置的时间,缺省会抛出E_WARNING异常, 除非 PDO::ATTR_ERRMODE 被改变.

更多属性信息可以在 PHP官网的属性设置 里查看到.
最后的思考

PDO是一个在PHP中访问你的数据库的很棒的方式,可以认为是最好的方式。除非你拒绝使用面向对象的方法或是太习惯 MySQLi 的方法名称,否则没有理由不使用PDO。

更好的是完全切换到只使用预处理语句,这最终将使你的生活更轻松!

PHP 相关文章推荐
Apache2 httpd.conf 中文版
Dec 06 PHP
PHP 数组实例说明
Aug 18 PHP
PHP计划任务、定时执行任务的实现代码
Apr 23 PHP
PHP根据传入参数合并多个JS和CSS文件的简单实现
Jun 13 PHP
修改ThinkPHP缓存为Memcache的方法
Jun 25 PHP
php获得客户端浏览器名称及版本的方法(基于ECShop函数)
Dec 23 PHP
Symfony生成二维码的方法
Feb 04 PHP
PHP页面输出时js设置input框的选中值
Sep 30 PHP
PHP 实现从数据库导出到.csv文件方法
Jul 06 PHP
一个实用的php验证码类
Jul 06 PHP
php计数排序算法的实现代码(附四个实例代码)
Mar 31 PHP
yii 框架实现按天,月,年,自定义时间段统计数据的方法分析
Apr 04 PHP
php中 $$str 中 &quot;$$&quot; 的详解
Jul 06 #PHP
PHP生成唯一订单号
Jul 05 #PHP
启用Csrf后POST数据时出现的400错误
Jul 05 #PHP
php超快高效率统计大文件行数
Jul 05 #PHP
PHP版QQ互联OAuth示例代码分享
Jul 05 #PHP
PHP 获取ip地址代码汇总
Jul 05 #PHP
PHP中$_SERVER使用说明
Jul 05 #PHP
You might like
关于mysql 字段的那个点为是定界符
2007/01/15 PHP
PHP+.htaccess实现全站静态HTML文件GZIP压缩传输(一)
2007/02/15 PHP
PHP array操作10个小技巧分享
2011/06/23 PHP
php入门学习知识点八 PHP中for循环基本应用之九九乘法口绝表
2011/07/14 PHP
PHP内核介绍及扩展开发指南―基础知识
2011/09/11 PHP
解析php 版获取重定向后的地址(代码)
2013/06/26 PHP
php使用PDO下exec()函数查询执行后受影响行数的方法
2017/03/28 PHP
PHP获取远程http或ftp文件的md5值的方法
2019/04/15 PHP
Javascript图像处理—虚拟边缘介绍及使用方法
2012/12/27 Javascript
JS 有趣的eval优化输入验证实例代码
2013/09/22 Javascript
IE浏览器中图片onload事件无效的解决方法
2014/04/29 Javascript
jQuery简单几行代码实现tab切换
2015/03/10 Javascript
jQuery 判断图片是否加载完成方法汇总
2015/08/10 Javascript
jQuery查找节点方法完整实例
2016/09/13 Javascript
原生js实现可拖拽效果
2017/02/28 Javascript
JQueryMiniUI按照时间进行查询的实现方法
2017/06/07 jQuery
jQuery用户头像裁剪插件cropbox.js使用详解
2017/06/07 jQuery
微信小程序switch开关选择器使用详解
2018/01/31 Javascript
简单了解node npm cnpm的具体使用方法
2019/02/27 Javascript
[48:44]2014 DOTA2国际邀请赛中国区预选赛5.21 TongFu VS HGT
2014/05/22 DOTA
pyqt4教程之messagebox使用示例分享
2014/03/07 Python
一个Python最简单的接口自动化框架
2018/01/02 Python
Python+unittest+DDT实现数据驱动测试
2020/11/30 Python
CSS伪类与CSS伪元素的区别及由来具体说明
2012/12/07 HTML / CSS
老海军美国官网:Old Navy
2016/09/05 全球购物
美国最大的家庭鞋类零售商之一:Shoe Carnival
2017/10/06 全球购物
澳大利亚领先的折扣药房:Chemist Direct(有中文站)
2018/11/24 全球购物
护理专科毕业推荐信
2013/11/10 职场文书
满月酒主持词
2014/03/27 职场文书
2014年大班保育员工作总结
2014/12/02 职场文书
实习单位推荐信
2015/03/27 职场文书
同学聚会感言一句话
2015/07/30 职场文书
导游词之西湖雷峰塔
2019/09/18 职场文书
导游词之五台山
2019/10/11 职场文书
css height属性中的calc方法详解
2021/06/03 HTML / CSS
详解Spring Boot使用系统参数表提升系统的灵活性
2021/06/30 Java/Android