一、自定义异常类处理异常:
用框架写代码,框架中输出的很多错误信息,比如说:控制器找不到、数据类型错误或者数据结果集没有获取、SQL语句错误等等,这些错误信息的提示都是框架的开发者重写了系统的异常类进行输出的,不会直接输出系统的提示信息,样式不行。
如果能将系统的异常进行自定义处理,写出来的代码逼格显得逼格很高
<?php
// 三、自定义异常类处理
// Exception 类是可以进行扩展的
// Exception 中只有__construct()和__toString()允许扩展,其它都是最终方法final,不允许扩展
// 下面我们来创建一个自己的异常类, 专用于处理算术运算中可能出现的错误
// 基本要求是: 错误代码加粗, 提示文本描红, 并要求换行显示
// 处理运行中的错误
namespace jiangshi\ouyangke;
// 异常进行错误的统一处理
// 异常类是PHP中专用于错误 信息的统一处理
use Exception;
// 将系统的异常类进行扩展,自定义异常提示信息
class CalException extends Exception {
// CalException继承了系统的异常类,构造方法不能被继承,应该重写
public function __construct($message = "", $code = 0) {
// 第一个参数错误信息,第二个默认的代码
parent::__construct($message, $code);
}
// 自定义错误提示信息,对错误信息的输出格式进行自定义
public function errorInfo() {
// errorInfo()也是父类方法,进行重写
// heredoc语法:用来输出大段的HTML代码或字符串,并且中间允许有变量且会解析
// 以 <<< 开头表示 这种语法
return // 输出错误代码 :<strong>{$this->getCode()} 后面跟上错误信息 $this->getMessage()
<<< ERROR
<h2>
<strong>{$this->getCode()}:
<span style="color: red">{$this->getMessage()}</span>
</h2>
ERROR;
}
}
// 以try 块中,所有的 Exception 类全部替换成 CalException 类即可
// try 中放上要处理的代码 然后把所有的错误信息放到 catch中进行处理
try {
class Calculator {
protected $defaultOperators = ['+', '-', '*', '/', '%'];
protected $result;
public function __construct(...$operators) {
// 判断操作是否在有效范围内
foreach ($operators as $operator) {
if (in_array($operator, $this->defaultOperators)) {
continue;
} else {
// die('操作符错误');
throw new CalException('操作符错误', 101);
}
}
// 更新操作符类型
$this->defaultOperators = $operators;
}
// 运算方法
public function operation($type, ...$args) {
// 判断操作符合法性
if (in_array($type, $this->defaultOperators)) {
$num = count($args);
switch ($num) {
case 0:
throw new CalException('参数不能为空', 102);
// die('参数不能为空');
break;
case ($num < 2):
// die('参数不能少于2个');
throw new Exception('参数不能少于2个', 103);
break;
default:
// 用参数数组的第一个值初始化$this->result属性,该属性保存着返回值
// 同时, $this->>result 也做为所有运算的第一个操作数
$this->result = array_shift($args);
if (is_numeric($this->result)) {
$this->execute($type, ...$args);
} else {
// die('第一个参数必须数值型');
throw new CalException('第一个参数必须数值型', 104);
}
}
} else {
// die('操作类型错误');
throw new CalException('操作类型错误', 105);
}
// 运算结果四舍五入(保留小数点后2位)
return round($this->result, 2);
}
// 运算执行器方法
protected function execute($type, ...$args) {
foreach ($args as $arg) {
// 判断当前操作数是否数字/可转为数字的字符串
if (is_numeric($arg)) {
switch ($type) {
case '+': // 加法
$this->result += $arg;
break;
case '-': // 减法
$this->result -= $arg;
break;
case '*': // 乘法
$this->result *= $arg;
break;
case '/': // 除法
if ($arg !== 0) {
$this->result /= $arg;
break;
} else {
// die('除数不能为0');
throw new CalException('除数不能为0', 106);
}
case '%': // 取模
$this->result %= $arg;
break;
}
} else {
// die('操作数类型错误');
throw new CalException('操作数类型错误', 107);
}
}
}
}
// 实例化, 并自定义允许进行的操作类型
$calculator = new Calculator('+', '-', '*', '/');
echo '<per>';
// echo $calculator->operation('*');
// echo $calculator->operation('*',30);
// echo $calculator->operation('*','abc',999);
// echo '<hr>';
echo $calculator->operation('*', '379', 200);
echo '<hr>';
echo $calculator->operation('/', 678, 234);
} catch (CalException $e) {
// 使用异常,将执行过程中可能出现的错误进行统一管理,更加优雅高效
// getCode(): 获取自定义错误代码, getMessage(): 获取自定义错误信息
// echo $e->getCode(). ': ' .$e->getMessage();
echo $e->errorInfo();
}
?>
二、文件上传:
- 前端表单中进行一些必要的设置, 以支持文件上传
- 后端PHP主要使用超全局变量: $_FILES 来处理上传的文件
文件上传流程:
- 配置上传参数
- 判断上传是否成功?
- 拿到拓展名,并判断文件扩展名是否正确?
- 为了防止同名文件相互覆盖, 应该将上传到指定目录的文件重命名,例如用:时间戳+md5.文件拓展名
- 文件上传
<?php
// 1. 配置上传参数
// 设置允许上传的文件类型
$fileType = ['jpg', 'jpeg', 'png', 'gif'];
// 设置允许上传的最大文件长度
$fileSize = 3145728;
// 上传到服务器上指定的目录
$filePath = '/uploads/';
// 上传的原始文件名 $_FILES是一个二维数组,my_file标明从哪里拿到文件,name是文件的原始名
$fileName = $_FILES['my_file']['name'];
// 上传保存在服务器上的临时文件名
$tempFile = $_FILES['my_file']['tmp_name'];
// 2. 判断上传是否成功?
// 主要是通过$_FILES['my_file']['error']值, 等于0成或,大于1出错,出错类型用switch分析
$uploadError = $_FILES['my_file']['error'];
if ($uploadError > 0) {
switch ($uploadError) {
case 1:
case 2:die('上传文件不允许超过3M');
case 3:die('上传文件不完整');
case 4:die('没有文件被上传');
default:die('未知错误');
}
}
// 3. 拿到拓展名,并判断文件扩展名是否正确?
$extension = explode('.', $fileName)[1]; // explode()将一个字符串分割成一个数组,第一参数是分隔标志,第二参数是字符串
if (!in_array($extension, $fileType)) {
die('不允许上传' . $extension . '文件类型');
}
// 4. 为了防止同名文件相互覆盖, 应该将上传到指定目录的文件重命名,例如用:时间戳+md5.文件拓展名
$fileName = date('YmdHis', time()) . md5(mt_rand(1, 99)) . '.' . $extension;
// 5. 文件上传
// 判断是否是通过post上传的
if (is_uploaded_file($tempFile)) { // 先判断临时文件是否存在,存在表示上传成功
// 然后把临时文件上传到指定目录下面 上传文件用:move_uploaded_file()
if (move_uploaded_file($tempFile, __DIR__ . $filePath . $fileName)) {
// 提示用户上传成功,并返回上到一个页面,再强行刷新当前页面
echo '<script>alert("上传成功");history.back();</script>';
} else {
die('文件无法移动到指定目录,请检查目录权限');
}
} else {
die('非法操作');
}
exit();
?>
三、框架中的模型的实现原理
- 框架中的模型, 通常会与一张数据表对应, 而模型对象,则与数据表中的一条记录对应
- 这种数据表到类的映射关系, 对于面向对象的方式管理数据库极其重要
①经过$stmt=$pdo->prepare($sql)
后,当前 数据表
已经和 PDO对象:$stmt
进行了绑定。我们知道当前的SQL语句,它里面的数据还有结果集已经绑定到PDO
对象中。
②然后为
这个 PDO对象
设置一下数据获取模式,$stmt->setFetchMode(PDO::FETCH_CLASS, movies::class);
将数据表和当前类实现绑定
③总结①、②:先 数据表
和 PDO对象
绑定,然后 PDO对象
和 类
进行绑定,实现 数据表和类绑定
,数据表的列和类中属性对应。
FETCH_CLASS:指定获取方式,返回一个所请求类的新实例,映射列到类中对应的属性名
框架中的模型就是一个类,这个类与数据数据表进行映射。
<?php
// 框架中的模型(类)通常是与一张数据表进行关联
// 实现了类到数据表的映射,可以用面向对象的方式访问表中的数据
namespace _0127;
use PDO;
// 类名与表明对应
class movies {
private $mov_id;
private $name;
private $image;
// 属性重载
public function __get($name) {
return $this->$name;
}
public function __set($name, $value) {
$this->$name = $value;
}
// 构造方法: 用于初始化或设置默认值
public function __construct() {
// 设置属性值的自动转换
// 将时间戳转为日期字符串
$this->last_time = date('Y/m/d', $this->last_time);
// 将性别转为可识别的字符
$this->sex = $this->sex ? '男' : '女';
}
}
$pdo = new PDO('mysql:host=localhost;dbname=sordidez', 'root', 'root');
$stmt = $pdo->prepare('SELECT * FROM `movies`');
//设置数据的获取模式:将当前的数据获取模式和一个类进行关联,用movies这个类和PDO产生的一个对象进行关联,实现类和数据表的绑定
$stmt->setFetchMode(PDO::FETCH_CLASS, movies::class);
// 当前数据表已经已经和$stmt这条SQL语句对象(PDO对象)进行了绑定。我们知道当前的SQL语句,它里面的数据还有结果集已经绑定到$stmt对象中。
//然后这个对象设置一下获取模式,就将数据表和当前类movies实现了绑定(先数据表和PDO对象绑定,然后PDO对象和类进行绑定,实现类和数据表绑定)
// FETCH_CLASS:指定获取方式,返回一个所请求类的新实例,映射列到类中对应的属性名
// 绑定后执行一下
$stmt->execute();
//$obj = $stmt->fetch();
// 返回的每一条记录都是movies类的实例对象
//var_dump($obj);
// $movie 是movies类的实例
while ($movie = $stmt->fetch()) {
// 测试
// echo '<pre>' . print_r($movie, true);
// 属性重载的应用
echo "<li>{$movie->mov_id}: {$movie->name}--{$movie->image}</li>";
}
?>