Laravel Exception处理逻辑解析
vendor/laravel/framework/src/Illuminate/Foundation/Application.php
- app首先继承了container,作为一个容器类存在
- 注册了laravel运行过程的需要的基础类进容器,并且生成了运行需要的实例。承担了初始化功能。这里需要额外说一下,app里面说的所谓注册,不是指绑定,应该是直接直接实例化了,并注入到容器。但是,针对provider,实例化了provider,并运行了,并不会生成实际的类,而是将类绑定。
ExceptionHandler的注册就是在Application的__construct方法中。
$this->registerErrorHandling();
接着我们来到定义该方法的trait:RegistersExceptionHandlers找到该方法。看一下该方法实现了什么样的逻辑。便于理解我加上了一些注释。
protected function registerErrorHandling()
{
error_reporting(-1);//-1报告所有异常,包括后续新定义的异常级别,作用与E_ALL相同
/** set_error_handler,set_exception_handler,register_shutdown_function分别注册不同级别不同类型异常的处理方法。 */
set_error_handler(function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
});//代替标准错误处理方法
set_exception_handler(function ($e) {
$this->handleUncaughtException($e);
});//兜底异常处理方法注册
register_shutdown_function(function () {
$this->handleShutdown();
});//注册一个在脚本正常或非正常情况终止执行时调用的方法。(终极兜底)
}
我们看到laravel主要通过三个原生方法来实现主要的ExceptionHandler机制。下面我们分开看一下这里都分别注册了哪些函数。
set_error_handler
function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
这块很简单,set_error_handler注册的函数会代替原生的报错处理逻辑。这里可以看到laravel将执行error作为异常抛出。并且保留了error的主要信息。(level,msg,file,line)
set_exception_handler
/**set_exception_handler(function ($e) {
$this->handleUncaughtException($e);
});**/
protected function handleUncaughtException($e)
{
//从容器中实例化一个真正的handler。(使用make方法)
$handler = $this->resolveExceptionHandler();
//如果获取到的是Error,通过Error信息实例化一个已定义的“可抛出的致命错误”
if ($e instanceof Error) {
$e = new FatalThrowableError($e);
}
//记录日志(先判断是否报告)
$handler->report($e);
//render错误
if ($this->runningInConsole()) {
$handler->renderForConsole(new ConsoleOutput, $e);
} else {
$handler->render($this->make('request'), $e)->send();
}
}
- resolveExceptionHandler方法从容器中make了一个handler实例,从该方法可以找到laravel实现的handler方法。该方法会判断是否绑定抽象类型来判断使用开发者自行绑定的ExceptionHandler还是系统自带的。handler的作用只有一点,就是解析异常,并将异常处理成我们想要的,更加用户友好的方式展示。(++render方法,可以看“LaravelLumenExceptionsHandler”,该类实现了Laravel的ExceptionHandler接口,想要自定义异常输出的话也可以参考该类++)
- report和render方法都是handler中定义的,report会先进行判断,并根据判断结果决定是否记录log。render自然不必多说,formatexception info,并作为一个HTTP response输出。
register_shutdown_function
protected function handleShutdown()
{
if (! is_null($error = error_get_last()) && $this->isFatalError($error['type'])) {
$this->handleUncaughtException(new FatalErrorException(
$error['message'], $error['type'], 0, $error['file'], $error['line']
));
}
}
error_get_last()是5.2版本实现的方法,能够很好的配合的register_shutdown_function进行兜底处理。改善了需要自定义变量判断的方法。
error_get_last 返回了一个关联数组,描述了最后错误的信息,以该错误的 "type"、 "message"、"file" 和 "line" 为数组的键。
另外,该方法规定只有致命的错误才会启动。