详解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 相关文章推荐
php 代码优化的42条建议 推荐
Sep 25 PHP
PHP extract 将数组拆分成多个变量的函数
Jun 30 PHP
自己在做项目过程中学到的PHP知识收集
Aug 20 PHP
php setcookie(name, value, expires, path, domain, secure) 参数详解
Jun 28 PHP
php上传文件中文文件名乱码的解决方法
Nov 01 PHP
PHP批量生成静态HTML的简单原理和方法
Apr 20 PHP
PHP面向对象程序设计之类常量用法实例
Aug 20 PHP
PHP字符串比较函数strcmp()和strcasecmp()使用总结
Nov 19 PHP
PHP基于DOMDocument解析和生成xml的方法分析
Jul 17 PHP
PHP5.6读写excel表格文件操作示例
Feb 26 PHP
php常用经典函数集锦【数组、字符串、栈、队列、排序等】
Aug 23 PHP
laravel请求参数校验方法
Oct 10 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
PHP实现绘制3D扇形统计图及图片缩放实例
2014/10/01 PHP
php使用COPY函数更新配置文件的方法
2015/06/18 PHP
round robin权重轮循算法php实现代码
2016/05/28 PHP
动态加载js的几种方法
2006/10/23 Javascript
javascript 打开页面window.location和window.open的区别
2010/03/17 Javascript
基于jquery的复制网页内容到WORD的实现代码
2011/02/16 Javascript
通过action传过来的值在option获取进行验证的方法
2013/11/14 Javascript
jQuery(js)获取文字宽度(显示长度)示例代码
2013/12/31 Javascript
使用JSON.parse将json字符串转换成json对象的时候会出错
2014/09/04 Javascript
jquery获取select选中值的方法分析
2015/12/22 Javascript
BootStrap下jQuery自动完成的样式调整
2016/05/30 Javascript
js轮播图无缝滚动效果
2017/06/17 Javascript
Angularjs上传图片实例详解
2017/08/06 Javascript
nodejs超出最大的调用栈错误问题
2017/12/27 NodeJs
示例vue 的keep-alive缓存功能的实现
2018/12/13 Javascript
详解小程序毫秒级倒计时(适用于拼团秒杀功能)
2019/05/05 Javascript
Vue实现push数组并删除的例子
2019/11/01 Javascript
ES6常用小技巧总结【去重、交换、合并、反转、迭代、计算等】
2019/12/21 Javascript
微信小程序中的列表切换功能实例代码详解
2020/06/09 Javascript
[01:03:59]2018DOTA2亚洲邀请赛3月30日 小组赛B组VGJ.T VS Secret
2018/03/31 DOTA
[01:32]dota2拉比克至宝(222)
2018/12/20 DOTA
Python随机生成均匀分布在单位圆内的点代码示例
2017/11/13 Python
python 使用装饰器并记录log的示例代码
2019/07/12 Python
Python中turtle库的使用实例
2019/09/09 Python
python 调用API接口 获取和解析 Json数据
2020/09/28 Python
法国最大的在线眼镜店:EasyLunettes
2019/08/26 全球购物
波兰办公用品和学校用品在线商店:Dlabiura24.pl
2020/11/18 全球购物
大学生就业推荐信范文
2013/11/29 职场文书
自我评价个人范文
2013/12/16 职场文书
淘宝活动策划方案
2014/02/06 职场文书
法律六进活动方案
2014/03/13 职场文书
长城导游词400字
2015/01/30 职场文书
办公室禁烟通知
2015/04/23 职场文书
2015年学校减负工作总结
2015/05/19 职场文书
我的兄弟姐妹观后感
2015/06/15 职场文书
SpringBoot2零基础到精通之数据与页面响应
2022/03/22 Java/Android