PDO

PDO类:数据库初始化,包括连接认证和执行sql指令

PDOStatement类:数据解析操作,针对数据结果操作(有数据结果返回)

PDOException类:数据库异常类

加载PDO扩展:php5以后自动加载,无须手动加载

开启PDO MYSQL扩展-修改php.ini:extension=pdo_mysql;重启web服务器.使用phpinfo()函数查看是否加载成功.

PDO常用方法

PDO::__construct():实例化PDO对象 PDO::exec():执行一个写操作SQL指令,返回受影响的行数

PDO::query():执行一个读操作SQL指令,返回一个PDOStatement类对象(后者进行数据解析操作).

PDO::errorCode()和PDO::errorInfo():获取上次错误的信息(错误码和错误描述数组)

实例化PDO对象

使用PDO::__construct(string $dsn, string $user, string $password[, array $drivers])实例化PDO对象

$dsn:数据库基本信息

驱动名字-数据库产品,使用英文:分割,比如mysql:

驱动选项-主机地址,使用 host=主机地址,跟着驱动名字之后,如’mysql:host=localhost’

驱动选项-端口,使用port=端口号,默认3306可不写.可写为为’mysql:host=localhost;port=3306;’或’mysql:port=3306;host=localhost;’

驱动选项-数据库,使用dbname=数据库名字(可以事先没有)

$user:数据库用户名(如果数据库允许匿名用户,那么可以没有该参数)

$password:密码(若允许匿名用户,和用户名一样…)

$drivers:PDO属性设置.是关联数组.利用PDO内部常量进行设置

示例:

$pdo = new PDO(‘mysql:host=localhost;port=3306;dbname=mydb’, ‘root’, ‘123456’);

$dsn= ‘mysql:host=localhost;port=3306;dbname=mydb’;

$user=’root’;

$password=’123456′;

$pdo=new PDO($dsn,$user,$password);

PDO写操作和读操作

建立pdo对象后,我们可以使用exec方法执行写操作;query方法执行读操作.

写操作

$sql = “delete from tab_student order by s_id desc limit 1”; //写操作sql指令

$rows= $pdo->exec($sql); //执行,得到受影响的行数.如果没有受影响的行,则返回0

读操作

$sql=’select * from tab_student’; //读操作sql

$stmt = $pdo -> query($sql); //执行读操作.成功返回PDOStatement对象,失败返回false

PDO错误处理errorCode()和errorInfo()

$pdo = new PDO(‘mysql:host=localhost;port=3306;dbname=mydb’, ‘root’, ‘123456’);//实例化PDO对象

$sql = “delete fromstudent”; //一条错误sql语句

$rows= $pdo->exec($sql); //执行

//若sql执行成功exec()也可能返回0.SQL执行错误则返回false.

//所以判断sql是否错误,需要判断结果为false

if(false===$rows){//无论sql读/写错误,都返回false

echo ‘sql执行出错:<b+r>’;

echo ‘错误代码为:’ . $pdo->errorCode() . ‘<b+r>’;

echo ‘错误信息为:’ . $pdo->errorInfo()[2] . ‘<b+r>’;//errorInfo返回数组,下标2代表具体错误信息

exit;//退出-错误不要继续执行代码

}

PDO简单封装

//初始化PDO

//mypdo.php

function pdo_init(){

$pdo= @new PDO(‘mysql:host=localhost;port=3306;dbname=mydb’,’root’,’123456′);

if(!$pdo){

exit(‘数据库连接认证失败!’);

}

//字符集初始化-好像新版本不需要?

$pdo->exec(‘set names utf8’);// $pdo->exec(‘set names utf8mb4’);

//返回得到的pdo对象

return $pdo;

}

//PDO写操作

function pdo_exec($pdo,$sql){

$res=$pdo->exec($sql);

if(false===$res){//错误判定

echo ‘sql执行出错:<b+r>’;

echo ‘错误代码为:’ . $pdo->errorCode() . ‘<b+r>’;

echo ‘错误信息为:’ . $pdo->errorInfo()[2] . ‘<b+r>’;//errorInfo返回数组,下标2代表具体错误信息

exit;//退出-错误不要继续执行代码

}

return $res;//返回结果,受影响的行数

}

//写操作获取自增长id

function pdo_id($pdo){

return $pdo->lastInsertId();

}

//PDO读操作-查询

function pdo_query($pdo, $sql){

$stmt = $pdo->query($sql);

if(false===$stmt){//错误判定

echo ‘sql执行出错:<b+r>’;

echo ‘错误代码为:’ . $pdo->errorCode() . ‘<b+r>’;

echo ‘错误信息为:’ . $pdo->errorInfo()[2] . ‘<b+r>’;//errorInfo返回数组,下标2代表具体错误信息

exit;//退出-错误不要继续执行代码

}

return $stmt;//返回查询结果

}

//PDO读操作后获取查询结果

function pdo_get($stmt, $only = true){

if($only){//

//fetch()默认返回关联数组+索引数组;FETCH_ASSOC返回关联数组;FETCH_NUM返回索引数组

return $stmt->fetch(PDO::FETCH_ASSOC);//获取一条记录:返回一维数组

} else{

//fetchAll()默认返回关联数组+索引数组;FETCH_ASSOC返回关联数组;FETCH_NUM返回索引数组

return $stmt->fetchAll(PDO::FETCH_ASSOC);//返回二维数组,一个维度代表一个记录

}

}

//实际编程并调用封装PDO的代码

include ‘mypdo.php’;

$pdo=pdo_init();//初始化

$sql=”select * from student limit 1″;//查询语句

$stmt = pdo_query($pdo,$sql);//执行读操作查询

$row=pdo_get($stmt);//获取一条记录

$rows=pdo_get($stmt,false);//获取多条记录

PDO事务

mysql事务

开启事务:start transaction,记录到事务日志

事务操作:具体的写操作,通常为多个指令

提交事务:本次事务结束

成功提交:commit,所有事务日志内容同步到数据库,并清空当前事务日志

失败回滚:rollback,直接清空当前事务日志

PDO实现事务

PDO::beginTransaction():开启事务

PDO::exec():执行事务(写操作)

PDO::rollBack():回滚所有事务

PDO::commit():成功提交所有事务

示例代码:

$pdo=new PDO(‘mysql:host=localhost;prot=3306;db=mydb’, ‘root’, ‘123456’);

$pdo->beginTransaction() or die(‘事务开启失败’);

$pdo->exec(‘insert into tab_students values()’);

回滚点(手写)

mysql没有提供回滚点,我们可以手写,以下为示例

//接上述代码

$pdo->exec(‘savepoint sp1′); //mysql存回滚点命令

//继续执行事务/语句…

//回滚到指定回滚点

$pdo->exec(‘rollback to sp1’);

//…….事务/语句

//终止事务:成功/失败

$pdo->commit();//成功提交事务

$pdo->rollback();//失败回滚

PDO异常

异常

代码示例:

$n1=5;

$n2=0;

//要求$n1/$n2

if($n2==0){//除数为0

throw new Exception(‘除数不能为0’);//抛出自定义异常信息

}

$res=$n1/$n2;//安全的做除法运算

异常处理机制
使用try-catch和set_error_handler

代码示例:

set_error_handler(function(){ //必须的回调函数,抛出自己设置的自定义错误

throw new Exception(‘错误’)

});

//也可以不采用匿名函数,用以下普通形式书写

function exception_handler(){

throw new Exception(‘错误’);

}

set_error_handler(‘exception_handler’);


echo ‘hello, world!’;

$n1= $_GET[‘n1’];

$n2= $_GET[‘n2’];

//可能出错的代码 使用try-catch结构

try{

$res=$n1/$n2; //可能会出错的语句

}catch(Exception $e){ //捕获错误-没有错误流程不会进入catch结构

//die(‘发生错误!’); //提示错误并退出

//更详细的错误,调用Exception的方法或属性

echo ‘代码发生错误!<b+r>’;

echo ‘错误文件为:’ . $e->getFile() . ‘<b+r>’;

echo ‘错误行号为:’ . $e->getLine() . ‘<b+r>’;

echo ‘错误描述为:’ . $e->getMessage() . ‘<b+r>’;

}

注意:没有被try结构包裹的语句发生错误将不会被我们自定义的错误捕获,此时仍由php系统处理错误.即PHP报错并提示信息.

有时候代码如果走到一个’死胡同’,即代码执行没有任何问题,但是不符合我们的逻辑,以前我们是跳转提示,现在可以抛出异常,交给异常来处理,例如

try{

if($n !=0 ){

$res=10/$n;

}else{

//业务没法发展了,直接抛出异常

throw new Exception (‘0不能做除数!’);

}

}catch(Exception $e){

//更详细的错误,调用Exception的方法或属性

echo ‘代码发生错误!<b+r>’;

echo ‘错误文件为:’ . $e->getFile() . ‘<b+r>’;

echo ‘错误行号为:’ . $e->getLine() . ‘<b+r>’;

echo ‘错误描述为:’ . $e->getMessage() . ‘<b+r>’;

die();

}

PDO错误机制

PDO中提供了三种错误机制,是通过PDO的常量PDO::ATTR_ERRMODE来选择的.

PDO::ERRMODE_SILENT:静默模式,出错了不处理(默认) PDO::ERRMODE_WARNING:警告模式,出错了立马给出错误提示 PDO::ERRMODE_EXCEPTION:异常模式,出错了把错误交给异常PDOExeption对象

PDO::ERRMODE_SILENT举例:静默模式

$pdo=new PDO(‘msyql:host=localhost;port=3306;dbname=mydb’ , ‘root’ , ‘123456’);

$pdo->exec(‘insert into students values()’);//错误sql,但系统不会报错

PDO::ERRMODE_WARNING举例:警告模式

$pdo=new PDO(‘msyql:host=localhost;port=3306;dbname=mydb’ , ‘root’ , ‘123456’);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);

$pdo->exec(‘insert into students values()’);//错误sql,但系统不会报错

PDO::ERRMODE_EXCEPTION举例:异常模式

$pdo=new PDO(‘msyql:host=localhost;port=3306;dbname=mydb’ , ‘root’ , ‘123456’);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try{

$pdo->exec(‘insert into students values()’);//错误sql,但系统不会报错

}catch(PDOException $e){

echo ‘sql语句发生错误!<b+r>’;

echo ‘错误文件为:’ . $e->getFile() . ‘<b+r>’;

echo ‘错误行号为:’ . $e->getLine() . ‘<b+r>’;

echo ‘错误描述为:’ . $e->getMessage() . ‘<b+r>’;

die();

}

//似乎抓取PDOException不需要设置set_error_handler

PDOException异常处理

两种方法设置PDOException

初始化PDO时,指定第四个参数,例如:

//初始化PDO时设定错误模式

$drivers = array( //可以设置多种驱动(属性设置) PDO::ATTR_ERRMODE =>PDO::ERRMODE_EXCEPTION ); $pdo = new PDO(‘mysql:host=localhost;port=3306;dbname=mydb’ , ‘ root ‘ , ‘ root ‘ , $drivers);

初始化PDO后,通过PDO的常量PDO::ATTR_ERRMODE设置为PDO::ERRMODE_EXCEPTION,例如:

$pdo=new PDO(‘msyql:host=localhost;port=3306;dbname=mydb’ , ‘root’ , ‘123456’);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

PDOException也可以捕获实例化PDO时的错误

$drivers = array( //可以设置多种驱动(属性设置) PDO::ATTR_ERRMODE =>PDO::ERRMODE_EXCEPTION );

try{

//如果PDO实例化出错,也可以捕捉到.

$pdo = new PDO(‘mysql:host=localhost;port=3306;dbname=unknowndb’ , ‘ root ‘ , ‘ root ‘ , $drivers);

}catch(PDOException $e){

echo ‘sql语句发生错误!<b+r>’;

echo ‘错误文件为:’ . $e->getFile() . ‘<b+r>’;

echo ‘错误行号为:’ . $e->getLine() . ‘<b+r>’;

echo ‘错误描述为:’ . $e->getMessage() . ‘<b+r>’;

die();

}

当语法没问题但业务逻辑有问题,可以主动抛出异常

function my_exception(PDOException $e){ //封装异常处理

echo ‘sql语句发生错误!<b+r>’;

echo ‘错误文件为:’ . $e->getFile() . ‘<b+r>’;

echo ‘错误行号为:’ . $e->getLine() . ‘<b+r>’;

echo ‘错误描述为:’ . $e->getMessage() . ‘<b+r>’;

die();

}

try{

$res= $pdo->exec(‘delete from student where s_id=100’);

if(!$res) throw new PDOException(‘删除失败!’);//如果删除结果为0(没有删除成功)则主动抛出异常

}catch(PDOException $e){

my_exception($e);//自行处理异常

}

PDO预处理

mysql预处理

发送预处理:prepare 预处理名字 from ‘重复执行的sql指令’;

执行预处理:execute 预处理名字

prepare stu_select from ‘select * from student’;//重复执行的sql

execute stu_select;//执行预处理

预处理占位

预处理占位符:sql语句使用?代替未知数据部分

预处理执行using:执行预处理时,将对应数据携带到预处理指令中

prepare stu_select_id from ‘select * from student where s_id=?’; #查询时ID不确定,使用占位符

#查询时确认id

set @id=10; #使用变量.执行预处理时不能直接使用数据,必须通过变量传入

execute stu_select_id using @id; #执行查询

预处理可使用多个占位符

多个占位符必须按顺序匹配

prepare stu_select_age from ‘select * from student where s_age between ? and ?’; #查询年龄区间学生

#执行预处理,提供2个参数

set @min=10;

set @max=20;

execute stu_selectage using @min, @max; #注意变量是按顺序传入预处理的,不可混淆顺序

删除预处理

drop prepare stu_select_age ;

PDO预处理方法机制

思考:其实所有sql指令都可以通过PDO的exec()和query()方法执行.

PDO中预处理提供了—套方法机制,主要由以下几个方法组成 PDO::prepare():发送预处理指令,只需要提供要执行的指令即可,不需要prepare 名字from。成功得到一个PDOStatement类对象,失败得到一个false (或者异常错误) PDOStatement::bindParam():绑定预处理所需要的参数,按引用传递,只能绑定变量(引用传递) PDOStatement::bindvalue():绑定预处理所需要的参数,值传递,可以绑定值(值传递) PDOStatement:.execute():执行预处理,成功返回true,失败返回false

PDO预处理代码示例

$drivers=array(

PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

);

$pdo= new PDO(‘mysql:host=localhost;port=3306;dbname=mydb’, ‘root’, ‘123456’);//初始化PDO

$pre_sql=’select * from student’; //无数据预处理

$pre_sql=’select * from student where s_id= ?’; //MySQL式有参数预处理指令

$pre_sql=’select * from student where s_id=:id’; //PHP PDO特定预处理参数指令(:+字符串),更明确

$stmt=$pdo->prepare($pre_sql);//PDO发送带参数预处理指令

if(!$stmt) die(‘发送预处理指令失败!’); //判断是否错误

$stmt->bindValue(‘:id’, 1);//值传递,直接绑定数据

$id=2;

$stmt->bindValue(‘:id’, $id); //值传递,绑定变量的值

注意:如果发送预处理指令时,使用的是?占位符,那么我们在进行绑定数据的时候,是按照顺序进行绑定的,起始位置的占位符序号为1,如果有多个占位符,依此类推

//预处理指令: select * from student where s_id = ?

$stmt->bindValue(1, $id); //此处1指第一个占位符,将$id按值传递绑定

//以下是按引用传递,只能绑定变量

$stmt->bindParam((‘:id’, $id);

$res=$stmt->execute(); //执行预处理

if (!$res) die(‘预处理执行失败!’);//判断是否错误

$row=$stmt->fetch(PDO::FETCH_ASSOC);//如果是查询,还需要使用PDOStatement::fetch()方法获取查询来的数据

PDO预处理数据绑定

数据绑定,是指在进行预处理指令定义时使用了占位符,为保证后续执行预处理时能够正确执行,将实际数据替换占位符的过程

PDO中,PDOStatement::execute()本身是可以直接进行数据绑定的,即在参数中增加对应的占位符数据,以数组形式传入

//预处理使用原始占位符?

$stmt=$pdo->prepare(‘select * from student where s_id = ?’);

$stmt->execute(array(10)); //array代表数组, 10代表替换值. 如果有多个元素,顺序放入即可

//预处理使用PDO形式占位符:+名字

$stmt=$pdo->prepare(‘select * from students where s_id = :id’);

$stmt->execute(array(‘:id’=>10)); //使用占位符作为数组元素下标

bindValue()和bindParam()区别:值传递和按引用传递

$stmt = $pdo->prepare( ‘select * from student where s_id = :id ‘ ); $id = 1; $stmt->bindvalue( ‘:id’ ,$id);//值传递 for( ; $id < 10; $id++){ $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC) ; } //上述所有结果$row都是s_id为1的数据

$id = 1; $stmt->bindParam( ‘:id’, $id); //按引用传递 for( ; $id < 10; $id++){ $stmt->execute(); $row = $stmt->fetch(PDO : :FETCH_ASSOC); } //上述所有结果$row是s_id从1-9的数据

总结:如果数据本身是变化的,而且有规则的(数组遍历出来的结果),那么可以使用bindParam()按引用传递.

封装PDO

//增加命名空间

//命名空间:PDO通常属于核心类 namespace core; //有了命名空间,PDO3个类不能再直接使用,必须限定名称 use \PDO, \PDOStatement, \PDOException

//定义类

class Dao{ private $pdo; private $fetch_mode;

//增加初始化方法-构造方法

//默认采用PDO异常和获取关联数组设定 public function __construct($database_info = array( ) , $drivers = array()){ //如果要考虑细致,可以看看是否存在 $type = $database_info[ ‘type’ ] ?? ‘mysql’;//默认mysql数据库 $host = $database_info[ ‘host’ ] ?? ‘localhost’; $port = $database_info[ ‘port’ ] ?? ‘3306’; $user = $database_info[ ‘user’ ] ?? ‘root’; $pass = $database_info[ ‘pass’] ?? ‘root’ ; $dbname = $database_info[ ‘dbname’] ?? ‘test’; $charset = $database_info[ ‘charset’ ] ?? ‘utf8’ ; //fetchmode不能在初始化的时候实现,需要在得到PDOStatement类对象设置 $this->fetch_mode = $database_info[ ‘fetch_mode’ ] ?? PDO::FETCH_ASSOC;

//驱动控制:pdo异常模式处理

$drivers[PDO::ATTR_ERRMODE] = $drivers[PDO::ATTR_ERRMODE] ?? PDO::ERRMODE_EXCEPTION; //实例化PDO对象 try{ $this->$pdo = @new PDO($type . ‘:host=’ . $host . ‘;port=’ . $port . ‘;dbname=’ . $dbname, $user, $pass, $drivers); }catch(PDOException $e){ $this->dao_exception($e); } $this->pdo->exec(“set names {$charset}”);//设置字符集-本句也可以用try抓取错误 }

//SQL执行错误的异常处理

private function dao_exception(PDOException $e){ echo ‘SQL执行错误!<b+r/> ‘; echo‘错误文件为:’.$e->getFile(l ‘<b+r/>’; echo‘错误行号为:’. $e->getLine() . ‘<b+r/>’; echo ‘错误描述为:‘. $e->getMessage(); die(); }

//写方法-被外部调用

public function dao_exec($sql){ try{ return $this->pdo->exec($sql); }catch(PDOException $e){ $this->dao_exception($e); } }

//获取自增长ID-被外部调用

public function dao_insert_id(){ return $this->pdo->lastInsertId(); }

//读方法-被外部调用

//读方法:按条件进行单行或者多行数据返回 public function dao_query($sql,$only = true){ try{ $stmt = $this->pdo->query($sql);//设置查询模式 $stmt->setFetchMode($this->fetch_mode ) ; //数据解析 if($only) //考虑到查询结果为0(没有有效结果),我们可主动抛出异常 $row=$stmt->fetch( ); if(!$row) throw new PDOException(‘当前查询没有数据!’);//查询无结果.主动抛出异常 return $row; //有数据返回结果 else $rows=$stmt->fetchAll(); if(!$rows) throw new PDOException(‘当前查询没有数据!’);//查询无结果.主动抛出异常 return $rows; //有数据返回结果 }catch(PDOException $e){ $this->my_exception($e); } } }

//测试

$dao=new Dao(); $res=$dao->dao_query(‘select * from student where age>18’, false); //调用读方法查询多条数据 vardump($res);

总结:以上封装有些草率.因根据具体项目进行实际封装.

作者 申佳明

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code