详解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 Session变量不能传送到下一页的解决方法
Nov 27 PHP
shopex中集成的站长统计功能的代码简单分析
Aug 11 PHP
探讨Smarty中如何获取数组的长度以及smarty调用php函数的详解
Jun 20 PHP
CodeIgniter实现更改view文件夹路径的方法
Jul 04 PHP
PHP之uniqid()函数用法
Nov 03 PHP
php中使用session防止用户非法登录后台的方法
Jan 27 PHP
PHP中使用curl伪造IP的简单方法
Aug 07 PHP
WordPress中缩略图的使用以及相关技巧
Nov 24 PHP
PHP安全下载文件的方法
Apr 07 PHP
php实现给二维数组中所有一维数组添加值的方法
Feb 04 PHP
Yii2第三方类库插件Imagine的安装和使用
Jul 06 PHP
PHP实现的多维数组去重操作示例
Jul 21 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中理解print EOT分界符和echo EOT的用法区别小结
2010/02/21 PHP
php操作mysqli(示例代码)
2013/10/28 PHP
php循环table实现一行两列显示的方法
2015/06/04 PHP
js保存当前路径(cookies记录)
2010/12/14 Javascript
Javascript学习笔记一 之 数据类型
2010/12/15 Javascript
超酷的网页音乐播放器DewPlayer使用方法
2010/12/18 Javascript
JavaScript返回当前会话cookie全部键值对照的方法
2015/04/03 Javascript
JavaScript中的parse()方法使用简介
2015/06/12 Javascript
AngularJS入门教程之Cookies读写操作示例
2016/11/02 Javascript
angularjs实现柱状图动态加载的示例
2017/12/11 Javascript
vue中Npm run build 根据环境传递参数方法来打包不同域名
2018/03/29 Javascript
vue-cli项目优化方法- 缩短首屏加载时间
2018/04/01 Javascript
jQuery中的$是什么意思及 $. 和 $().的区别
2018/04/20 jQuery
详解Vue组件插槽的使用以及调用组件内的方法
2018/11/13 Javascript
JS事件流与事件处理程序实例分析
2019/08/16 Javascript
jstree中的checkbox默认选中和隐藏示例代码
2019/12/29 Javascript
vue缓存之keep-alive的理解和应用详解
2020/11/02 Javascript
python的tkinter布局之简单的聊天窗口实现方法
2014/09/03 Python
python中将函数赋值给变量时需要注意的一些问题
2017/08/18 Python
Python判断文件和字符串编码类型的实例
2017/12/21 Python
pandas series序列转化为星期几的实例
2018/04/11 Python
python爬虫 使用真实浏览器打开网页的两种方法总结
2018/04/21 Python
使用PIL(Python-Imaging)反转图像的颜色方法
2019/01/24 Python
Python docx库用法示例分析
2019/02/16 Python
python 实现交换两个列表元素的位置示例
2019/06/26 Python
python+jinja2实现接口数据批量生成工具
2019/08/28 Python
基于Python实现签到脚本过程解析
2019/10/25 Python
selenium中get_cookies()和add_cookie()的用法详解
2020/01/06 Python
解决Pycharm 中遇到Unresolved reference 'sklearn'的问题
2020/07/13 Python
Python绘图之二维图与三维图详解
2020/08/04 Python
python中append函数用法讲解
2020/12/11 Python
美国最大的网络男装服装品牌:Bonobos
2017/05/25 全球购物
小学后勤管理制度
2014/01/14 职场文书
信息专业大学生自我评价分享
2014/01/17 职场文书
公司廉洁自律承诺书
2014/03/27 职场文书
电子专业自荐信
2014/07/01 职场文书