资讯专栏INFORMATION COLUMN

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

xialong / 3564人阅读

摘要:源码分析错误及异常处理机制错误及异常处理机制文件是,在框架引导文件的的基础文件中注册不知道的可以去看源码分析二入口篇,通过进行的注册。异常中止处理将错误信息托管至写入日志通过获取最后抛出的错误,把信息托管至,在通过异常处理函数进行记录信息。

源码分析 错误及异常处理机制

错误及异常处理机制文件是/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"]);
    }

该方法做了四件事情:

设置报错级别 E_ALL为E_STRICT所有报错。

设置错误处理函数,set_error_handler([__CLASS__, "appError"])

设置异常处理函数,set_exception_handler([__CLASS__, "appException"]);

设置程序异常终止处理函数,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);

        // 符合异常处理的则将错误信息托管至 thinkexceptionErrorException
        if (error_reporting() & $errno) {
            throw $exception;
        }

        self::getExceptionHandler()->report($exception);
    }

在appError方法中,把符合异常处理的则将错误信息托管至系统的ErrorException,其他的异常通过thinkexceptionHandle进行处理。

//thinkexceptionErrorException文件

/**
 * ThinkPHP错误异常
 * 主要用于封装 set_error_handler 和 register_shutdown_function 得到的错误
 * 除开从 thinkException 继承的功能
 * 其他和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, "	hinkexceptionHandle")
            ) {
                $handle = new $class;
            } else {
                $handle = new Handle;

                if ($class instanceof Closure) {
                    $handle->setRender($class);
                }

            }
        }

        return $handle;
    }

这里有一个关键的地方是:static $handle; 声明该变量是静态变量时候,当赋值给该变量后,函数调用结束后不会销毁,直到脚本结束才会销毁。

这个逻辑就是判断$handle是否已经赋值,没有赋值,获取默认配置文件是否设置处理handle,如果设置,这个handle必须是 hinkexceptionHandle的子类(is_subclass_of($class, " hinkexceptionHandle")),如果没有设置,那么用默认的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 .= "
" . $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()
    {
        // 将错误信息托管至 thinkErrorException
        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, " hinkexceptionHandle")可以知。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30147.html

相关文章

  • thinkphp源码分析(一)—开门

    摘要:源码分析开门篇生命周期入口文件用户发起的请求都会经过应用的入口文件,通常是文件。注册错误和异常机制执行注册错误和异常处理机制。由三部分组成应用关闭方法错误处理方法异常处理方法注册应用关闭方法是为了便于拦截一些系统错误。 源码分析—开门篇 thinkphp生命周期 1、入口文件 用户发起的请求都会经过应用的入口文件,通常是 ==public/index.php==文件。当然,你也可以更改...

    flybywind 评论0 收藏0
  • thinkphp源码分析(二)—入口

    摘要:源码分析入口篇源码分析应用入口用户发起的请求都会经过应用的入口文件,通常是文件。当然,你也可以更改或者增加新的入口文件。通常,我们不建议在应用入口文件中加入过多的代码,尤其是和业务逻辑相关的代码。 源码分析---入口篇 源码分析 应用入口 用户发起的请求都会经过应用的入口文件,通常是 ==public/index.php==文件。当然,你也可以更改或者增加新的入口文件。 通常入口文件的...

    lingdududu 评论0 收藏0
  • 带你抛出优雅的处理系统异常

    摘要:关于抛出异常如在我的上一篇文中所说的一样在接口的设计中接口的返回的数据是非常重要的例如无法避免的等等这些都是要命的错误同时还有一个极大的问题就是在新增模块中例如我最近需要新增一个的分词查询模块这个在添加索引删除索引等等操作的时候是非常容易导 showImg(http://pqykjwm6s.bkt.clouddn.com/Grassland_Scenery_by_Shi_Yuejun....

    susheng 评论0 收藏0
  • PHP处理WEB请求的流程

    摘要:会依据协议,将请求的数据等信息发送给解析器,接下来解析器会解析文件,初始化执行环境,然后处理请求,再以规定的格式返回处理后的结果,退出进程。它的特点是会在动态分配处理进程给请求,以达到提高效率的目的,大多数实现都会维护一个进程池。 PHP作为世界上最好的编程语音,被广泛的运用到Web开发中。因为其语法和C类似,有着非常平缓的学习曲线,越来越多的人使用PHP进行Web产品的快速开发。PH...

    zhangrxiang 评论0 收藏0
  • thinkphp源码分析(三)—自动加载(Loader的分析

    摘要:源码分析自动加载系统会调用方法注册自动加载,在这一步完成后,所有符合规范的类库包括依赖加载的第三方类库都将自动加载。是通过加载对应的文件进行注册加载的。 源码分析 自动加载 系统会调用 Loader::register()方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer依赖加载的第三方类库)都将自动加载。 系统的自动加载由下面主要部分组成: 1. 注册系统的自...

    Pandaaa 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<