博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
thinkphp源码分析(四)—错误及异常处理篇
阅读量:5826 次
发布时间:2019-06-18

本文共 6153 字,大约阅读时间需要 20 分钟。

源码分析

错误及异常处理机制

错误及异常处理机制文件是/thinkphp/library/think/Error.php,在框架引导文件的的基础文件base.php中注册(不知道的可以去看《《源码分析(二)—入口篇》》),通过thinkError::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中注册了thinkError::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,其他的异常通过thinkexceptionHandle进行处理。

//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进行处理的是thinkexceptionHandle的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")),如果没有设置,那么用默认的thinkexceptionHandle调用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中注册了thinkError::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中注册了thinkError::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()获取最后抛出的错误,把信息托管至thinkErrorException,在通过异常处理函数进行记录信息。最后写入日志。

总结

整体整个错误处理机制都是通过获取ExceptionHandle再调用handle的report方法,但是多了一步把异常呈现,如果是命令行写到命令行输出,如果是web的就把错误信息通过reponse响应返回客户端。默认的处理handle是thinkexceptionHandle,当然也可以自定义handle,但是必须是thinkexceptionHandle的子类, 通过self::getExceptionHandler的is_subclass_of($class, "\think\exception\Handle")可以知。

转载地址:http://zasdx.baihongyu.com/

你可能感兴趣的文章
从githup下载私有代码到linux本地目录
查看>>
F5系统升级细节之Service Check Date
查看>>
squid透明代理(Transparent Proxy)
查看>>
教你怎么花1个小时做外链,得到别人一天的外链效果
查看>>
mongodb 用户的创建和权限的划分
查看>>
今天是周几
查看>>
ZooKeeper的伪分布式集群搭建以及真分布式集群搭建
查看>>
<JavaScript高级程序设计>读书笔记(第8章BOM之window对象)
查看>>
java字符、字节、位
查看>>
lsof详解
查看>>
规划网络地址分配
查看>>
求助:路由交换-关于×××虚拟私有网二层隧道协议的问题
查看>>
我的友情链接
查看>>
LimeSDR Mini轻松上手系列3:Cubic SDR收听FM广播
查看>>
Android四大组件及意图和意图过滤器
查看>>
权限管理系统
查看>>
sed 问题
查看>>
lamp环境的搭建和应用
查看>>
我的友情链接
查看>>
LaTeX - 黎曼和
查看>>