开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第三阶段:PDO 预处理】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/713/detail/12737
PDO 预处理
内容介绍:
一、介绍
二、示例
三、小结
一、介绍:
1、目标:掌握PDO对于MySQL预处理的支持,能够利用PDO实现预处理操作
2、概念:
PDO预处理:是PDO针对MySQL预处理而封装一套特定的方法,在方法中做了一些优化操作,使得开发人员可以便捷的用来实现预处理
(1)PDO预处理的实现是基于MySQL预处理机制,只是针对预处理的操作过程进行了内部封装(与事务处理相同:内部再做封装,写好固定SQL指令)
参数处理:可以使用原占位符?,也可以使用PDO占位符:名字
例如在数据库中输入
prepare t_40_select from ‘select * from t_40 where id=?’
使用此类方式进行操作。而PDO可以使用占位符:名字做处理。’?’表达意思不明确,不能清楚具体代表;而名字表达清晰。在系统内部实际是将名字替换为?,使得用户操作方便。
(2)PDO中预处理提供了一套方法机制,主要由以下几个方法组成
PDO::prepare():发送预处理指令,只需要提供要执行的指令即可(然后会发送给服务器处理),不需要prepare名字from(名字为指定的)。成功得到一个PDOStatement类对象,失败得到一个false (或者异常错误)
PDOStatement:bindParam():绑定预处理所需要的参数,只能绑定变量(引用传递:传递数据时使用引用传递,所以只能传变量)
PDOStatement::bindValue():绑定预处理所需要的参数,可以绑定值(值传递:传递的为具体的一个值)
PDOStatement::execute():执行预处理(不需要再提供名字,系统自动检测执行),成功返回true,失败返回false
二、示例
1、PDO发送预处理指令∶即利用PDO:prepare()方法将要执行的SQL指令先发送给服务器编译
#实例化PDO对象
$drivers = array(
PDO ::ATTR_ERRMODE => PDO ::ERRMODE_EXCEPTION
); //此处用到异常处理模式,仅作了解
$pdo = new PDO( 'mysql:host=localhost;port=3306; dbname=db_2 ' , ' root ' , ' root ' , $drivers);
#发送预处理指令
$pre_sql = "select * from t_40";
#无数据预处理指令
$pre_sql = "select * from t_40 where id = ?";
#有参数预处理指令
$pre_sql = "select * from t_40 where id =:id";
# PDO特定预处理参数指令(:+字符串),更明确
#执行最后一条预处理指令:即使用:id作为参数的
$stmt = $pdo->prepare($pre_sql);
if( !$stmt) die('预处理指令执行失败! ');
演示预处理的执行:
创建文件36prepare.php
编写代码:
<?php
#PDO实现预处理
#实例化
$pdo = new PDO( 'mysql:host=localhost;port=3306;dbname=db_2;charset=utf8' , ' root' , 'root ' ) or die('数据库连接失败!');
//可以添加上字符集,加上连接
#准备预处理指令:发送给服务器
$pre_sql = "select * from t_40";
#无数据预处理指令
$pre_sql = "select * from t_40 where id = ?";
#有参数预处理指令
$pre_sql = "select * from t_40 where id =:id";
# PDO特定预处理参数指令(:+字符串),更明确
#发送预处理指令(例如现在使用第三条指令)
$stmt = $pdo->prepare($pre_sql);
if(!$stmt) die (‘预处理指令执行失败!’);
//成功继续向下执行,失败则die
访问网址36.prepare.php结果未显示任何内容。说明此处获取到的为false,查看false,继续输入代码:
var_dump($stmt);
再次刷新页面结果显示:
object(PDOStatement)#2 (1)( [ “queryString”]=> string(32) “select * from t _40 whereid = :id" )
结果得到的为一个对象。服务器这条指令并未执行,只是编译所以并不知哪种错误。
以上就拿到了对象,接着绑定参数,删掉var_dump($stmt);
2、绑定预处理参数:如果预处理本身是需要携带参数的,那么可以使用PDOStatement::bindvalue()/bindParam()进行参数绑定
l PDOStatement::bandValue()
#接上述代码
$stmt->bindValue( ' :id',1);#直接绑定数值
$id = 2;
$stmt->bindValue( ' :id ' , $id);#绑定变量数据
注意:如果在发送预处理指令的时候,使用的是"?"作为占位符,那么我们在进行绑定数据的时候,是按照顺序进行绑定的,起始位置的占位符序号为1,如果有多个占位符,依次类推
#预处理指令: select * from t_40 where id = ?
$stmt->bindValue(1,$id);
l PDOStatement::bindParam()
#接预处理指令
#$stmt->bindParam( ' : id',3);#
错误:不能绑定值,引用传递,只能是变量
$id = 4;
$stmt->bindParam(' :id ' , $id);
#必须是传递变量
演示:
继续在代码中添加:
#绑定预处理参数:bindValue
$stmt->bindValue( ' :id',1);
#直接绑定数值
$id = 2;
$stmt->bindValue( ' :id ' , $id);
#绑定变量数据
刷新页面无内容显示
#绑定预处理参数:bindParam
$stmt->bindParam( ' :id',1); #错误:bindParam第二个参数要求必须为变量(引用传递)
bindParam不能绑定值,若绑定值则会出现如下错误:
出现语法错误,提示Param第二个参数只能为引用。所以不能绑定数据,注释掉$stmt->bindParam( ' :id',1);
输入正确操作:
$id = 4;
$stmt->bindParam(' :id ' , $id);
#必须是传递变量
刷新页面无显示结果
绑定完成后下一步为执行:
3、执行预处理:利用PROStatement::execute()方法
#接上述代码
$res = $stmt->execute();
if( !$res) die('预处理执行失败! ');
#如果是查询,想要得到预处理执行的结果,还需要使用PDOStatement::fetch()方法进行数据解析
$row = $stmt->fetch(PDO : : FETCH_ASSOC) ;
演示:
添加代码:
#执行预处理
$res = $stmt->execute();
var_dump($res);
刷新页面结果显示: bool(false)
结果拿到false说明执行失败,预处理存在问题,因为上述的预处理指令$pre_sql = "select * from t_40 whereid =:id"是错误的,想要正确指令,进行修改$pre_sql = "select * from t_40 where id =:id"。结果显示bool(true)
#如果结果为true,说明预处理执行了,但是结果无法取出
#如果是查询预处理,需要继续进行PDOStatement::fetch()才能查出结果
输入代码:$row = $stmt->fetch(PDO : : FETCH_ASSOC) ;
var_dump($row);
刷新页面拿到结果。
#如果错误:取出信息
if($row===false){
#取出错误细信息:实际开发是将错误信息记录到系统日志中,返回false
echo 'SQL错误:<br/>' ;
echo '错误代码为:' . $stmt->errorCode() . '<br/> ';
echo’错误原因为:' . $stmt->errorInfo()[2];
exit;
}
刷新页面结果显示:
SQL错误:
错误代码为: 00000
错误原因为:You have an error in your SQl syntax, check the manual that coresponds to your MySQL sever version for the right syntax to use near' =’4" at line 1
以上提供的方法检查错误主要检查预处理后续执行错误,因为在前面预处理指令发送时这种指令并不会执行,所以SQL中有语法错误系统不能检测。只能等到执行预处理时才能执行指令。
例如:
图上SQL有语法问题,prepare不能判定,只能输入execute t_40_select;此处没办法执行,还需要定义变量。过程如此。
MySQL中出现预处理执行错误,PDO会将其放到PDOStatement类中进行查询,通过errorInfo方法获取具体信息。
以上就为预处理,从定义到绑定指令,绑定完成后再进行执行,本质都十分清晰。例如bandValue是将?进行替换,替换成id,如果多个可以进行传递数组。如果为:id,系统找到:id替换为对应id值即可。做替换即完成数据操作,实际上是解析变量然后使用set变量,例如:set @id = 1;
execute t_40_select using @id;
结果如下:
本质就是设置了一个变量再进行最终的execute时将设置的名字放在using中。名字与变量名不需考虑,只需绑定数据即可,内部自身组装。
三、小结
1、PDO预处理除了本身实现了MySQL的预处理,还额外优化了占位符:使用:+名字作为占位符(查看更清晰)
2、PDO利用了PDO::prepare()方法发送预处理SQL指令,成功得到PDOStatement对象
3、PDO利用PDOStatement::bindValue()/bindParam()来实现预处理数据绑定
4、PDO利用PDOStatement::execute()来实现调用预处理
5、如果利用PDO预处理来实现数据查询,那么在执行完成预处理之后,还需要调用PDOStatement::fetch()方法获取预处理得到的数据(所以查询预处理较少)
有了预处理后操作比较安全,数据是后期进行绑定,所以SQL注入可以实现避免。在批量处理时预处理也高效安全,但这些与PDO的预处理并无关系,而是MySQL预处理本身的特点。
PDO只是做外部的封装并没有改变预处理本身逻辑,由MySQL系统决定。