PHP之PDO的理解和实践
2016-08-02
概念
什么是PDO, 即PHP DATA OBJECT
,是一种抽象的数据库接口, 其本身并不会有任何的数据库功能, 只是定义了一些数据操作的接口规范。
如果需要针对某个数据库的操作, 需要对应的数据库类型的PDO驱动。这样才能真正的操作数据库.
同时, PDO 是需要PHP的面向对象的支持
优点
那么PDO 有什么优点呢
灵活
PDO 提供的是一种抽象的接口定义, 每种实际的PDO驱动都要实现这套定义。
既然接口一样, 那我们再代码中就能通过很少的代价对数据库进行替换了。 实际的操作只是更改了数据库驱动
安全
通过使用PDO的预处理语句,我们可以直接将查询参数传递进来, 而无需担心sql注入的问题
快速
当需要执行多个相似的sql查询语句, 而仅仅是查询参数不同的操作时, 通过预处理语句, 可以避免数据库对查询语句的重复 解析/编译/优化
的过程, 从而提高运行速度
PDO 类说明
PDO 的实现有主要有两个类, 在此说明下
PDO 类
这个类主要用于保存用户的数据库连接, 并设置连接参数,操作事务
通过这个类的实例, 我们还可以获取一些当前使用的数据库驱动的参数 比如: 当前连接是否处于事务状态,当前使用的是哪种数据库驱动
通过这个类实例的prepare
方法, 我们可以预处理要执行的sql语句,这个函数返回的结果实际上就是下面要说的另外一个类PDOStatement
类的实例
此外, 这个类的实例也可以直接对数据库发起请求, 比如exec
和query
函数
PDOStatement 类
代表一条预处理语句,并在该语句被执行后代表一个相关的结果集。
这是官方的一个说明。太过简单
上面 PDO
类实例的prepare
query
函数的返回结果, 实际上也是PDOStatement
的实例。
这个类的实例拥有对数据库请求结果的多种操作和设置, 特别是,为PDO连接预处理sql语句时,其拥有的快速安全的特性,很受推崇
实践
PDO 类实践
连接数据库
try{
$dbh = new PDO("mysql:host=localhost;dbname=test_db;charset=utf8","root","1");
}catch(PDOException $e){
die("Error: $e");
}
这里我们连接上数据库后创建了一个PDO
的类实例$dbh
获取数据库驱动类型
var_dump($dbh->getAvailableDrivers());
可以看到输出是
array(1) {
[0]=>
string(5) "mysql"
}
执行query
var_dump($stm = $dbh->query("select id,title from article",PDO::FETCH_NAMED));
这里我们直接使用PDO
的query
函数,第二个参数控制返回结果集, 比如返回的是一个关联数组, 用字段名作key
,用查询结果做value
时用PDO::FETCH_NAMED
参数, 或者要返回一个索引数组, 直接用偏移量做key
时, 用PDO::FETCH_NUM
参数
在这里, 我们可以通过var_dump
得到$stm
的结果实际上就是PDOStatement
类的实例
输出如下
object(PDOStatement)#2 (1) {
["queryString"]=>
string(28) "select id,title from article"
}
结果处理部分参考 PDOStatement
类实践
预处理
当我们有多个相似的语句, 这些语句唯一的区别就是参数不同, 或者需要执行多次的语句, 这个时候我们就比较适合使用预处理语句了
预处理语句可以预先解析,编译,优化sql语句, 返回一个PDOStatement
类实例, 再调用该实例的execute
函数, 传递不同的参数就能得到不同的结果, 同时, 因为避免了多次解析编译优化的过程, 可以使得后面的query请求更快
$stm = $dbh->prepare("select id,title from article where cid = :cid");
至于其中的:cid
,其实只是一个占位符
, 后面通过调用PDOStatement
的bindParam
或 bindValue
方法就可以通过变量将参数传递进去, 这里的:cid
是关键词占位符, 每个关键词不可重复
或者这里也可以是
$stm = $dbh->prepare("select id,title from article where cid = ?");
?
也是一个占位符, 通过调用PDOStatement
的bindParam
或bindValue
方法也能传递参数进去, 也可以通过在调用execute
时传递一个索引数组,数组里面的元素与?
占位符一一对应, 也能传递参数到sql的请求里
PDOStatement 类实践
在上面我们通过两种方式得到了这个类实例
PDO
的query
函数 和 PDO
的预处理函数prepare
我们先说对调用prepare
后的这个类实例
bindParam
在prepare
当我们使用占位符时,我们可以通过bindParam
将关键字与某个变量关联起来, 然后调用execute
, 就能得到传递了参数的查询结果
如
$cid = 6;
var_dump($stm = $dbh->prepare("select id,title from article where cid = :cid"));
$stm->bindParam(":cid",$cid);
$stm->execute();
注意这里的绑定是引用绑定, 如果后面我们继续如下
$cid = 5;
$stm->execute();
实际的查询将是直接传递新的cid参数而无须再次绑定
对使用?
来说, 绑定的关系只是从关键词变成了偏移量,
$cid = 6;
var_dump($stm = $dbh->prepare("select id,title from article where cid = ?"));
$stm->bindParam(0,$cid);
$stm->execute();
bindValue
bindValue
与 bindParam
其实很类似, 也是为占位符填充具体的参数,绑定的方式基本上也与bindParam
一致,只要把bindParam
换成 bindValue
即可
但其余bindParam
有什么区别呢
bindParam
是引用方式绑定的变量, 只要变量做了修改, 那么query
传递的参数就发生了变化。bindParam
的例子里有说明
而bindValue
只是绑定具体的值, 而非引用方式, 所以query
传递的参数一旦需要修改,就要重新bindValue
,所以要注意这点
execute
在prepare
并绑定好参数后, 我们就可以直接调用execute
去执行query请求了
$stm->bindParam(":cid",$cid);
$stm->execute();
假如prepare
的query 中用的是?
占位符, 还可以直接使用 execute
传递一个数组, 数组元素与query中的?
一一对应, 也是可以传递请求参数的
setFetchMode
设置返回结果的格式, 比如是返回关联数组(PDO::FETCH_NAMED
)结果还是索引数组(PDO::FETCH_NUM
)结果
$stm->setFetchMode(PDO::FETCH_NAMED);
fetch 系列函数
然后我们就可以通过fetch系列函数返回结果了
如:
$stm->fetchall();//返回所有查询结果
$stm->fetch();//从结果集的下一行返回结果
$stm->fetchColumn();//从结果集中的下一行返回单独的一列。
$stm->fetchObject();// 从结果集的下一行返回结果并以对象的方式返回
对PDO
的query
函数的执行结果就可以通过这些函数返回
closeCursor
关闭游标,使语句能再次被执行。
在有些数据库驱动中, 如果查询结果不被fetch 完, 后续的查询就不能继续执行, 所以再查询完成后, 我们要调用这个函数,防止这种问题发生
总结
以上一个完整的例子如下:
try{
$dbh = new PDO("mysql:host=localhost;dbname=test_db;charset=utf8","root","1");
}catch(PDOException $e){
die("Error: $e");
}
var_dump($dbh->getAvailableDrivers());
$cid = 6;
$stm = $dbh->prepare("select id,title from article where cid = :cid");
$stm->bindParam(":cid",$cid);
$stm->execute();
$stm->setFetchMode(PDO::FETCH_NAMED);
while($row = $stm->fetch()){
var_dump("hello");
var_dump($row);
}
$stm->closeCursor();