PHP-PDO介绍
- 2020 年 3 月 26 日
- 笔记
1.2 PDO介绍
1.2.1 连接数据库方式
方法一:mysql扩展【这种方式php7已经淘汰】 方法二:mysqli扩展 方法三:PDO扩展
1.2.2 PDO介绍
PDO(PHP Data Object)扩展为PHP访问各种数据库提供了一个轻量级,一致性的接口。无论访问什么数据库,都可以通过一致性的接口去操作。

1.2.3 开启PDO扩展
开启PDO连接MySQL扩展
extension=php_pdo_mysql.dll
1.3 PDO核心类
1、PDO类:表示PHP和数据库之间的一个连接 2、PDOStatement 类 第一:表示执行数据查询语句(select ,show)后的相关结果集 第二:预处理对象 3、PDOException类:表示PDO的异常

1.4 实例化PDO对象
语法
__construct($dsn,用户名,密码)
1.4.1 DSN
DSN:data source name,数据源名称,包含的是连接数据库的信息,格式如下:
$dsn=数据库类型:host=主机地址;port=端口号;dbname=数据库名称;charset=字符集
数据库类型:
MySQL数据库 => mysql: oracle数据库 => oci: SQL Server =>sqlsrv: 具体驱动类型参见手册“PDO驱动”
1.4.2 实例化PDO
实例化PDO的过程就是连接数据库的过程
<?php $dsn='mysql:host=localhost;port=3306;dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); var_dump($pdo); //object(PDO)#1 (0) { }
1.4.3 注意事项
1、如果连接的是本地数据库,host可以省略
<?php $dsn='mysql:port=3306;dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); var_dump($pdo); //object(PDO)#1 (0) { }
2、如果使用的是3306端口,port可以省略
<?php $dsn='mysql:dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); var_dump($pdo); //object(PDO)#1 (0) { }
3、charset也省略,如果省略,使用的是默认字符编码
<?php $dsn='mysql:dbname=data'; $pdo=new PDO($dsn,'root','root'); var_dump($pdo);
4、dbname也可以省略,如果省略就没有选择数据库
<?php $dsn='mysql:'; $pdo=new PDO($dsn,'root','root'); var_dump($pdo);
5、host、port、dbname、charset不区分大小写,没有先后顺序
6、驱动名称不能省略,冒号不能省略(因为冒号是驱动名组成部分),数据库驱动只能小写
1.5 使用PDO
1.5.1 执行数据操作语句
方法:pdo−>exec(pdo->exec(pdo−>exec(sql),执行数据增、删、改语句,执行成功返回受影响的记录数,如果SQL语句错误返回false。
<?php //1、实例化PDO $dsn='mysql:host=localhost;port=3306;dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); //2执行数据操作语句 //2.1 执行增加 /* if($pdo->exec("insert into news values (null,'bb','bbbbbb',unix_timestamp())")) echo '自动增长的编号是:'.$pdo->lastInsertId (),'<br>'; */ //2.2 执行修改 //echo $pdo->exec("update news set title='静夜思' where id in (3,4)"); //2.3 执行删除 //echo $pdo->exec('delete from news where id=5'); //2.4 完善 $sql="update news set title='静夜思1' where ids in (3,4)"; $rs=$pdo->exec($sql); if($rs){ echo 'SQL语句执行成功<br>'; if(substr($sql, 0,6)=='insert') echo '自动增长的编号是:'.$pdo->lastInsertId (),'<br>'; else echo '受到影响的记录数是:'.$rs,'<br>'; }elseif($rs===0){ echo '数据没有变化<br>'; }elseif($rs===false){ echo 'SQL语句执行失败<br>'; echo '错误编号:'.$pdo->errorCode(),'<br>'; //var_dump($pdo->errorInfo()); echo '错误信息:'.$pdo->errorInfo()[2]; }
1.5.2 执行数据查询语句
方法:pdo−>query(pdo->query(pdo−>query(sql),返回的是PDOStatement对象
<?php $dsn='mysql:dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); //1、执行数据查询语句 $stmt=$pdo->query('select * from products'); //var_dump($stmt); //object(PDOStatement) //2、获取数据 //2.1 获取二维数组 //$rs=$stmt->fetchAll(); //默认返回关联和索引数组 //$rs=$stmt->fetchAll(PDO::FETCH_BOTH); //返回关联和索引数组 //$rs=$stmt->fetchAll(PDO::FETCH_NUM); //返回索引数组 //$rs=$stmt->fetchAll(PDO::FETCH_ASSOC); //返回关联数组 //$rs=$stmt->fetchAll(PDO::FETCH_OBJ); //返回对象数组 //2.2 获取一维数组,匹配完成后指针下移一条 //$rs=$stmt->fetch(); //关联和索引数组 //$rs=$stmt->fetch(PDO::FETCH_NUM); //索引数组 //例题:通过while循环获取所有数据 /* while($row=$stmt->fetch(PDO::FETCH_ASSOC)){ $rs[]=$row; } echo '<pre>'; var_dump($rs); */ //3.3 匹配列:匹配当前行的第n列,列的编号从0开始,匹配完毕后指针下移一条 //echo $stmt->fetchColumn(); //获取当前行的第0列 //echo $stmt->fetchColumn(1); //获取当前行的第1列 //3.4 总行数,总列数 /* echo '总行数:'.$stmt->rowCount(),'<br>'; echo '总列数:'.$stmt->columnCount(); */ //3.5 遍历PDOStatement对象(PDOStatement对象是有迭代器的) foreach($stmt as $row){ echo $row['proname'],'-',$row['proprice'],'<br>'; }
stdClass类是所有PHP类的父类
1.5.3 PDO操作事务
事务:是一个整体,要么一起执行,要么一起回滚
事务的特性:原子性,一致性,隔离性,永久性
需要将多个SQL语句作为一个整体执行,就需要使用到事务
语法
start transaction 或 begin 开启事务 commit 提交事务 rollback 回滚事务
例题
创建测试数据
create table bank( cardid char(4) primary key comment '卡号', balance decimal(10,2) not null comment '余额' )engine=innodb charset=utf8 comment '银行卡号表' insert into bank values ('1001',1000),('1002',1)
PDO操作事务
<body> <?php if(!empty($_POST)){ $dsn='mysql:dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); $out=$_POST['card_out']; //转出卡号 $in=$_POST['card_in']; //注入卡号 $money=$_POST['money']; //金额 $pdo->beginTransaction(); //开启事务 //转账 $flag1=$pdo->exec("update bank set balance=balance-$money where cardid='$out'"); $flag2=$pdo->exec("update bank set balance=balance+$money where cardid='$in'"); //查看转出的账号是否大于0,大于0返回true,否则返回false $stmt=$pdo->query("select balance from bank where cardid='$out'"); $flag3=$stmt->fetchColumn()>=0?1:0; if($flag1 && $flag2 && $flag3){ $pdo->commit (); //提交事务 echo '转账成功'; } else{ $pdo->rollBack (); //回滚事务 echo '转账失败'; } } ?> <form action="" method="post"> 转出卡号: <input type="text" name="card_out" id=""> <br> 转入卡号: <input type="text" name="card_in" id=""> <br> 金额:<input type="text" name="money" id=""> <br> <input type="submit" value="提交"> </form> </body>
运行结果

小结:
$pdo->beginTransaction() 开启事务 $pdo->commit () 提交事务 $pdo->rollBack() 回滚事务
1.5.4 PDO操作预处理
复习MySQL中预处理
预处理好处:编译一次多次执行,用来解决一条SQL语句多次执行的问题,提高了执行效率。
预处理语句:
prepare 预处理名字 from 'sql语句'
执行预处理
execute 预处理名字 [using 变量]
PDO中的预处理——位置占位符
<?php $dsn='mysql:dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); //创建预处理对象 $stmt=$pdo->prepare("insert into bank values (?,?)"); //?是占位符 //执行预处理 $cards=[ ['1003',500], ['1004',100] ]; foreach($cards as $card){ //绑定参数,并执行预处理, //方法一: /* $stmt->bindParam(1, $card[0]); //占位符的位置从1开始 $stmt->bindParam(2, $card[1]); $stmt->execute(); //执行预处理 */ //方法二: /* $stmt->bindValue(1, $card[0]); $stmt->bindValue(2, $card[1]); $stmt->execute(); */ //方法三:如果占位符的顺序和数组的顺序一致,可以直接传递数组 $stmt->execute($card); }
PDO中的预处理——参数占位符
<?php $dsn='mysql:dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); //创建预处理对象 $stmt=$pdo->prepare("insert into bank values (:p1,:p2)"); //:p1,:p2是参数占位符 //执行预处理 $cards=[ ['p1'=>'1003','p2'=>500], ['p1'=>'1004','p2'=>1000] ]; foreach($cards as $card){ //方法一: /* $stmt->bindParam(':p1', $card['p1']); $stmt->bindParam(':p2', $card['p2']); $stmt->execute(); */ //方法二:但数组的下标和参数名一致的时候就可以直接传递关联数组 $stmt->execute($card); }
小结:
1、?是位置占位符
2、参数占位符以冒号开头
3、stmt−>bindParam()和stmt->bindParam()和stmt−>bindParam()和stmt->bindValue()区别

4、预处理的好处
a)提高执行效率 b)提高安全性
1.6 PDO异常处理
<?php try{ $dsn='mysql:dbname=data;charset=utf8'; $pdo=new PDO($dsn,'root','root'); //这是PDO错误模式属性,PDO自动抛出异常 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->query('select * from newsssssss'); //自动抛出异常 } catch (PDOException $ex) { echo '错误信息:'.$ex->getMessage(),'<br>'; echo '错误文件:'.$ex->getFile(),'<br>'; echo '错误行号:'.$ex->getLine(); }
小结:
1、PDOException是PDO的异常类
2、实例化PDO会自动抛出异常
3、其他操作不会抛出异常,需要设置PDO的异常模式
4、PDO异常模式
PDO::ERRMODE_EXCEPTION 抛出异常 PDO::ERRMODE_SILENT 中断 PDO::ERRMODE_WARNING 警告
1.7 单例模式封装MyPDO类
1.7.1 步骤
1、单例模式
2、初始化参数
3、连接数据库
4、执行增删改
5、执行查询
a)返回二维数组
b)返回一维数组
c)返回一行一列
1.7.2 代码实现
第一部分:单例、初始化参数、实例化PDO
<?php class MyPDO{ private $type; //数据库类别 private $host; //主机地址 private $port; //端口号 private $dbname; //数据库名 private $charset; //字符集 private $user; //用户名 private $pwd; //密码 private $pdo; //保存PDO对象 private static $instance; private function __construct($param) { $this->initParam($param); $this->initPDO(); } private function __clone() { } public static function getInstance($param=array()){ if(!self::$instance instanceof self) self::$instance=new self($param); return self::$instance; } //初始化参数 private function initParam($param){ $this->type=$param['type']??'mysql'; $this->host=$param['host']??'127.0.0.1'; $this->port=$param['port']??'3306'; $this->dbname=$param['dbname']??'data'; $this->charset=$param['charset']??'utf8'; $this->user=$param['user']??'root'; $this->pwd=$param['pwd']??'root'; } //初始化PDO private function initPDO(){ try{ $dsn="{$this->type}:host={$this->host};port={$this->port};dbname={$this->dbname};charset={$this->charset}"; $this->pdo=new PDO($dsn, $this->user, $this->pwd); } catch (PDOException $ex) { echo '错误编号:'.$ex->getCode(),'<br>'; echo '错误行号:'.$ex->getLine(),'<br>'; echo '错误文件:'.$ex->getFile(),'<br>'; echo '错误信息:'.$ex->getMessage(),'<br>'; exit; } } } //测试 $param=array( ); $mypdo= MyPDO::getInstance($param); var_dump($mypdo);
第二部分:数据操作部分
<?php class MyPDO{ private $type; //数据库类别 private $host; //主机地址 private $port; //端口号 private $dbname; //数据库名 private $charset; //字符集 private $user; //用户名 private $pwd; //密码 private $pdo; //保存PDO对象 private static $instance; private function __construct($param) { $this->initParam($param); $this->initPDO(); $this->initException(); } private function __clone() { } public static function getInstance($param=array()){ if(!self::$instance instanceof self) self::$instance=new self($param); return self::$instance; } //初始化参数 private function initParam($param){ $this->type=$param['type']??'mysql'; $this->host=$param['host']??'127.0.0.1'; $this->port=$param['port']??'3306'; $this->dbname=$param['dbname']??'data'; $this->charset=$param['charset']??'utf8'; $this->user=$param['user']??'root'; $this->pwd=$param['pwd']??'root'; } //初始化PDO private function initPDO(){ try{ $dsn="{$this->type}:host={$this->host};port={$this->port};dbname={$this->dbname};charset={$this->charset}"; $this->pdo=new PDO($dsn, $this->user, $this->pwd); } catch (PDOException $ex) { $this->showException($ex); exit; } } //显示异常 private function showException($ex,$sql=''){ if($sql!=''){ echo 'SQL语句执行失败<br>'; echo '错误的SQL语句是:'.$sql,'<br>'; } echo '错误编号:'.$ex->getCode(),'<br>'; echo '错误行号:'.$ex->getLine(),'<br>'; echo '错误文件:'.$ex->getFile(),'<br>'; echo '错误信息:'.$ex->getMessage(),'<br>'; } //设置异常模式 private function initException(){ $this->pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); } //执行增、删、改操作 public function exec($sql){ try{ return $this->pdo->exec($sql); } catch (PDOException $ex) { $this->showException($ex, $sql); exit; } } //获取自动增长的编号 public function lastInsertId(){ return $this->pdo->lastInsertId(); } } //测试 $param=array( ); $mypdo= MyPDO::getInstance($param); //echo $mypdo->exec('delete from news where id=6'); if($mypdo->exec("insert into news values (null,'11','1111',unix_timestamp())")) echo '自动增长的编号是:'.$mypdo->lastInsertId ();
第三部分:数据查询部分
<?php class MyPDO{ ... //判断匹配的类型 private function fetchType($type){ switch ($type){ case 'num': return PDO::FETCH_NUM; case 'both': return PDO::FETCH_BOTH; case 'obj': return PDO::FETCH_OBJ; default: return PDO::FETCH_ASSOC; } } //获取所有数据 ,返回二维数组 public function fetchAll($sql,$type='assoc'){ try{ $stmt=$this->pdo->query($sql); //获取PDOStatement对象 $type= $this->fetchType($type); //获取匹配方法 return $stmt->fetchAll($type); } catch (Exception $ex) { $this->showException($ex, $sql); } } //获取一维数组 public function fetchRow($sql,$type='assoc'){ try{ $stmt=$this->pdo->query($sql); //获取PDOStatement对象 $type= $this->fetchType($type); //获取匹配方法 return $stmt->fetch($type); } catch (Exception $ex) { $this->showException($ex, $sql); exit; } } //返回一行一列 public function fetchColumn($sql){ try{ $stmt=$this->pdo->query($sql); return $stmt->fetchColumn(); } catch (Exception $ex) { $this->showException($ex, $sql); exit; } } } //测试 $param=array( ); $mypdo= MyPDO::getInstance($param); //echo $mypdo->exec('delete from news where id=6'); /* if($mypdo->exec("insert into news values (null,'11','1111',unix_timestamp())")) echo '自动增长的编号是:'.$mypdo->lastInsertId (); */ //$list=$mypdo->fetchAll('select * from news'); //$list=$mypdo->fetchRow('select * from news where id=1'); $list=$mypdo->fetchColumn('select count(*) from news'); echo '<pre>'; var_dump($list);