thinkphp源码分析(四)—错误及异常处理篇

源码分析

错误及异常处理机制

错误及异常处理机制文件是/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")可以知。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
thinkphp6中,异常处理是通过异常类来实现的。当系统出现异常时,会抛出一个异常对象,我们可以通过捕获这个异常对象来进行异常处理。以下是一个简单的异常处理示例: 1. 在控制器中抛出一个异常: ```php throw new \think\Exception('这是一个异常'); ``` 2. 在应用的异常处理类中进行异常处理: ```php namespace app\exception; use think\exception\Handle; use think\Response; use Throwable; class Http extends Handle { public function render($request, Throwable $e): Response { // 根据不同异常类型进行处理 if ($e instanceof \think\Exception) { // 返回错误信息 return json(['msg' => $e->getMessage()], 500); } // 其他异常交给系统处理 return parent::render($request, $e); } } ``` 注意,上面的代码中,我们继承了think\exception\Handle类,并重写了它的render方法。render方法接受两个参数:$request表示当前请求对象,$e表示抛出的异常对象。在这个方法中,我们可以根据不同的异常类型进行处理,并返回一个Response对象。如果我们不需要对异常进行特殊处理,可以直接调用父类的render方法,让系统进行默认的异常处理。 3. 在应用的配置文件中配置异常处理类: ```php return [ // 异常处理类 'exception_handle' => 'app\exception\Http', ]; ``` 在上面的配置中,我们将异常处理类设置为app\exception\Http。这样,在应用出现异常时,系统就会自动调用这个类的render方法进行异常处理。 除了上面的方法,thinkphp6还提供了其他的异常处理方式,比如使用自定义的异常类、使用异常监听器等。你可以根据自己的需求选择合适的方式进行异常处理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值