
第10章 PDO数据库抽象层
(视频讲解:45分钟)
在PHP的早期版本中,各种不同的数据库扩展(MySQL、MS SQL、Oracle)根本没有真正的一致性,虽然都可以实现相同的功能,但是这些扩展却互不兼容,都有各自的操作函数,各自为政。结果导致PHP的维护非常困难,可移植性也非常差,为了解决这些问题,PHP的开发人员编写了一种轻型、便利的API来统一各种数据库的共性,从而达到PHP脚本最大程度的抽象性和兼容性,这就是数据库抽象层。而在本章中将要介绍的是目前PHP抽象层中最为流行的一种——PDO数据库抽象层。
学习摘要:
PDO概述
使用PDO连接数据库
PDO中执行SQL语句
PDO中获取结果集
捕获异常
10.1 什么是PDO

视频讲解
10.1.1 PDO概述
PDO是PHP Date Object(PHP数据对象)的简称,它是与PHP 5.1版本一起发行的,目前支持的数据库包括Firebird、FreeTDS、Interbase、MySQL、MS SQL Server、ODBC、Oracle、PostgreSQL、SQLite和Sybase。有了PDO,不必再使用mysql_*函数、oci_*函数或者mssql_*函数,也不必再为它们封装数据库操作类,只需要使用PDO接口中的方法就可以对数据库进行操作。在选择不同的数据库时,只需修改PDO的DSN(数据源名称)即可。
注意
从PHP 5.1开始附带了PDO,PDO需要PHP 5核心的新面向对象特性,因此不能在较早版本的PHP上运行。
10.1.2 PDO特点
PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与MySQL函数库相比,PDO让跨数据库的使用更具有亲和力;与ADODB和MDB2相比,PDO更高效。此外,PDO还具有以下特点:
PDO将通过一种轻型、清晰、方便的函数,统一各种不同RDBMS库的共有特性,实现PHP脚本最大程度的抽象性和兼容性。
PDO吸取现有数据库扩展成功和失败的经验教训,利用PHP 5的最新特性,可以轻松地与各种数据库进行交互。
PDO扩展是模块化的,能够在运行时为数据库后端加载驱动程序,而不必重新编译或重新安装整个PHP程序。例如,PDO_MySQL扩展会替代PDO扩展,实现MySQL数据库API。还有一些用于Oracle、PostgreSQL、ODBC和Firebird的驱动程序。
10.1.3 安装PDO
默认情况下,PDO在PHP 7中为开启状态,但是要启用对某个数据库驱动程序的支持,仍需要进行相应的配置操作。
在Windows环境下,PDO在php.ini文件中进行配置,如图10.1所示。

图10.1 Windows环境下配置PDO
要启用PDO,首先必须加载extension=php_pdo.dll,如果要想其支持某个具体的数据库,那么还要加载对应的数据库选项。例如,要支持MySQL数据库,则需要加载extension=php_pdo_mysql.dll选项。
注意
在完成数据库的加载后,要保存php.ini文件,并且重新启动Apache服务器,修改才能够生效。
10.2 PDO连接数据库

视频讲解
10.2.1 PDO构造函数
在PDO中,要建立与数据库的连接需要实例化PDO的构造函数,PDO构造函数的语法格式如下:

参数说明如下。
dsn:数据源名,包括主机名、端口号和数据库名称。
username:连接数据库的用户名。
password:连接数据库的密码。
driver_options:连接数据库的其他选项。
通过PDO连接MySQL数据库的代码如下:

10.2.2 DSN详解
DSN是Data Source Name(数据源名称)的首字母缩写。DSN提供连接数据库需要的信息。PDO的DSN包括3部分:PDO驱动名称(如mysql、sqlite或者pgsql)、冒号和驱动特定的语法。每种数据库都有其特定的驱动语法。
数据库服务器是完全独立于PHP的实体。数据库服务器可能与Web服务器不是在同一台计算机上,此时要通过PDO连接数据库时,就需要修改DSN中的主机名称。
每种数据库服务器都有一个默认的端口号(MySQL默认是3306),数据库管理员可以对端口号进行修改,因此有可能PHP找不到数据库的端口,此时就需要在DSN中包含端口号。
另外由于一个数据库服务器中可能拥有多个数据库,所以在通过DSN连接数据库时,通常都包括数据库名称,这样可以确保连接的是想要的数据库,而不是其他的数据库。
10.3 PDO中执行SQL语句

视频讲解
在PDO中,可以使用下面的3种方法来执行SQL语句。
1.exec()方法
exec()方法返回执行后受影响的行数,其语法格式如下:

参数$statement是要执行的SQL语句。该方法返回执行查询时受影响的行数,通常用于insert、delete和update语句中。
例如,使用exec()执行删除操作,删除member表中id为1的记录。代码如下:

2.query()方法
query()方法用于返回执行查询后的结果集。其语法格式如下:

其中参数$statement是要执行的SQL语句。它返回的是一个PDOStatement对象。
例如,使用query()执行查询操作,查询member表中所有记录的id。代码如下:

3.预处理语句——prepare()和execute()
预处理语句包括prepare()和execute()两个方法。首先,通过prepare()方法做查询的准备工作,语法格式如下:

其中参数$statement是要执行的SQL语句。它返回的是一个PDOStatement对象。
然后,通过execute()方法执行查询,并且还可以通过bindParam()方法来绑定参数提供给execute()方法。其语法格式如下:

例如,查询member表中id大于2且会员等级为C的所有记录。代码如下:

10.4 PDO中获取结果集

视频讲解
在PDO中获取结果集常用3种方法:fetch()、fetchAll()和fetchColumn()。
10.4.1 fetch()方法
fetch()方法可以获取结果集中的下一行记录,其语法格式如下:

参数说明如下。
fetch_style:控制结果集的返回方式,其可选方式如表10.1所示。
表10.1 fetch_style控制结果集的可选值

cursor_orientation:PDOStatement对象的一个滚动游标,可用于获取指定的一行。
cursor_offset:游标的偏移量。
【例10.01】 使用fetch()方法获取明日学院会员列表。(实例位置:资源包\源码\10\10.01)
通过fetch()方法获取结果集中下一行的数据,进而应用while语句完成数据库中数据的循环输出,具体步骤如下。
(1)创建member表。首先创建database10数据库,并设置数据库编码格式为UTF-8。创建member表的SQL语句如下:

(2)插入测试数据。为了显示会员信息,我们需要先在member表中插入几条测试数据。插入数据的SQL语句如下:

(3)创建数据库配置文件config.php,代码如下:

(4)创建index.php文件,用于连接数据库,执行查询语句,并引入模板文件。index.php文件的具体代码如下:

(5)创建lists.html文件,显示会员信息。使用$result->fetch(PDO::FETCH_ASSOC)将结果集返回到数组中,通过while语句循环遍历数组,将各会员的数据插入到<table>表格中,具体代码如下:

使用浏览器访问index.php,运行结果如图10.2所示。

图10.2 显示会员列表
10.4.2 fetchAll()方法
fetchAll()方法可以获取结果集中的所有行。其语法格式如下:

参数及返回值说明如下。
fetch_style:控制结果集中数据的显示方式。
column_index:字段的索引。
返回值:是一个包含结果集中所有数据的二维数组。
【例10.02】 使用fetchAll()方法获取明日学院会员列表。(实例位置:资源包\源码\10\10.02)
通过fetchAll()方法获取结果集中所有行,并且通过foreach语句读取二维数组中的数据,完成数据库中数据的循环输出。由于开发流程与例10.01相似,这里只介绍修改的关键代码。
(1)创建index.php文件,用于连接数据库,执行查询语句,并引入模板文件。使用fetchAll(PDO::FETCH_ASSOC)方法获取结果集中所有行,将结果赋值给$data数组。index.php文件修改后代码如下:

(2)创建lists.html文件。使用foreach语句遍历$data数组,将相应数据写入<table>表格,关键代码如下:

运行结果与例10.01相同。
10.4.3 fetchColumn()方法
fetchColumn()方法可以获取结果集中下一行指定列的值。其语法格式如下:

其中可选参数column_number设置行中列的索引值,该值从0开始。如果省略该参数则从第1列开始取值。
【例10.03】 使用fetchColumn()方法读取会员名。(实例位置:资源包\源码\10\10.03)
通过fetchColumn()方法获取结果集中下一行中指定列的值,注意这里是“指定列的值”。本实例输出member表中nickname的值。具体步骤如下。
(1)创建index.php文件,用于连接数据库,执行查询语句,并引入模板文件。index.php文件的具体代码如下:

(2)创建lists.html文件,使用fetchColumn()方法获取nickname。关键代码如下:

运行结果如图10.3所示。

图10.3 fetchColumn()方法获取用户昵称
10.5 PDO中捕获SQL语句中的错误

视频讲解
在PDO中有3种方法可以捕获SQL语句中的错误,分别为:默认模式(PDO::ERRMODE_SILENT)、警告模式(PDO::ERRMODE_WARNING)和异常模式(PDO::ERRMODE_EXCEPTION)。下面就对这3种方法分别进行讲解。
10.5.1 默认模式
在默认模式中设置PDOStatement对象的errorCode属性,但不进行其他任何操作。
通过prepare()和execute()方法向数据库中添加数据,设置PDOStatement对象的errorCode属性,手动检测代码中的错误。
【例10.04】 使用默认模式捕获SQL语句中的错误。(实例位置:资源包\源码\10\10.04)
创建index.php文件。通过PDO连接MySQL数据库,通过预处理语句prepare()和execute()执行insert添加语句,向数据表中添加数据,并且设置PDOStatement对象的errorCode属性,检测代码中的错误。代码如下:

在本实例中,在定义insert添加语句时,使用了错误的数据表名称members(正确名称是member),导致输出结果如图10.4所示。

图10.4 在默认模式中捕获SQL中的错误
10.5.2 警告模式
警告模式会产生一个PHP警告,并设置errorCode属性。如果设置的是警告模式,那么除非明确地检查错误代码,否则程序将继续按照其方式运行。
例如,设置警告模式,通过prepare()和execute()方法读取数据库中数据,体会在设置成警告模式后执行错误的SQL语句。具体代码如下:

在设置为警告模式后,如果SQL语句出现错误将给出一个提示信息,但是程序仍能够继续执行下去,其运行结果如图10.5所示。

图10.5 设置警告模式后捕获的SQL语句错误
10.5.3 异常模式
异常模式会创建一个PDOException,并设置errorCode属性。它可以将执行代码封装到一个try{…}catch{…}语句块中。未捕获的异常将会导致脚本中断,并显示堆栈跟踪,让我们了解是哪里出现的问题。
例如,在执行数据库中数据的删除操作时,设置为异常模式,并且编写一个错误的SQL语句(操作错误的数据表members),体会异常模式与警告模式和默认模式的区别。具体代码如下:

在设置为异常模式后,执行错误的SQL语句返回的结果如图10.6所示。

图10.6 异常模式捕获的SQL语句错误信息
10.6 小结
本章重点介绍了数据库抽象层——PDO,从它的概述、特点和安装开始讲解,到它的实际应用,包括如何连接不同的数据库、如何执行SQL语句、如何获取结果集,以及错误处理,再到它的高级应用事务都进行了详细讲解,并且都配有相应的实例。通过本章的学习,相信读者能够掌握PDO技术的应用。
10.7 实战
10.7.1 获取所有会员的“邮箱”信息
实例位置:资源包\源码\10\实战\01
试着修改例10.03,使用fetchColumn()方法获取所有会员的“邮箱”信息。
10.7.2 使用默认模式捕获SQL语句中的错误
实例位置:资源包\源码\10\实战\02
试着删除例10.04中config.php文件中的数据库密码,使用默认模式捕获SQL语句中的错误,如图10.7所示。

图10.7 捕获异常