PDO(PHP Data Object)提供通用接口访问多种数据库,是建立PHP以及PHP连接数据库之间的抽象层。使用传统的mysql_connect()
mysql_query()
方法连接查询数据库,如果过滤不严,很容易产生SQL注入的风险。虽然可以使用mysql_real_escape_string()
等函数过滤用户提交的值,但还是在安全、性能方面有缺陷。
连接与获取
在使用PDO连接到数据库需要实例化一个PDO对象并且传递一个DSN(Data Source Name),包括数据库类型、主机地址、数据库名、用户名、密码。
|
|
♠以user表为例,获取其中的所有数据
|
|
获取有两种类型fetch()
(获取下一条记录)和fetchAll()
(获取所有记录)都接受fetch_style参数,该参数定义如何格式化结果集。
- PDO::FETCH_ASSOC:返回列名键组
- PDO::FETCH_NUM:返回数字键组
- PDO::FETCH_BOTH:结合了PDO::FETCH_ASSOC和PDO::NUM,返回每个值都出现两次的数组,一次使用列名,一次使用数字索引
- PDO::FETCH_CLASS:返回一个类的对象,列的名字设置到其属性当中。
参数绑定与预编译
PDO最大的特点就是参数绑定和预编译,绑定有两种:参数绑定和变量绑定。
♠以从user表里获取name为iat的用户为例
|
|
PDO中的bindParam()
方法还有第三个可选参数,即可把传入的参数转化为需要的类型,类型对应如下
- PDO::PARAM_BOOL:布尔类型
- PDO::PARAM_NULL:NULL类型
- PDO::PARAM_INT:整数类型
- PDO::PARAM_STR:字符串类型
等等,这样的话数据经过相应的处理就相对安全些了。
bindValue()
为绑定具体的值,变量作为引用被绑定,并只在execute() 被调用的时候才取其值。
占位符有两种形式:名称和问号,用问号的话则需要注意在传入execute()的时候,数组的值需要与对应的问号保持一致。如果SQL语句比较复杂,命名占位符更容易理解,更容易看出哪个值属于哪个参数。
♠以user表绑定name为例
|
|
在mysql中,为了防止注入攻击,通常使用
addslashes()
:特殊字符前加反斜线mysql_escape_string()
:转义SQL字符串的特殊字符mysql_real_escape_string()
:转义SQL字符串的特殊字符,并考虑连接的当前字符htmlspecialchars()
:把一些预定义的字符转换为html实体strip_tags()
:去除一些html和php标签
等等。当调用prepare()的时候,查询语句已经发送给服务器,此时是占位符发送了过去,没有用户提交的数据。而当调用execute()时,用户提交过来的数据才会传送过去,两者是相互独立的。在使用普通的mysql函数时需要使用mysql_escape_string()等函数过滤特殊的字符,而使用PDO的预处理语句已经帮我们完成了这一步骤。
使用预编译的好处是,查询只需被解析一次,但可以使用不同的参数执行多次。对于复杂查询来说,如果需要重复执行许多次有不同参数但结构相同的查询,这个过程会占用大量的时间。通过一个预编译语句可以避免重复分析、编译、优化环节。
异常与错误
PDO提供异常处理类PDOException,使用getMessage()
方法可以获得抛出异常的原因。
|
|
如果不是由于异常引起的错误,PDO并不会有任何解释的,这时候就需要使用PDO或PDOStatement类的errorInfo()
方法找出错误的原因,该方法返回一个数组,包括
- SQLSTATE,错误产生原因的ANSI SQL标准码
- 数据库驱动的错误代码
- 数据库驱动的错误消息
♠以select数据失败为例,返回false
|
|
事务
使用PDO中的事务的时候只需要
- 语句运行之前调用
beginTransaction()
方法启动事务 - 语句执行后调用
commit()
方法提交事务 - 出错的时候调用
rollback()
回滚
|
|
参考资料:
《PHP精粹》2.4PHP数据库对象
《PHP核心技术与最佳实践》5.1什么是PDO