上篇:Laravel源碼入門-啓動引導過程(七)LoadConfiguration
上文介紹了 LoadConfiguration,載入 config/*.php 配置,在 《Laravel源碼入門-啓動引導過程(五)$kernel->handle($request)》中第三個要載入的是 HandleExceptions,也就是 Foundation\Http\Kernel::bootstrapers[] 的第三個
\Illuminate\Foundation\Bootstrap\HandleExceptions::class, 如下:
// Illuminate\Foundation\Http\Kernel.php 片段
/**
* The bootstrap classes for the application.
* 引導類,起引導作用的類
*
* @var array
*/
protected $bootstrappers = [
// 載入服務器環境變量(.env 文件)
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
// 載入配置信息(config 目錄)
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
// 配置如何處理異常
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
// 註冊 Facades
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
// 註冊 Providers
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
// 啓動 Providers
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
我們再直接貼出 HandleExceptions 類的代碼,進行分析,非常直觀,如下:
<?php
namespace Illuminate\Foundation\Bootstrap;
use Exception;
use ErrorException;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\FatalThrowableError;
class HandleExceptions
{
/**
* The application instance.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* Bootstrap the given application.
* 引導給定(注入)的 $app。
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
$this->app = $app;
// 報告所有 PHP 錯誤,和 error_reporting(E_ALL); 一樣
// ini_set('error_reporting', E_ALL);
// 參考:http://php.net/manual/zh/errorfunc.constants.php
error_reporting(-1);
// 設置自定義的錯誤處理方法,數組形式表示 類和類的函數
set_error_handler([$this, 'handleError']);
// 設置自定義的異常處理方法
set_exception_handler([$this, 'handleException']);
// 註冊PHP中止時自定義的處理方法(爲什麼是 register_ 而不是 set_?)
register_shutdown_function([$this, 'handleShutdown']);
// 如何應用環境是 .env 文件中 APP_ENV=testing,則關閉錯誤顯示
// 但是,儘管 display_errors 也可以在運行時設置 (使用 ini_set()),
// 但是腳本出現致命錯誤時任何運行時的設置都是無效的。
// 因爲在這種情況下預期運行的操作不會被執行
// 參見:http://php.net/manual/zh/errorfunc.configuration.php#ini.display-errors
// 這裏 environment() 帶了參數,據說 php 弱類型,不定義參數也可以傳入,
// 使用 func_get_arg() 和 func_get_args() 獲取,見 $app->environment()源碼和php說明。
if (! $app->environment('testing')) {
ini_set('display_errors', 'Off');
}
}
/**
* Convert PHP errors to ErrorException instances.
* 轉換 php 錯誤 爲 ErrorException實例(php內置類)
*
* handleError() 是按照PHP中set_error_handler(callable $error_handler )中
* $error_handler 嚴格定義的,參見 set_error_handler 文檔,最終由 trigger_error()觸發。
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
* @param array $context
* @return void
*
* @throws \ErrorException
*/
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
/**
* Handle an uncaught exception from the application.
* 處理程序本身代碼未能想到(處理,寫代碼時想不到的)的異常
*
* Note: Most exceptions can be handled via the try / catch block in
* the HTTP and Console kernels. But, fatal error exceptions must
* be handled differently since they are not normal exceptions.
*
* 大多數能用 try/catch 捕獲到,但是但凡捕獲不到的,都作爲 Fatal?
*
* @param \Throwable $e
* @return void
*/
public function handleException($e)
{
// 記住 instanceof 是運算符,不是函數
if (! $e instanceof Exception) {
$e = new FatalThrowableError($e);
}
// 實例化異常處理對象來報告 $e。
$this->getExceptionHandler()->report($e);
if ($this->app->runningInConsole()) {
$this->renderForConsole($e);
} else {
$this->renderHttpResponse($e);
}
}
/**
* Render an exception to the console.
* 異常渲染到 console
*
* @param \Exception $e
* @return void
*/
protected function renderForConsole(Exception $e)
{
$this->getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
}
/**
* Render an exception as an HTTP response and send it.
* 異常渲染到HTTP response,並 send().
*
* @param \Exception $e
* @return void
*/
protected function renderHttpResponse(Exception $e)
{
$this->getExceptionHandler()->render($this->app['request'], $e)->send();
}
/**
* Handle the PHP shutdown event.
*
* @return void
*/
public function handleShutdown()
{
// $error 由 error_get_last() 返回和定義,
// 有固定的 "type"、 "message"、"file" 和 "line" 鍵值。
if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
$this->handleException($this->fatalExceptionFromError($error, 0));
}
}
/**
* Create a new fatal exception instance from an error array.
*
* @param array $error
* @param int|null $traceOffset
* @return \Symfony\Component\Debug\Exception\FatalErrorException
*/
protected function fatalExceptionFromError(array $error, $traceOffset = null)
{
return new FatalErrorException(
$error['message'], $error['type'], 0, $error['file'], $error['line'], $traceOffset
);
}
/**
* Determine if the error type is fatal.
*
* @param int $type
* @return bool
*/
protected function isFatal($type)
{
return in_array($type, [E_COMPILE_ERROR, E_CORE_ERROR, E_ERROR, E_PARSE]);
}
/**
* Get an instance of the exception handler.
*
* @return \Illuminate\Contracts\Debug\ExceptionHandler
*/
protected function getExceptionHandler()
{
return $this->app->make(ExceptionHandler::class);
}
}
至此所有的錯誤處理、異常處理、PHP中止處理的準備工作都已配置完成。