Open main menu
首页
专栏
课程
分类
归档
Chat
Sci-Hub
谷歌学术
Libgen
GitHub镜像
登录/注册
搜索
搜索
关闭
Previous
Previous
Next
Next
【thinkphp5分析四】ThinkPHP错误及异常处理机制 - 优化应用的异常处理策略
sockstack
/
814
/
2023-07-28 22:09:38
ThinkPHP5 源码分析
<p><span style="color: red; font-size: 18px">ChatGPT 可用网址,仅供交流学习使用,如对您有所帮助,请收藏并推荐给需要的朋友。</span><br><a href="https://ckai.xyz/?sockstack§ion=detail" target="__blank">https://ckai.xyz</a><br><br></p> ## 源码分析 ### 错误及异常处理机制 错误及异常处理机制文件是/thinkphp/library/think/Error.php,在框架引导文件的的基础文件base.php中注册(不知道的可以去看《《源码分析(二)—入口篇》》),通过\think\Error::register()进行的注册。 ``` /** * 注册异常处理 * @access public * @return void */ public static function register() { error_reporting(E_ALL); set_error_handler([__CLASS__, 'appError']); set_exception_handler([__CLASS__, 'appException']); register_shutdown_function([__CLASS__, 'appShutdown']); } ``` 该方法做了四件事情: 1. 设置报错级别 E_ALL为E_STRICT所有报错。 2. 设置错误处理函数,set_error_handler([__CLASS__, 'appError']) 3. 设置异常处理函数,set_exception_handler([__CLASS__, 'appException']); 4. 设置程序异常终止处理函数,register_shutdown_function([__CLASS__, 'appShutdown']); #### PHP报错级别 php的报错级别有:E_STRICT,E_ALL, E_USER_WARNING等,具体可查看[php 预定义常量]。 #### 错误处理函数 thinkphp中注册了\think\Error::appError()方法对错误进行处理。 ``` /** * 错误处理 * @access public * @param integer $errno 错误编号 * @param integer $errstr 详细错误信息 * @param string $errfile 出错的文件 * @param integer $errline 出错行号 * @return void * @throws ErrorException */ public static function appError($errno, $errstr, $errfile = '', $errline = 0) { $exception = new ErrorException($errno, $errstr, $errfile, $errline); // 符合异常处理的则将错误信息托管至 think\exception\ErrorException if (error_reporting() & $errno) { throw $exception; } self::getExceptionHandler()->report($exception); } ``` 在appError方法中,把符合异常处理的则将错误信息托管至系统的ErrorException,其他的异常通过\think\exception\Handle进行处理。 ``` //think\exception\ErrorException文件 /** * ThinkPHP错误异常 * 主要用于封装 set_error_handler 和 register_shutdown_function 得到的错误 * 除开从 think\Exception 继承的功能 * 其他和PHP系统\ErrorException功能基本一样 */ class ErrorException extends Exception { /** * 用于保存错误级别 * @var integer */ protected $severity; /** * 错误异常构造函数 * @param integer $severity 错误级别 * @param string $message 错误详细信息 * @param string $file 出错文件路径 * @param integer $line 出错行号 * @param array $context 错误上下文,会包含错误触发处作用域内所有变量的数组 */ public function __construct($severity, $message, $file, $line, array $context = []) { $this->severity = $severity; $this->message = $message; $this->file = $file; $this->line = $line; $this->code = 0; empty($context) || $this->setData('Error Context', $context); } /** * 获取错误级别 * @return integer 错误级别 */ final public function getSeverity() { return $this->severity; } } ``` errorException设置错误级别,错误信息,出错文件路径,行号,上下文。 对exception进行处理的是\think\exception\Handle的report()方法:self::getExceptionHandler()->report($exception); ``` //self::getExceptionHandler() /** * 获取异常处理的实例 * @access public * @return Handle */ public static function getExceptionHandler() { static $handle; if (!$handle) { // 异常处理 handle $class = Config::get('exception_handle'); if ($class && is_string($class) && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle") ) { $handle = new $class; } else { $handle = new Handle; if ($class instanceof \Closure) { $handle->setRender($class); } } } return $handle; } ``` 这里有一个关键的地方是:static $handle; 声明该变量是静态变量时候,当赋值给该变量后,函数调用结束后不会销毁,直到脚本结束才会销毁。 这个逻辑就是判断$handle是否已经赋值,没有赋值,获取默认配置文件是否设置处理handle,如果设置,这个handle必须是\think\exception\Handle的子类(is_subclass_of($class, "\\think\\exception\\Handle")),如果没有设置,那么用默认的\think\exception\Handle调用report方法进行处理, 记录到日志文件中。 ``` /** * Report or log an exception. * * @param \Exception $exception * @return void */ public function report(Exception $exception) { if (!$this->isIgnoreReport($exception)) { // 收集异常数据 if (App::$debug) { $data = [ 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'message' => $this->getMessage($exception), 'code' => $this->getCode($exception), ]; $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]"; } else { $data = [ 'code' => $this->getCode($exception), 'message' => $this->getMessage($exception), ]; $log = "[{$data['code']}]{$data['message']}"; } if (Config::get('record_trace')) { $log .= "\r\n" . $exception->getTraceAsString(); } Log::record($log, 'error'); } } ``` 把errorException的数据组装成对应的字符串,写入日志。 #### 异常处理函数 thinkphp中注册了\think\Error::appException()方法对错误进行处理。 ``` /** * 异常处理 * @access public * @param \Exception|\Throwable $e 异常 * @return void */ public static function appException($e) { if (!$e instanceof \Exception) { $e = new ThrowableError($e); } $handler = self::getExceptionHandler(); $handler->report($e); if (IS_CLI) { $handler->renderForConsole(new ConsoleOutput, $e); } else { $handler->render($e)->send(); } } ``` 方法和appError处理差不多,基本都是通过获取ExceptionHandle再调用handle的report方法,但是多了一步把异常呈现,如果是命令行写到命令行输出,如果是web的就把错误信息通过reponse响应返回客户端。 #### 异常中止时执行的函数 thinkphp中注册了\think\Error::appShutdown()方法对错误进行处理。 ``` /** * 异常中止处理 * @access public * @return void */ public static function appShutdown() { // 将错误信息托管至 think\ErrorException if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) { self::appException(new ErrorException( $error['type'], $error['message'], $error['file'], $error['line'] )); } // 写入日志 Log::save(); } ``` 通过error_get_last()获取最后抛出的错误,把信息托管至think\ErrorException,在通过异常处理函数进行记录信息。最后写入日志。 ### 总结 整体整个错误处理机制都是通过获取ExceptionHandle再调用handle的report方法,但是多了一步把异常呈现,如果是命令行写到命令行输出,如果是web的就把错误信息通过reponse响应返回客户端。默认的处理handle是\think\exception\Handle,当然也可以自定义handle,但是必须是\think\exception\Handle的子类, 通过self::getExceptionHandler的is_subclass_of($class, "\\think\\exception\\Handle")可以知。 [1]: php%20%E9%A2%84%E5%AE%9A%E4%B9%89%E5%B8%B8%E9%87%8F
【thinkphp5分析四】ThinkPHP错误及异常处理机制 - 优化应用的异常处理策略
作者
sockstack
许可协议
CC BY 4.0
发布于
2023-07-28
修改于
2024-10-12
上一篇:百度快速收录
下一篇:【thinkphp5分析三】ThinkPHP自动注册分析 - 深入了解ThinkPHP框架的自动注册机制
尚未登录
登录 / 注册
文章分类
博客重构之路
5
Spring Boot简单入门
4
k8s 入门教程
0
MySQL 知识
1
NSQ 消息队列
0
ThinkPHP5 源码分析
5
使用 Docker 从零开始搭建私人代码仓库
3
日常开发汇总
3
标签列表
springboot
hyperf
swoole
webman
php
多线程
数据结构
docker
k8s
thinkphp
mysql
tailwindcss
flowbite
css
前端