Laravel源碼入門-啓動引導過程(八)HandleExceptions

上篇: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中止處理的準備工作都已配置完成。

下篇:Laravel源碼入門-啓動引導過程(九)RegisterFacades

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章