1. PDO 概览;
PDO(PHP data object)是一个扩展类库,为 PHP 访问数据库定义了轻量级、一致性的接口
什么是扩展类库?就是在 PHP 基础上进行了一些扩展、并且是以一个类的形式进行扩展。当然它不是 PHP 实现的,扩展类库一般都用 C 语言实现,因为 PHP 是用 C 语言实现的。既然是扩展类库,那使用的时候要把类实例化成对象才能使用 什么是一致性的接口?就是不管操作任何的数据库,都可以使用 PDO 进行操作。只需要掌握标准的 SQL 语句,我们只需要写一套 SQL 语句,就可以操作任何它支持的数据库。它提供了一个数据库访问的抽象层。PDO 就像日常生活中有一个翻译,这个翻译懂很多国语言,只需要请到他,就可以去很多国家玩,不要担心语言的问题。PDO 就充当这样的工作,只要写了标准的 SQL 语句,它会发送给你所操作的各种各样的数据库,都是没问题的。 PDO 提供了一个数据访问抽象层,这样,无论你使用了什么数据库,都可以通过一致的函数执行查询和获取数据,大大简化了数据库的操作,并能够屏蔽不同数据库之间的差异。
学会了 PDO,不管工作中遇见什么数据库,都不要再重新学习,只需要学会 PDO 中自带的成员方法就可以操作了。 使用 PDO 可以很方便的进行跨数据库程序的开发,以及不同数据库间的移植,是 PHP 在数据库处理方面的只要发展方向。它支持 MySQL,Oracle,MSSQL 等多种数据库。
2. 创建 PDO 对象与连接相关设置;
# 创建 PDO 对象
$dsn = 'mysql:dbname=testdb;host=127.0.0.1'; // 连接MySQL数据库的DSN(也就是PDO的MySQL驱动)
$user = 'dbuser'; // MySQL数据库的用户名
$password = 'dbpass'; // MySQL数据库的密码
try {
$dbh = new PDO($dsn, $user, $password); // 获得 PDO 对象
} catch (PDOException $e) {
echo '数据库连接失败: ' . $e->getMessage();
}
# 设置 PHP 连接 MySQL 时的客户端字符串和连接字符串集为:
# 注意:开发项目的时候设置字符集不写,因为一旦写了这句,用户连接一次数据库 / 发送一条语句 就执行一次,降低数据库效率
# 只要保证编辑器编码,数据库字符校验集,HTML页面编码三者一致,就不会有乱码。
$pdo->exec(“set names utf8”);
# 或者(两者有区别):
$pdo->query(“set names utf8”);
# PDO 与连接相关的选项
# 异常处理模式:PDO::ATTR_ERRMODE
- PDO::ERRMODE_SILENT // 不报、忽略错误(0)
- PDO::ERRMODE_WARNING // 以警告的方式报错(1)
- PDO::ERRMODE_EXCEPTION // 以异常的方式报错(2)(推荐)
# 自动提交:PDO::ATTR_AUTOCOMMIT
# 会在事务的时候用到,在事务处理的时候首先要关闭自动提交;在事务结束后要开启自动提交
# MySQL 里自动提交是默认开启的
- 0 //关闭自动提交
- 1 //开启自动提交
# 结果集返回格式:PDO::ATTR_DEFAULT_FETCH_MODE
- PDO::FETCH_ASSOC // 返回关联数组
- PDO::FETCH_NUM // 索引数组(数字数组)
- PDO::FETCH_BOTH // 以上两个
- PDO::FETCH_OBJ // 返回对象格式
<?php
try {
$dsn = "mysql:dbname=test;host=127.0.0.1" ;
$name = "root" ;
$pwd = "asdf" ;
$pdo = new PDO ( $dsn , $name , $pwd ) ;
$pdo - > setAttribute ( PDO : : ATTR_ERRMODE , PDO : : ERRMODE_EXCEPTION ) ;
var_dump ( $pdo ) ;
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
echo $e - > getFile ( ) ;
echo $e - > getLine ( ) ;
echo $e - > getCode ( ) ;
}
3. 使用 PDO 对象;
调整 PDO 行为 设置错误处理模式 使用 PDO 执行 SQL 语句 实例:pdo.php
<?php
try {
$dsn = "mysql:dbname=test;host=127.0.0.1" ;
$name = "root" ;
$pwd = "asdf" ;
$pdo = new PDO ( $dsn , $name , $pwd ) ;
$pdo - > setAttribute ( PDO : : ATTR_ERRMODE , PDO : : ERRMODE_EXCEPTION ) ;
$username = "user1" ;
$pwd = md5 ( 123456 ) ;
$email = 'user@admin.com' ;
$sql = "SELECT * FROM user" ;
$stmt = $pdo - > query ( $sql ) ;
foreach ( $stmt as $v ) {
echo "{ $v [ 'id' ] } -{ $v [ 'username' ] } -{ $v [ 'pwd' ] } -{ $v [ 'email' ] } -<br />" ;
}
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
}
4. PDO 预处理;
概念
PDO 预处理对象,更安全,更高效 在使用 PHP 的 MySQL 执行 SQL 语句,这样的语句会有一个问题:每次发送语句,数据库都会重新编译一下 这里有一个脚本,要执行删除用户的操作,语句为DELETE FROM user WHERE id = $_GET['id']
,然后就会把这条语句发送到数据库,让这条语句编译执行。 如果在编译的过程中,这个 id 不经过特殊处理的话,很可能产生安全性问题,比如说 SQL 注入。 举个例子, http://www.123.com/user.php?id=5
,这虽然是一个正常的 url,但是它在地址栏里,用户是可以改变它的,用户把它改了下,变成了 http://www.123.com/user.php?id=5||1=1
,那之前的 SQL 语句通过传值就变成了DELETE FROM user WHERE id = 5||1=1
然后这条 SQL 语句发送给数据库执行,然后表里所有的数据库就都被删除了。 这就是原来的处理方式,它并不安全。 如果使用 PDO 的预处理语句,它就不会发生以上的情况。 同样是上面的 SQL 语句,DELETE FROM user WHERE id =?
,这里的“?”是一个语法叫占位符。这句叫准备语句,会先发送给数据库进行编译。 编译的过程中,它只编译一部分,就是DELETE FROM user WHERE id =
,问号部分是等待传参数。 如果学过编译原理那就会知道,计算机在运行的时候,程序在运行的时候,是有一定的逻辑运算的,但是这些逻辑运算需要计算机去认识的。而我们的语言是高级语言,计算机是不认识的。 而编译的过程就是让计算机去认识我们所写的一些代码,实际就是一个翻译的过程。 现在,我们只翻译了这一部分DELETE FROM user WHERE id =
,那计算机就认识了。 问号的部分等待传参。如果问号的部分传过来了,不经过编译,里面有任何的逻辑,计算机都不会认识,会把它当做一个普通的字符串去处理。 就相当于你看到一个甲骨文。你不明白它是什么意思,你只知道它是一种文字。 预处理就是这样一个过程。后传的参数不会去翻译,再多的逻辑都不会被执行,这样就比较安全。 PDO 预处理还有一个好处,我们的语句只编译一次。就像DELETE FROM user WHERE id =
这部分编译好了,之后在执行相同的语句,就不会重新编译了。 在软件开发中,编译的过程是一个最耗时的过程,效率最低。 PDOStatement 对象的方法
# 方法
fetch() 返回结果集的下一行,结果指针下移,到头返回false
fetchAll() 通过一次调用返回所有结果,结果是以数组形式保存
execute() 负责执行一个准备好了的预处理语句
rowCount() 返回使用增、删、改、查操作语句后受影响的行总
bindParam() 将参数绑定到相应的查询占位符上
bindColumn() 将查询结果的字段绑定变量
# 得到 PDO 预处理对象的方法:
$sql = "select * from user order by id";
$sth = $pdo->prepare($sql);
# 以上代码中的 $sth 即为预处理对象
# 在 PDO 中参数式的 SQL 语句有两种(预处理 SQL ):
insert into stu(id,name) value(?,?); // ?号式(适合参数少的)
insert into stu(id,name) value(:id,:name); // 别名式(适合参数多的)
1.占位符方式
$stmt->bindParam(1, $name, PDO::PARAM_STR);
2.别名方式
$stmt->bindParam(':name', $name);
1.占位符方式
$query = "INSERT INTO contactInfo (name, address, phone) VALUES (?, ?, ?)";
$stmt = $dbh->prepare($query);
// 传递一个数组为预处理查询中的问号参数绑定值,并执行一次。
$stmt->execute(["张三", "上海", "13111111111"]);
// 再次传递一个数组为预处理查询中的问号参数绑定值,并执行第二次插入数据。
$stmt->execute(["李四", "北京", "133333333333"]);
2.别名方式
$query = "INSERT INTO contactInfo (name, address) VALUES (:name, :address)";
// 调用 PDO 对象中的 prepare() 方法准备查询,使用命名参数
$stmt = $dbh->prepare($query);
// 传递一个数组为预处理查询中的命名参数绑定值,并执行一次。
$stmt->execute([":name"=>"张三",":address"=>"上海"]);
// 再次传递一个数组为预处理查询中的命名参数绑定值,并执行第二次插入数据。
$stmt->execute([":name"=>"李四",":address"=>"北京"]);
$query = "SELECT uid, name, phone, email FROM contactInfo WHERE xx = yy
$stmt = $dbh->prepare($query); // 准备声明好的一个查询
$stmt->execute(); // 执行准备好的查询
$stmt->bindColumn(1, $uid); // 通过列位置偏移数绑定变量
$stmt->bindColumn(2, $name); // 通过列位置偏移数绑定变量$name
$stmt->bindColumn('phone', $phone); // 绑定列名称到变量$phone上
$stmt->bindColumn('email', $email); // 绑定列名称到变量$email上
while ($stmt->fetch(PDO::FETCH_BOTH)) {
echo $uid."\t".$name."\t".$phone."\t".$email."\n";
}
<?php
try {
$dsn = "mysql:host=127.0.0.1;dbname=test" ;
$name = "root" ;
$pwd = "asdf" ;
$pdo = new PDO ( $dsn , $name , $pwd ) ;
$pdo - > setAttribute ( PDO : : ATTR_ERRMODE , PDO : : ERRMODE_EXCEPTION ) ;
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
}
<?php
include ( "pdo.php" ) ;
try {
$sql = "INSERT INTO user(username,pwd,email) VALUE(?,?,?)" ;
$stmt = $pdo - > prepare ( $sql ) ;
$pwd = md5 ( 123456 ) ;
$stmt - > execute ( [ "user5" , $pwd , "user5@admin.com" ] ) ;
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
}
<?php
include ( "pdo.php" ) ;
try {
$sql = "INSERT INTO user(username,pwd,email) VALUE(:username,:pwd,:email)" ;
$stmt = $pdo - > prepare ( $sql ) ;
$pwd = md5 ( 123456 ) ;
$arr = [ "username" = > "user7" , "pwd" = > $pwd , "email" = > "user7@admin.com" ] ;
$stmt - > execute ( $arr ) ;
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
}
<?php
include ( "pdo.php" ) ;
try {
$sql = "SELECT * FROM user WHERE id > :id" ;
$stmt = $pdo - > prepare ( $sql ) ;
$stmt - > execute ( $_GET ) ;
$stmt - > bindColumn ( 1 , $id ) ;
$stmt - > bindColumn ( 2 , $username ) ;
$stmt - > bindColumn ( "pwd" , $pwd ) ;
$stmt - > bindColumn ( "email" , $email ) ;
while ( $stmt - > fetch ( PDO : : FETCH_ASSOC ) ) {
echo "{ $id } -{ $username } -{ $pwd } -{ $email } -<br />" ;
}
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
}
5. PDO 事务处理。
# 事务:将多条 SQL 操作(增删改)作为一个操作单元,要么都成功,要么都失败。
# MySQL 对事务的支持:
# 被操作的表必须是 innoDB 类型的表(支持事务)
# MySQL 常用的表类型:MyISAM (非事务)增删改速度快、InnodB(事务型)安全性高
$pdo - > beginTransaction ( ) ;
$pdo - > commit ( ) ;
$pdo - > rollback ( ) ;
$pdo - > setAttribute ( PDO : : ATTR_AUTOCOMMIT , 0 ) ;
$pdo - > setAttribute ( PDO : : ATTR_AUTOCOMMIT , 1 ) ;
<?php
try {
$dsn = "mysql:host=127.0.0.1;dbname=test" ;
$name = "root" ;
$pwd = "asdf" ;
$pdo = new PDO ( $dsn , $name , $pwd ) ;
$pdo - > setAttribute ( PDO : : ATTR_ERRMODE , PDO : : ERRMODE_EXCEPTION ) ;
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
}
<?php
include ( "pdo.php" ) ;
try {
$pdo - > setAttribute ( PDO : : ATTR_AUTOCOMMIT , 0 ) ;
$pdo - > beginTransaction ( ) ;
$sql = "UPDATE cash SET money = money - 50 WHERE username = 'zhangsan'" ;
$affectedRow = $pdo - > exec ( $sql ) ;
if ( ! $affectedRow ) {
throw new PDOException ( "转出失败!" ) ;
}
$sql = "UPDATE cash SET money = money + 50 WHERE username = 'lisi'" ;
$affectedRow = $pdo - > exec ( $sql ) ;
if ( ! $affectedRow ) {
throw new PDOException ( "转入失败!" ) ;
}
$pdo - > commit ( ) ;
echo "汇款成功!" ;
} catch ( PDOException $e ) {
echo $e - > getMessage ( ) ;
$pdo - > rollback ( ) ;
}
$pdo - > setAttribute ( PDO : : ATTR_AUTOCOMMIT , 1 ) ;