10 php面向对象高级PDO和MVC模式04

介绍

PDO操作手册:
https://www.php.net/manual/zh/intro.pdo.php

菜鸟教程:
https://www.runoob.com/php/php-pdo.html

PDO扩展,即PDO提供了一套帮助用户实现多数据库操作的统一接口,通过使用PDO,开发人员不需要额外自定义数据库对应的操作类,从而简化开发过程。

PDO是一种外部提供的扩展,PHP如果想直接调用,需要加载扩展

PDO是PHP Data Object的简称,代表PHP数据对象,是一种纯面向对象的方式实现的数据库操作扩展。

  • PDO类:统一的数据库的初始化操作,包括连接认证和执行SQL指令
  • PDOStatement类:数据解析操作,主要针对数据结果操作(有数据结果返回)
  • PDOException类:异常处理操作,针对所有PDO操作可能出现的错误采用的异常模式处理

开启PDO配置

  1. PDO在PHP5以前,是需要手动加载扩展的,在php.ini配置文件中,会有这么一个扩展(在PHP5以后就自动加载了不需要手动再加载)
;extension = pdo				#现在php.ini中找不到该配置项了
  1. PDO是一种针对多种数据库的扩展,那就意味着PDO很庞大,所以为了保证PHP运行的效率(系统会自动在开始时加载扩展),默认并没有开启对数据库产品的对应扩展,因此还需要在php.ini中开启目前项目所需要的PDO对应数据库产品的支持
;extension=pdo_firebird
;extension=pdo_mysql			#绝大部分项目使用的,只要去掉注释重启Apache即可
;extension=pdo_oci
;extension=pdo_odbc
;extension=pdo_pgsql
;extension=pdo_sqlite
  1. 加载PDO对mysql数据库产品的扩展,重启Apache,然后通过phpinfo()函数就可以查看PDO的加载情况了,至少有两个加载才算成功:PDO本身和PDO针对的数据库产品
http://www.sghc.com/class/app/phpinfo.php 查看是否配置成功

image

PDO类基本应用

PDO是PHP Data Object的简称,代表PHP数据对象,是一种纯面向对象的方式实现的数据库操作扩展

常用方法

  • PDO::__construct():实例化PDO对象

  • PDO::exec():执行一个写操作SQL指令,返回受影响的行数,如果没有影响,返回是0

  • PDO::query():执行一个读操作SQL指令,返回一个PDOStatement类对象(后者进行数据解析操作),如果没有返回则是false

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

实例化对象和使用

PDO类主要用来实现数据库的初始化操作和SQL指令的执行

PDO实例化对象:利用其构造方法__construct(string $dsn,string $user,string $pass[,array $drivers])实现,构造方法由4个参数组成,其中一般前三个为必须参数,第四个为可选参数

  • $dsn:一个数据库基本信息字符串,包含数据库产品,主机地址等
    • 驱动名字(数据库产品),使用英文:分隔,如mysql:表示使用mysql数据库
    • 驱动选项(主机地址),使用host=具体主机地址,跟在驱动名字之后,如'mysql:host=localhost'
    • 驱动选项(端口),使用port=端口号,默认为3306可以不写,拼凑在驱动名字后,不区分先后顺序。如'mysql:host=localhost;port=3306'或者'mysql:port=3306;host=localhost',使用分号和其他驱动分开
    • 驱动选项(数据库名字),使用dbname=数据库名字(可以事先没有)
  • $user:用户名,如果数据库允许匿名用户出现,那么可以没有该参数(只有$dsn)
  • $pass:密码,与用户名一样
  • $drivers:PDO属性设置,是关联数组,利用PDO内部的常量进行设置
<?php
// 方案1:直接写入数据进行数据库初始化
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=my_database','root','root');

//方案2:利用变量保存数据来实现数据库初始化(数据来源可以是其他配置文件:安全)
$dsn = 'mysql:host=localhost;dbname=my_database';
$user = 'root';
$pass = 'root';
$pdo = new PDO($dsn,$user,$pass);

得到对象后,我们就可以利用PDO对象调用相关方法执行SQL操作:exec执行数据返回SQL,query执行有结果返回SQL

<?php
// 1. 库连接-实例化PDO对象
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=mydb', 'root', 'root');

// 2. 操作得到对象后,我们就可以利用PDO对象调用相关方法执行SQL操作:exec执行数据返回SQL,query执行有结果返回SQL

// 2.1 读操作
$sql = "SELECT * from class";
$stmt = $pdo->query($sql);  // 成功返回PDOStatement类对象,失败得到false
var_dump($stmt);

// 2.2 写操作
$sql = "INSERT INTO class VALUES(DEFAULT,'八班')";
$rows = $pdo->exec($sql);    //得到受影响的行数:执行失败返回0


// 如果PDO对象在执行SQL时出现错误,即SQL本身有错误,那么可以通过PDO类提供的errorCode()和errorInfo()

//错误判定:exec方法执行结果成功也存在返回0的情况,错误会返回false,所以要判定是否是SQL错误,需要判定结果为false
if (false === $rows) {
  //取出错误细信息
  echo 'SQL错误:<br/>';
  echo '错误代码为:' . $pdo->errorCode() . '<br/>';
  echo '错误原因为:' . $pdo->errorInfo()[2];
  //errorInfo返回数组,2下标代表错误具体信息
  exit;
  //错误不需要继续执行代码
}

写操作exec

写操作,即利用PDO,实现数据库增删改操作,操作过程中要考虑到SQL本身可能出现的错误处理,以及对结果的操作处理。

<?php
// 写操作:增 删 改

// 1. 初始化PDO:增加错误抑制符,一致在初始化过程可能出现的错误(信息不对连接不上)
function pdo_init()
{
  $pdo = @new PDO('mysql:host=localhost;port=3306;dbname=mydb', 'root', 'root');
  if (!$pdo) {
    exit('数据库连接认证失败!');
  }

  //字符集处理
  $pdo->exec('set names 字符集');

  //返回得到的PDO对象
  return $pdo;
}

// 2. SQL通常应该是外部传入,外部需要的只是结果,不考虑过程,所以在实际开发时要考虑二次封装。即外部传入SQL,内部执行并控制错误,最终返回结果
function pdo_exec($pdo, $sql)
{
  // 调用PDO对象的方法执行写SQL
  $res = $pdo->exec($sql);
  //错误判定
  if (false === $res) {
    //取出错误细信息
    echo 'SQL错误:<br/>';
    echo '错误代码为:' . $pdo->errorCode() . '<br/>';
    echo '错误原因为:' . $pdo->errorInfo()[2];
    exit;
  }

  //返回执行结果:受影响的行数
  return $res;
}


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

// 组织要执行的SQL
$sql = "INSERT INTO class VALUES(DEFAULT,'99班级')";
$res = pdo_exec($pdo, $sql);
var_dump($res);


// 一般写操作都是受影响行数,但是如果是插入操作,有时候需要新增记录的自增长ID,可以通过PDO::lastInsertId()来获取
$id = $pdo->lastInsertId();  //实际开发也应封装
echo '新增成功!新增的ID为:' . $id;

查询操作query

查询操作,即通过执行SQL指令后从数据库获得相应的数据,然后对数据加工编程PHP可识别的

<?php
// 写操作:增 删 改

// 1. 初始化PDO:增加错误抑制符,一致在初始化过程可能出现的错误(信息不对连接不上)
function pdo_init()
{
  $pdo = @new PDO('mysql:host=localhost;port=3306;dbname=mydb', 'root', 'root');
  if (!$pdo) {
    exit('数据库连接认证失败!');
  }

  //字符集处理
  $pdo->exec('set names 字符集');

  //返回得到的PDO对象
  return $pdo;
}

// 2. SQL通常应该是外部传入,外部需要的只是结果,不考虑过程,所以在实际开发时要考虑二次封装。即外部传入SQL,内部执行并控制错误,最终返回结果
function pdo_query($pdo, $sql)
{
  // 调用PDO对象的方法执行写SQL
  $stmt = $pdo->query($sql);
  //错误判定
  if (false === $stmt) {
    //取出错误细信息
    echo 'SQL错误:<br/>';
    echo '错误代码为:' . $pdo->errorCode() . '<br/>';
    echo '错误原因为:' . $pdo->errorInfo()[2];
    exit;
  }

  //返回执行结果:受影响的行数
  return $stmt;
}

// 处理结果  $only默认是显示一条数据集
function pdo_get($stmt, $only = true, $fetch_style = PDO::FETCH_ASSOC)
{
  //$stmt是PDO查询得到的对象,$only代表默认只获取一条记录

  //PDOStatement类中提供了两种方法分别去获取一条和多条记录
  // 此时查询出来的结果是一个对象,不能提供任何PHP可访问的数据结果,还需要对结果进行处理。而我们查询数据的时候通常是两种操作:根据具体条件获取一条记录或者多条记录,因此返回的数据是不同的。解决方案也有多种方式:①创建多个函数来实现不同效果;②创建一个函数,但是通过参数来控制实现不同效果
  if ($only) {
    //获取一条记录PDOStatement::fetch():返回一维数组
    return $stmt->fetch($fetch_style);
  } else {
    //获取多条记录PDOStatement::fetchAll():返回二维数组,一个维度代表一条记录
    return $stmt->fetchAll($fetch_style);
  }
}


// 1. 初始化库连接
$pdo = pdo_init();

// 2. 组织要执行的SQL
$sql = "SELECT * from class";

// 3. 执行查询
$stmt = pdo_query($pdo, $sql);

// 4. 处理查询结果:获取多条记录
$res = pdo_get($stmt, false);

var_dump($res);

PDO事务

PDO事务功能,是将原来MySQL所支持的事务操作进行了一定的封装实现。事务执行是否成功是由MySQL对应的存储引擎是否支持决定的。

mysql事务

事务是指改变默认的一次操作一次写入数据表的机制,而是通过事务日志记录操作,在最后通过一次性操作写入到数据表

事务的本质是由既定SQL指令完成,而事务具体操作内容一定是外部指定的SQL(写操作)来处理

事务的过程:

  • 开启事务:start transaction,写操作停止直接写入数据表,而是记录到事务日志
  • 事务操作:具体的写操作,通常多个步骤多条指令
  • 提交事务:即事务操作结束
    • 成功提交:commit,所有事务日志内容同步到数据表,并清空当前事务日志
    • 失败回滚:rollback,直接清空当前事务日志

PDO事务

PDO类中提供一套方案来实现事务操作

  • PDO::beginTransaction():开启事务
  • PDO::exec():执行事务(写操作)
  • PDO::rollBack():回滚所有事务
  • PDO::commit():成功提交所有事务
<?php
//实例化PDO对象
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=mydb','root','root');

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

//执行事务
$pdo->exec('insert into student values()');

//终止事务
$pdo->commit();				//成功提交
$pdo->rollback();			//失败回滚

在事务操作中,有一种回滚点机制,在PDO中没有实现,如果有必要,可以通过SQL指令设置来实现

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

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

//执行事务
$pdo->exec('insert into student values()');

//设置回滚点
$pdo->exec('savepoint sp1');

//继续执行事务...

//回滚
$pdo->exec('rollback to sp1');

//终止事务
$pdo->commit();				//成功提交
$pdo->rollback();			//失败回滚

PDO异常

异常机制

异常exception是面向对象的一种错误处理机制,它允许开发人员将可能出现的错误被对象(Exception类)捕捉,然后在特定位置通过该对象来进行处理

捕获异常 throw new Exception

//外部传入数据:不确定
$n1 = 10;
$n2 = 5;
//要求$n1 / $n2
if($n2 == 0){
    //被除数为0,不能操作:抛出异常
    throw new Exception('被除数不能为0!');
}

//没有问题继续执行
$res = $n1 / $n2;

错误机制

PDO错误机制,是指PDO在使用过程中出现了错误(大多是SQL指令执行错误)的时候,PDO处理错误的方式
PDO中提供了三种错误机制,静默模式、警告模式和异常模式,默认是静默模式,是通过PDO的常量PDO::ATTR_ERRMODE来选择的

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

PDO::ERRMODE_SILENT静默模式

$pdo = new PDO('mysql:host=localhost;port=3306;dbname=project','root','root');
$pdo->exec('insert into student values()');		//错误:但是不会报错

PDO::ERRMODE_WARNING警告模式

PDO可以通过PDO::setAttribute()方法来设定错误处理模式

$pdo = new PDO('mysql:host=localhost;port=3306;dbname=project','root','root');
//设定处理模式为警告模式
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);
$pdo->exec('insert into student values()');		//错误:系统直接报错

PDO::ERRMODE_EXCEPTION异常模式

PDO异常模式是通过PDOException来捕捉异常

$pdo = new PDO('mysql:host=localhost;port=3306;dbname=project','root','root');
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
try{
    $pdo->exec('insert into student values()');	//错误:被捕捉到
}catch(PDOException $e){
    //进入到异常处理
}

PDOException异常处理

PDOException,是PDO从Exception继承的一个用于处理PDO错误的异常类。一般如果做一套扩展,都会从去继承Exception类来实现一套明确的错误机制(错误来自哪里)

  1. 要使用PDOException异常处理,需要设定PDO的错误模式为异常模式。在PDO中可以通过两种方法来实现异常模式的设定
<?php
// 方法1. 在初始化PDO对象的时候,利用第四个参数来设定
$drivers = array(
	//可以设置多种驱动(属性设置)
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=mydb','root','root',$drivers);


// 方法2. 使用属性设置
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=mydb','root','root');
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
  1. 使用异常来进行捕捉错误(通常是进行SQL执行的时候)
<?php
// 1. 设定异常模式
$drivers = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=project','root','root',$drivers);

// 执行SQL:捕获异常错误
try{
    $pdo->exec('set names utf-8');		//错误
}catch(PDOException $e){
    echo 'SQL执行错误!<br/>';
    echo '错误文件为:' . $e->getFile() . '<br/>';
    echo '错误行号为:' . $e->getLine() . '<br/>';
    echo '错误描述为:' . $e->getMessage();
    die();
}
  1. PDO执行过程中,如果碰到了意外(逻辑无法继续执行),那么也可以主动抛出异常
<?php
$drivers = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);

//实例化PDO
try{
	$pdo = new PDO('mysql:host=localhost;port=3306;dbname=project','root','root',$drivers);
}catch(PDOException $e){
    echo '数据库连接失败!<br/>';
    echo '错误文件为:' . $e->getFile() . '<br/>';
    echo '错误行号为:' . $e->getLine() . '<br/>';
    echo '错误描述为:' . $e->getMessage();
    die();
}

//因为异常使用比较多,我们可以对异常处理进行封装
function my_exception(PDOException $e){	//强类型
    echo 'SQL执行失败!<br/>';
    echo '错误文件为:' . $e->getFile() . '<br/>';
    echo '错误行号为:' . $e->getLine() . '<br/>';
    echo '错误描述为:' . $e->getMessage();
    die();
}

//抓异常
try{
    $res = $pdo->exec('delete from student where s_id = 100');
    if(!$res) throw new PDOException('删除失败!');
}catch(PDOException $e){
    my_exception($e);
}

预处理

SQL的执行从数据库端来讲,都是一条一条执行的。SQL在MySQL服务器端执行的逻辑是先编译后执行的,意味着即便是同一条SQL,每次发送到服务器端,都会经历编译、执行的过程,如果SQL量特别大,那么效率就会打折扣。为此,MySQL提供了一套预处理机制,可以实现效率的提升

1. MYSQL预处理

预处理prepare,是指客户端将要执行的SQL先发送给服务器,服务器先进行编译,不执行。等到客户端需要服务器端执行的时候,发送一条执行指令,让服务器再执行已经提前处理好(预处理)的SQL指令

  • 预处理prepare是MySQL提供的一套可以优化服务器重复执行SQL的方法,可以提升服务器的性能
  • 预处理是一套提前让服务器编译的机制,一次编译多次执行,节省后续编译的成本
  • 处理分为两个步骤:发送预处理指令 prepare 预处理名字 from '预处理SQL';执行预处理指令execute 预处理名字
  • 预处理可以进行数据更换执行,利用预处理指令的占位符“?”和执行时携带数据using(数据必须是变量)
  • 同一个客户端不允许设定同名预处理,因此在执行预处理的时候,如果不用了应该删掉
  • 预处理不只是针对查询操作,增删改查操作都可以使用预处理(因为查询最多,故以查询为例)

image
image

实现预处理:预处理的步骤最开始会比普通SQL执行多一步,但是后续会节省服务器端的响应时间,提升服务器端的服务效率。
预处理操作步骤如下:

  • 发送预处理:prepare 预处理名字 from '要重复执行的SQL指令';
  • 执行余出来:execute 预处理名字
<?php
# 要执行重复的语句:从学生表中查询所有数据
prepare student_select from 'select * from student';

# 执行余出来
execute student_select;

预处理占位
如果一条SQL本身就是重复多次,那么还不见得预处理有什么优势,毕竟这样的固定操作并不是特别多,更多的时候是需要条件变化的,因此预处理可以进行预处理占位,在执行的时候把数据填入,即可实现不同的SQL查询结果

  • 预处理占位符:在预处理指令中要执行的SQL指令,使用?来代替未知数据部分
  • 预处理执行(using):在执行预处理的时候将对应的数据携带到预处理指令中
<?php
# 执行重复的语句:从学生表中按学生ID查询学生信息:ID不确定,所以使用?占位
prepare student_select_id from 'select * from student where s_id = ?';

# 执行预处理:预处理数据不能直接使用数据常量代入,需要通过变量传入
set @id = 1;			# 设置变量
execute student_select_id using @id;

预处理可以同时设定多个占位符,在执行预处理的时候传入对应的参数即可(顺序匹配)

<?php
# 查询年龄区间的学生信息
prepare student_select_age from 'select * from student where s_age between ? and ?';

# 执行预处理,提供两个参数
set @min = 10;
set @max = 20;
execute student_select_age using @min,@max;

删除预处理

<?php
# 语法:drop prepare 预处理名字;

# 删除预处理
drop prepare student_select_age;

2. PDO预处理

PDO预处理,是PDO封装一套特定的方法,在方法中做了一些优化操作,使得开发人员可以便捷的用来实现预处理

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

2. PDO发送预处理指令
即利用PDO::prepare()方法将要执行的SQL指令先发送给服务器编译

<?php
//实例化PDO对象
$drivers = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$pdo = new PDO('mysql:host=localhost;port=3306;dbname=my_database','root','root',$drivers);


//发送预处理指令
$pre_sql = "select * from student";					//无数据预处理指令
$pre_sql = "select * from student where s_id = ?";	//有参数预处理指令
$pre_sql = "select * from student where s_id = :id";//PDO特定预处理参数指令(: + 字符串),更明确

//执行最后一条预处理指令:即使用:id作为参数的
$stmt = $pdo->prepare($pre_sql);
if(!$stmt) die('预处理指令执行失败!');

3. 绑定预处理参数
如果预处理本身是需要携带参数的,那么可以使用PDOStatement::bindValue()/bindParam()进行参数绑定

<?php
# 方式一: PDOStatement::bandValue()
# 直接绑定数值
$stmt->bindValue(':id',1);

# 绑定变量数据
$id = 2;
$stmt->bindValue(':id',$id);

# 如果在发送预处理指令的时候,使用的是“?”作为占位符,那么我们在进行绑定数据的时候,是按照顺序进行绑定的,起始位置的占位符序号为1,如果有多个占位符,依次类推
//预处理指令:select * from student where s_id = ?
$stmt->bindValue(1,$id);


# 方式二:PDOStatement::bindParam()
//接预处理指令
//$stmt->bindParam(':id',3);		//错误:不能绑定值,引用传递,只能是变量
$id = 4;
$stmt->bindParam(':id',$id);		//必须是传递变量	

PDOStatement::bindValue()和PDOStatement::bindParam()区别

  • 二者都可以实现占位符的数据绑定

  • bindValue()绑定数据的方式灵活,可以是变量也可以是数据常量;而bindParam()只能是变量

  • 因为bindParam绑定的是变量(引用传值),所以如果被绑定的变量发生改变,会直接影响后续execute结果

4. 执行预处理
利用PDOStatement::execute()方法

//接上述代码
$res = $stmt->execute();
if(!$res) die('预处理执行失败!');

//如果是查询,想要得到预处理执行的结果,还需要使用PDOStatement::fetch()方法进行数据解析
$row = $stmt->fetch(PDO::FETCH_ASSOC);
posted @   songxia777  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示