详解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 分库分表hash算法
Nov 12 PHP
php初始化对象和析构函数的简单实例
Mar 11 PHP
PHP使用Mysql事务实例解析
Sep 08 PHP
php实现session自定义会话处理器的方法
Jan 27 PHP
php curl抓取网页的介绍和推广及使用CURL抓取淘宝页面集成方法
Nov 30 PHP
使用PHP+AJAX让WordPress动态加载文章的教程
Dec 11 PHP
PHP实现对二维数组某个键排序的方法
Sep 14 PHP
PHP大神的十大优良习惯
Sep 14 PHP
php实现文件与16进制相互转换的方法示例
Feb 16 PHP
微信公众号开发之获取位置信息php代码
Jun 13 PHP
解决laravel5中auth用户登录其他页面获取不到登录信息的问题
Oct 08 PHP
thinkphp5实现微信扫码支付
Dec 23 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
MyEclipse常用配置图文教程
2014/09/11 PHP
CI框架文件上传类及图像处理类用法分析
2016/05/18 PHP
PHP正则删除HTML代码中宽高样式的方法
2017/06/12 PHP
javascript jQuery $.post $.ajax用法
2008/07/09 Javascript
文本框中,回车键触发事件的js代码[多浏览器兼容]
2010/06/07 Javascript
jquery.autocomplete修改实现键盘上下键自动填充示例
2013/11/19 Javascript
IE6中链接A的href为javascript协议时不在当前页面跳转
2014/06/05 Javascript
Web打印解决方案之普通报表打印功能
2016/08/29 Javascript
JS敏感词过滤代码
2016/12/23 Javascript
Vue 父子组件、组件间通信
2017/03/08 Javascript
JS与jQuery实现子窗口获取父窗口元素值的方法
2017/04/17 jQuery
JS中将多个逗号替换为一个逗号的实现代码
2017/06/23 Javascript
jqueryUI tab标签页代码分享
2017/10/09 jQuery
jQuery封装animate.css的实例
2018/01/04 jQuery
JS匿名函数内部this指向问题详析
2019/05/10 Javascript
OpenLayer学习之自定义测量控件
2020/09/28 Javascript
Python算法之图的遍历
2017/11/16 Python
Python中利用aiohttp制作异步爬虫及简单应用
2018/11/29 Python
关于Tensorflow分布式并行策略
2020/02/03 Python
基于Python计算圆周率pi代码实例
2020/03/25 Python
python实现音乐播放和下载小程序功能
2020/04/26 Python
Python LMDB库的使用示例
2021/02/14 Python
英国鲜花速递:Serenata Flowers
2018/04/03 全球购物
戴尔英国翻新电脑和电子产品:Dell UK Refurbished Computers
2019/07/30 全球购物
荷兰音乐会和音乐剧门票订购网站:Topticketshop
2019/08/27 全球购物
简述数组与指针的区别
2014/01/02 面试题
工厂厂长岗位职责
2013/11/08 职场文书
5.12护士节演讲稿
2014/04/30 职场文书
四风问题专项整治工作情况报告
2014/10/28 职场文书
公务员个人考察材料
2014/12/23 职场文书
信用卡工作证明范本
2015/06/19 职场文书
2016高一新生军训心得体会
2016/01/11 职场文书
2019年房屋委托租赁合同范本(通用版)!
2019/07/17 职场文书
浅谈JS的原型和原型链
2021/06/04 Javascript
python3操作redis实现List列表实例
2021/08/04 Python
CSS实现鼠标悬浮动画特效
2023/05/07 HTML / CSS