愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

PDO使用总结

PDO是PHP Data Object(PHP数据对象)的简写,它的功能是提供一个数据访问抽象层,使得不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据,PHP从5.1开始附带PDO

一、连接与连接管理

1、使用PDO的构造函数来连接数据库,以连接mysql为例

$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pwd);

如果连接错误,那么抛出一个PDOException异常,可以通过捕获该异常来处理错误状态
2、关闭一个连接
连接数据库成功后,会返回一个PDO类的实例,连接会在PDO对象的生存周期内保持活动。如果要关闭连接,则就需要销毁对象,来确保没有任何引用再指向它。最简单的方法,就是赋予对象一个NULL值。如果没有这么做的话,那么在PHP脚本结束后,才会自动关闭连接

$pdo = NULL;

3、使用永久连接
使用永久连接,需要在最后一个参数传入配置信息。而且对于永久连接,只能使用构造函数传入配置信息,使用setAttribute()方法无效

$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pwd, [
    PDO::ATTR_PERSISTENT => true
]);


二、事务与自动提交

使用$pdo->beginTransaction()来开启事务,使用$pdo->commit()来提交事务、$pdo->rollBack()来回滚事务
注意: 使用$pdo->beginTransaction()开启事务,那么PDO将在必要时自动回滚(如出错、脚本结束或者终止后尚有未完成的事务)。如果手动发出启动事务查询通知,那么PDO将无法知晓,并且必要时不会发生回滚


三、预处理语句与存储过程

1、预处理的好处:

  • 数据库查询语句,自准备好后,运行需要经过分析、编译、优化的过程。对于同样的查询,而个别条件不一致的情况,使用预处理后便可不需要重复的分析/编译/优化周期,从而占用更少的资源,运行地更快。
  • 提供给预处理语句的参数不需要用引号包围,驱动程序会自动地处理,从而经过预处理的语句,可以避免发生SQL注入的情况

2、使用预处理语句进行重复插入

$sql = $pdo->prepare('INSERT INTO students (name, gender) VALUES(:name, :gender)');
$sql->bindParam(':name', $name);
$sql->bindParam(':gender', $gender);

// 插入第一条数据
$name   = "RuphiLau";
$gender = "男";
$sql->exec();

// 插入第二条数据
$name   = "KPP";
$gender = "女";
$sql->exec();

也可以使用?取代:name:gender作为占位符,相应的代码可以改为:

$sql = $pdo->prepare('INSERT INTO students (name, gender) VALUES(?, ?)');
$sql->bindParam(1, $name);
$sql->bindParam(2, $gender);

// 插入第一条数据
$name   = "RuphiLau";
$gender = "男";
$sql->exec();

// 插入第二条数据
$name   = "KPP";
$gender = "女";
$sql->exec();

3、预处理语句避免SQL注入

$sql = $pdo->prepare('SELECT * FROM students WHERE sid = ?');
if( $sql->execute([ $_GET['sid'] ]) ) {
    // ...
}

4、占位符必须放在整个值的位置,如:

// 下面这种写法,占位符无效
$sql = $pdo->prepare("SELECT * FROM students WHERE name LIKE '%?%'");
$sql->execute( [ $_GET['name'] ] );

// 正确写法
$sql = $pdo->prepare("SELECT * FROM students WHERE name LIKE ?");
$sql->execute( ['%'.$_GET['name'].'%'] );

占位符中也不能包括“-”
5、带输出参数调用存储过程

$sql = $pdo->prepare('CALL sp_returns_string(?)');
$sql->bindParam(1, $rtn, PDO::PARAM_STR, 4000);
$sql->exec();

echo $rtn;

6、带输入、输出参数调用存储过程

$sql = $pdo->prepare('CALL sp_takes_string_returns_string(?)');

$val = 'Hello';

$sql->bindParam(1, $val, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
$sql->exec();

echo $val;


四、错误与错误处理

PDO提供三种不同的错误处理模式:

  • PDO::ERRMODE_SILENT
    默认模式,PDO只简单地设置错误码。可以使用$pdo->errorCode()$pdo->errorInfo()方法来检查语句和数据库对象。如果是错误是由于对语句对象的调用产生的,那么可以使用$sql->errorCode()$sql->errorInfo()

  • PDO::ERRMODE_WARNING
    除了设置错误码之外,PDO还将发出一条传统的E_WARNING信息

  • PDO::ERRMODE_EXCEPTION
    除了设置错误码之外,还将抛出一个PDOException异常类并设置它的属性,来反射错误码和错误信息

设置错误模式的方法:$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)


五、大对象(LOBs)

可以在数据库中存储“大”数据,“大”意味着4kb及以上。通常大对象的本质是文本或者二进制。所以可以在$pdo->bindParam()或者$pdo->bindColumn()调用中第三个参数传入PDO::PARAM_LOB来使用大数据类型。PDO::PARAM_LOB告诉PDO采用流来映射数据,这样子就可以使用PHP Streams API来操作数据
1、插入一张图片到数据库

$sql = $pdo->prepare('INSERT INTO avatars (data) VALUES(?)');

$fp = fopen("someAvatar.jpg", 'rb');
$sql->bindParam(1, $fp, PDO::PARAM_LOB);

$pdo->beginTransaction();
$sql->exec();
$pdo->commit();

2、从数据库中获得一张图片

$sql = $pdo->prepare('SELECT data FROM avatars WHERE id=?');
$sql->exec($_GET['id']);
$sql->bindColumn(1, $data, PDO::PARAM_LOB);
$sql->fetch(PDO::FETCH_BOUND);

header("Content-type: image/jpeg");
fpassthru($data);