在学习PHP时,书中将PHP和mysql数据库的链接一直使用的是mysqli类。但这也使得PHP在更改数据库时会比较麻烦,自从知道PDO类之后就发现它的方便之处,因此就想就PDO写一些学习笔记。
PDO 提供了一个 数据访问 抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。 PDO 不不提供 数据库 抽象层;它不会重写 SQL,也不会模拟缺失的特性。如果需要的话,应该使用一个成熟的抽象层。
从 PHP 5.1 开始附带了 PDO,在 PHP 5.0 中是作为一个 PECL 扩展使用。 PDO 需要PHP 5 核心的新 OO 特性,因此不能在较早版本的 PHP 上运行。
PDO的链接
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
?>
但有时链接数据库会出错,会抛出一个PDOException异常对象,可以通过捕获这个对象来处理错误。因此一般在链接时可以使用try..catch..。
<?php
try {
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
} catch (PDOException $e) {
die("Error!:".$e->getMessage());
}
?>
在链接一个数据库并使用完毕之后,应该关闭它,可以通过设置它的值为null来完成。
$dbh = null;
有时为了进行持久化链接,必须在传递给 PDO 构造函数的驱动选项数组中设置 PDO::ATTR_PERSISTENT 。如果是在对象初始化之后用 PDO::setAttribute() 设置此属性,则驱动程序将不会使用持久连接。
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
PDO::ATTR_PERSISTENT => true
));
?>
PDO事务管理
并非每种数据库都支持事务,因此当第一次打开连接时,PDO 需要在所谓的“自动提交”模式下运行。自动提交模式意味着,如果数据库支持,运行的每个查询都有它自己的隐式事务,如果数据库不支持事务,则没有。如果需要一个事务,则必须用 PDO::beginTransaction() 方法来启动。如果底层驱动不支持事务,则抛出一个 PDOException 异常(不管错误处理设置是怎样的,这都是一个严重的错误状态)。一旦开始了事务,可用 PDO::commit() 或 PDO::rollBack()来完成,这取决于事务中的代码是否运行成功。
当脚本结束或连接即将被关闭时,如果尚有一个未完成的事务,那么 PDO 将自动回滚该事务。这种安全措施有助于在脚本意外终止时避免出现不一致的情况——如果没有显式地提交事务,那么假设是某个地方出错了,所以执行回滚来保证数据安全。
只有通过 PDO::beginTransaction() 启动一个事务后,才可能发生自动回滚。如果手动发出一条查询启动事务, 则 PDO 无法知晓,从而在必要时不能进行回滚。
在下面例子中,假设为新员工创建一组条目,分配一个为23的ID。除了登记此人的基本数据之外,还需要记录他的工资。两个更新分别完成起来很简单,但通过封闭在 PDO::beginTransaction() 和PDO::commit() 调用中,可以保证在更改完成之前,其他人无法看到这些更改。如果发生了错误,catch 块回滚自事务启动以来发生的所有更改,并输出一条错误信息。
<?php
try {
$dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
array(PDO::ATTR_PERSISTENT => true));
echo "Connected\n";
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();
} catch (Exception $e) {
$dbh->rollBack();
echo "Failed: " . $e->getMessage();
}
?>
PDO预处理
预处理语句占用更少的资源,因而运行得更快。提供给预处理语句的参数不需要用引号括起来,驱动程序会自动处理。如果应用程序只使用预处理语句,可以确保不会发生SQL 注入(然而,如果查询的其他部分是由未转义的输入来构建的,则仍存在 SQL 注入的风险)。
prepare()方法负责准备要执行的查询。execute()方法负责执行准备好的查询。execute()方法中的input_parameters参数是可选的,虽然很方便,但是如果需要传递多个变量时,以这种方式提供数组会很快变得难以处理(当数组元素过多时,也就是当数据表中的列过多时,代码设计会变得特别难以阅读或出错)。使用bindParam()方法可以解决这个问题。
下面例子通过用 name 和 value 替代相应的命名占位符来执行一个插入查询
<?php
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// 插入一行
$name = 'one';
$value = 1;
$stmt->execute();
// 用不同的值插入另一行
$name = 'two';
$value = 2;
$stmt->execute();
?>
下面例子通过用 name 和 value 取代 ? 占位符的位置来执行一条插入查询。
<?php
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
// 插入一行
$name = 'one';
$value = 1;
$stmt->execute();
// 用不同的值插入另一行
$name = 'two';
$value = 2;
$stmt->execute();
?>
下面例子获取数据基于键值已提供的形式。用户的输入被自动用引号括起来,因此不会有 SQL 注入攻击的危险。
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name']))) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
?>
如果数据库驱动支持,应用程序还可以绑定输出和输入参数.输出参数通常用于从存储过程获取值。输出参数使用起来比输入参数要稍微复杂一些,因为当绑定一个输出参数时,必须知道给定参数的长度。如果为参数绑定的值大于建议的长度,就会产生一个错误。
带输出参数调用存储过程
<?php
$stmt = $dbh->prepare("CALL sp_returns_string(?)");
$stmt->bindParam(1, $return_value, PDO::PARAM_STR, 4000);
// 调用存储过程
$stmt->execute();
print "procedure returned $return_value\n";
?>
带输入/输出参数调用存储过程
<?php
$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)");
$value = 'hello';
$stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
// 调用存储过程
$stmt->execute();
print "procedure returned $value\n";
?>
占位符的无效使用
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute(array($_GET['name']));
// 占位符必须被用在整个值的位置
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
?>
PDO属性与方法
PDO::beginTransaction — 启动一个事务
PDO::commit — 提交一个事务
PDO::__construct — 创建一个表示数据库连接的 PDO 实例
PDO::errorCode — 获取跟数据库句柄上一次操作相关的 SQLSTATE
PDO::errorInfo — Fetch extended error information associated with the last operation on the database handle
PDO::exec — 执行一条 SQL 语句,并返回受影响的行数,用于增删改,对查询结果返回0
PDO::exec() 不会从一条 SELECT 语句中返回结果。对于在程序中只需要发出一次的 SELECT 语句,可以考虑使用 PDO::query()。对于需要发出多次的语句,可用 PDO::prepare() 来准备一个 PDOStatement 对象并用 PDOStatement::execute() 发出语句。
PDO::getAttribute — 取回一个数据库连接的属性
PDO::getAvailableDrivers — 返回一个可用驱动的数组
PDO::inTransaction — 检查是否在一个事务内
PDO::lastInsertId — 返回最后插入行的ID或序列值
PDO::prepare — Prepares a statement for execution and returns a statement object
PDO::query — Executes an SQL statement, returning a result set as a PDOStatement object
PDO::quote — Quotes a string for use in a query.
PDO::rollBack — 回滚一个事务
PDO::setAttribute — 设置属性
PDOStatement
PDOStatement::bindColumn — 绑定一列到一个 PHP 变量
PDOStatement::bindParam — 绑定一个参数到指定的变量名
PDOStatement::bindValue — 把一个值绑定到一个参数
PDOStatement::closeCursor — 关闭游标,使语句能再次被执行。
PDOStatement::columnCount — 返回结果集中的列数
PDOStatement::debugDumpParams — 打印一条 SQL 预处理命令
PDOStatement::errorCode — 获取跟上一次语句句柄操作相关的 SQLSTATE
PDOStatement::errorInfo — 获取跟上一次语句句柄操作相关的扩展错误信息
PDOStatement::execute — 执行一条预处理语句
PDOStatement::fetch — 从结果集中获取下一行
PDOStatement::fetchAll — 返回一个包含结果集中所有行的数组
PDOStatement::fetchColumn — 从结果集中的下一行返回单独的一列。
PDOStatement::fetchObject — 获取下一行并作为一个对象返回。
PDOStatement::getAttribute — 检索一个语句属性
PDOStatement::getColumnMeta — 返回结果集中一列的元数据
PDOStatement::nextRowset — 在一个多行集语句句柄中推进到下一个行集
PDOStatement::rowCount — 返回受上一个 SQL 语句影响的行数
PDOStatement::setAttribute — 设置一个语句属性
PDOStatement::setFetchMode — 为语句设置默认的获取模式。
以上资料均可在PHP手册上查到。