具体参考《PHP核心技术与最佳实践》的5.1章 《什么是PDO》php
PHP针对每种数据库都有一个独立的模块、一组独立的函数。这样的结构和设计让PHP兼容多种数据库变得困难。一旦要将一个应用移到另一种数据库环境中,或者是须要添加新的数据库支持,就不得不从新编写和数据库相关的操做。一般编写多个类,用适配器模式来实现。在这个历史背景下PDO出现了。PDO(PHP Data Objects)提供了一个通用接口访问多种数据库,即抽象的数据模型支持链接多种数据库。有了PDO使代码变得更简洁、更安全。mysql
在PHP中,链接MySQL数据库的一般有3种方式:sql
具体的能够参考:【链接数据库】PHP7的链接数据库的三种方法【原创】数据库
相比MySQLi,PDO的优点在于支持多种数据库,而MySQLi只能支持MySQL,因此通常更推荐使用PDO来对数据库进行操做。
PDO提供了一个数据访问抽象层,这就意味着无论使用哪一种数据库,均可以用一样一组API对数据进行操做,保证了可抽象性和访问接口的一致性。安全
开启PDO很容易,通常来讲安装好PHP默认都会开启PDO,若是没有则去php.ini中找到如下语句,把前面的分号去掉便可dom
;extension=php_pdo.dll
使用PDO的第一步是配置数据源,以后的用法和MySQL扩展操做数据库的方法没有什么区别了,
PDO的操做主要有PDO::query()、PDO::exec()、PDO::prepare()函数
如下是PDO的示例:fetch
<?php /** * PDO 的使用实例 */ try { // 配置PDO的数据源 $dsn = 'mysql: host=localhost; dbname=php_book'; // 构造方法 $db = new PDO($dsn, 'root', '123456'); // 设置异常可捕获 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec("SET NAMES 'UTF8'"); // 插入到日志中 $sql = "INSERT INTO users(name, email, password, created_at) values ('pdo_test', '8888@qq.com', 'bbb', now())"; $db->exec($sql); // 使用预处理语句 $insert = $db->prepare("INSERT INTO users(name, email, password, created_at) values (?, ?, ?, now())"); $insert->execute(array('pdo_test1', '8448657@qq.com', 'aaa')); // 异常 $insert->execute(array('pdo_test2', '8448657@qq.com', 'aaa', 9, 10)); $sql = "select name, email, password, created_at from users"; $query = $db->prepare($sql); $query->execute(); var_dump($query->fetchAll(PDO::FETCH_ASSOC)); } catch (PDOException $e) { echo $e->getMessage(); }
注意:使用PDO从MySQL数据库查询出来的数据都是string类型的,在某些特殊应用下,可能须要转换格式优化
PDO最大的特色就是引入参数绑定和预编译。ui
下面是从数据库中查询某条记录:
<?php $pdo = new PDO('mysql: host=localhost; dbname=php_book'); $pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']);
这是一段糟糕的代码。插入一个原始的请求参数到 SQL 请求中。这将让被黑客轻松地利用[SQL 注入]方式进行攻击。想一下若是黑客将一个构造的 id 参数经过像 http://domain.com/?id=1%3BDEL... 这样的 URL 传入。这将会使 $_GET['id'] 变量的值被设为 1;DELETE FROM users 而后被执行从而删除全部的 user 记录!所以,你应该使用 PDO 限制参数来过滤 ID 输入。
上面的代码可优化为:
<?php $pdo = new PDO('mysql: host=localhost;dbname=php_book'); $stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id'); $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); $stmt->bindParam(':id', $id, PDO::PARAM_INT); $stmt->execute();
在MySQL应用中,为了防止注入攻击,一般在PHP中使用intval、addslashes等函数对传入的参数进行转义,转变为SQL中合法的参数类型,这种方法较复杂,而使用PDO的bindParam方法会变得很快捷,只须要在函数中指定第三个参数,便可对传入的参数进行转换,转换为须要的类型拼接到原生的SQL语句中
好比:
<?php /** * PDO 的最大特色是引入参数绑定和预编译 * 参数绑定:经过绑定变量来执行准备好的语句 * 两种绑定参数的方式 */ $calories = 150; $colour = 'red'; // 配置PDO的数据源 $dsn = 'mysql: host=localhost; dbname=php_book'; // 构造方法 $db = new PDO($dsn, 'root', '123456'); // 执行预处理语句(第一种绑定变量的方式) $sth = $db->prepare('SELECT name, colour, calories FROM fruit WHERE calories > :calories AND colour = :colour'); // 绑定变量,将变量转化为int类型 $sth->bindParam(':calories', $calories, PDO::PARAM_INT); // 绑定变量,将变量转化为string类型 $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12); // 执行 $sth->execute(); var_dump($sth->fetchAll(PDO::FETCH_ASSOC)); // 执行预处理语句(第二种绑定变量的方式) $sth = $db->prepare('SELECT name, colour, calories FROM fruit WHERE calories > ? AND colour = ?'); // 绑定变量,将变量转化为int类型 $sth->bindParam(1, $calories, PDO::PARAM_INT); // 绑定变量,将变量转化为string类型 $sth->bindParam(2, $colour, PDO::PARAM_STR, 12); // 执行 $sth->execute(); var_dump($sth->fetchAll(PDO::FETCH_ASSOC));
预编译负责两件事,转义和软解析提速。程序要支持预编译,除了要数据库支持外,还须要驱动支持(PDO和MySQLi均支持)
一个事务中全部的工做在提交时,即便是分阶段执行,也要保证安全的应用于数据库,不被其余的链接干扰,事务工做能够在请求发生错误时自动取消。
事务的主要特性:原子性、一致性、独立性、持久性(Atomicity,Consistency,Isolation,Durability,ACID)。典型运用就是经过把批量的改变保存,而后当即执行,这样就能提升效率,一旦事务不成功,将会回滚到初始状态,保证数据的一致性。
SQL一般工做在自动提交模式下,这意味着执行的每一个查询都有本身隐含的事务处理,不管是数据库支持事务仍是因数据库不支持而不存在事务,DML语句执行的结果都将当即生效而不可更改。好比在MySQL中执行一条update语句,其功能将会当即生效而且是永久性不可更改性的。而在Oracle数据库中,默认是事务模式,要delete一条数据,数据并不会被永久性删除,只有执行了commit命令后才会生效。
PDO中使用beginTransaction()方法来建立事务。在一个事务中,使用commit()或者是rollback()方法来结束事务,具体应用哪一种方法这取决于事务中代码运行是否成功。脚本结束或者一个链接要关闭时,若是还有一个未处理完的事务,PDO自动将其回滚。这对于脚本意外终止状况来讲是一个安全方案,若是没有明确提交事务,它将假设发生一些错误,为数据的安全执行回滚。
自动回滚仅发生于经过beginTransaction()创建的事务。若是用手动方式执行一个开始事务的查询,PDO没法知道他的状况故没法回滚。
代码以下:
<?php try { $conn = new PDO('mysql: host=localhost; dbname=php_book', 'root', '123456'); // 开启事务 $conn->beginTransaction(); for($i = 0; $i < 1000000; $i++) { $conn->exec("insert into `users` values(null, 'username')"); } // 提交事务 $conn->commit(); } catch(PDOException $ex) { // 执行回滚 $conn->rollBack(); }
注意:由于使用了事务,要么成功要么失败,若是发现第一条执行了,可是第二条没有执行或者是失败了,则应该检查一下表类型是否为MyISAM,MyISAM引擎是不支持事务的,须要改用InnoDB或者其余的支持事务的引擎。