中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
【Laravel系列7.2】錯誤與異常處理

錯誤與異常處理

在學習完 Laravel 中的日志處理模塊之后,接下來馬上就進入到錯誤和異常的學習中。其實通過之前 PHP 基礎相關的學習,我們已經(jīng)了解到 PHP7 中的大部分錯誤都已經(jīng)可以通過異常來進行處理了,而我們的 Laravel 框架,基本全是通過異常來進行處理的。

如果沒有看過之前的文章或者視頻,可以回去再看一下,鏈接在文章底部,因為關于錯誤和異常有三篇文章。

產(chǎn)生錯誤異常信息

首先我們要來模擬產(chǎn)生一個異常的錯誤信息。其實很簡單,去寫一個未定義的變量就好了。

Route::get('error/test'function(){
    echo $a;
});

這時候直接訪問當前這個路由的話,在默認情況下就會顯示錯誤信息。比如下面這樣的。


在這個頁面中,我們可以看到的是報出的錯誤信息詳情,以及下面的調(diào)用堆棧信息。這種報錯頁面非常便于我們調(diào)試錯誤,同時,這些錯誤信息也會同步記錄到你的日志文件中,大家可以看看自己的日志里面是不是已經(jīng)記錄了錯誤信息。

這樣的錯誤頁面對我們的開發(fā)調(diào)試很友好,但是在線上可是不能直接暴露的,畢竟你的文件路徑都暴露出來了,這是非常危險的。所以,在正式的線上環(huán)境中,我們會修改 .env 文件中的 APP_DEBUG 為 false 。這樣的話,我們的詳細錯誤信息就不會顯示出來了,只會顯示一個錯誤頁面。


很明顯,對于錯誤信息的顯示就是通過 .env 中的 APP_DEBUG 來控制的,你也可以直接去修改 config/app.php 配置文件中的 debug 配置來指定調(diào)試值。

'debug' => (bool)env('APP_DEBUG'false),

報告異常

在框架中,我們所有的異常都是通過 app/Exceptions/Handler.php 這個類來進行處理的。在這個文件中,有一個 register() 方法,它可以注冊自定義的異常報告程序和渲染回調(diào),默認情況下,也會將異常信息寫到日志中。

public function register()
{
    $this->reportable(function (Throwable $e) {
        //
    });
}

在之前的基礎學習中,我們知道 Throwable 是現(xiàn)在 PHP 中所有異常和錯誤的基礎接口,所有的問題都可以通過這個 Throwable 來進行捕獲。如果只是異常的話,它們的基類可以用 Exception 來進行捕獲,如果只是錯誤的話,可以通過 ErrorException 來進行捕獲,而 Throwable 是所有信息都可以用它來捕獲。

默認情況下這個閉包方法中沒有任何操作,那么我們不管它,讓它繼續(xù)走默認的處理,我們自己定義一個捕獲特定錯誤進行處理的方法。

public function register()
{
    $this->reportable(function (ErrorException $e){
        Log::channel('custom')->error($e->getMessage());
    })->stop();

    $this->reportable(function (Throwable $e) {
        //
    });
}

在上面的例子中,定義了一個用于捕獲 ErrorException 的處理方法,在這個回調(diào)函數(shù)內(nèi)部將日志寫入到上節(jié)課中定義的 custom 日志配置中。然后再次運行路由進行測試,你會發(fā)現(xiàn)日志被記錄到了 storage/logs/zyblog.log 文件中,而 laravel.log 文件中沒有記錄。其實在默認情況下,所有的錯誤信息都會在 laravel.log 或者你定義的那個默認的日志配置中進行記錄,但在這里,我們給 ErrorException 的錯誤處理的 reportable() 方法再繼續(xù)調(diào)用了一個 stop() 方法。它的作用就是中止后續(xù)的默認日志的記錄。

怎么測試呢?你可以手動去拋出一個普通異常。

Route::get('error/test'function(){
    throw new Exception('test');
    echo $a;
});

然后查看對應的日志文件,就會發(fā)現(xiàn)這個 test 的手動拋出的異常只會在 laravel.log 中記錄,而 zyblog.log 中不會有記錄。

從這里,其實你也可以看出 reportable() 方法就是用于報告異常情況的,它的回調(diào)函數(shù)中除了日志記錄之外,還有一個最大的用處是可以讓我們把異常發(fā)送到外部,比如說釘釘、企業(yè)微信或者電子郵箱等等。如果你沒有這方面的需求,其實這里不太需要變動,直接讓他們記錄日志就好了。

渲染異常

產(chǎn)生了異常之后,我們肯定要有一個顯示異常的響應返回回來。對于 Laravel 來說,默認情況下根據(jù)不同的 APP_DEBUG 的配置,就可以得到上面兩個截圖中的不同的響應返回頁面。這是默認情況下框架為我們提供的頁面,那么我們能不能自定義異常的返回頁面或者返回信息呢?當然沒有問題。

$this->renderable(function (Throwable $e, $request){
    if($request->ajax()){
        return response()->json(['code'=>$e->getCode(), 'msg'=>$e->getMessage()]);
    }else{
        return response()->view('errors.custom', ['msg'=>$e->getMessage()], 500);
    }
});

同樣還是在 register() 方法中,不過這次我們使用的是 renderable() 這個方法。它的回調(diào)函數(shù)有兩個參數(shù),第一個是異常對象,第二個是請求信息。通過這個請求信息,我們就可以構造不同的響應返回頁面。比如說在這里我通過判斷請求是否是 ajax 請求來返回不同的響應的內(nèi)容,如果是 ajax 請求,那么就返回 json 格式的錯誤信息。如果不是的話,就返回一個我自己定義的錯誤頁面。這個頁面非常簡單,直接在 resources/views/errors 目錄下創(chuàng)建了一個 custom.blade.php 模板文件。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>發(fā)生錯誤啦</title>
    </head>
    <body>
        發(fā)生錯誤啦!!!!
        {{$msg}}
    </body>
</html>

聰明的你一定想到了,對于我們很多的業(yè)務開發(fā)來說,前后端分離已經(jīng)是現(xiàn)行的標準規(guī)范,只要是 ajax 請求,默認的響應處理器就會返回 json 格式的錯誤信息。但是這個錯誤信息的格式可能并不是和你系統(tǒng)中定義的格式是相同的。這時候,就可以通過自定義 renderable() 方法中的錯誤返回格式來實現(xiàn)全部數(shù)據(jù)接口的格式統(tǒng)一。另外,自定義錯誤頁面也是一個網(wǎng)站吸引人的地方,比如說很多網(wǎng)站的 404 頁面就設計的很有意思,在這里,也是可以通過 renderable() 來實現(xiàn)個性化的錯誤頁面展示的。

report()輔助函數(shù)

假設我們把異常給 try...catch 掉了,那么我們還會記錄到日志嗎?大家可以試試,這個時候日志中是不會有記錄的。但如果我們也想要 try...catch 的時候產(chǎn)生的錯誤信息也記到到日志文件中,那么我們就可以使用一個 report() 輔助函數(shù)。

try {
    throw new Exception('test');
    echo $a;
catch (Exception $e) {
    report($e);
}

這個時候你就會發(fā)現(xiàn)日志被記錄到了對應的日志文件中,同時,還有一個現(xiàn)象你發(fā)現(xiàn)沒有?那就是使用 report() 函數(shù),程序不會中斷執(zhí)行,依然是正常的執(zhí)行。

// vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
if (! function_exists('report')) {
    function report($exception)
    
{
        if (is_string($exception)) {
            $exception = new Exception($exception);
        }

        app(ExceptionHandler::class)->report($exception);
    }
}

通過 report() 方法的源碼,你會發(fā)現(xiàn)它只是調(diào)用了錯誤控制類的 report() 方法,在這里是使用容器獲得的錯誤處理對象,實際上的對象是 vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php 。

public function report(Throwable $e)
{
    $e = $this->mapException($e);

    if ($this->shouldntReport($e)) {
        return;
    }

    if (Reflector::isCallable($reportCallable = [$e, 'report'])) {
        if ($this->container->call($reportCallable) !== false) {
            return;
        }
    }

    foreach ($this->reportCallbacks as $reportCallback) {
        if ($reportCallback->handles($e)) {
            if ($reportCallback($e) === false) {
                return;
            }
        }
    }

    try {
        $logger = $this->container->make(LoggerInterface::class);
    } catch (Exception $ex) {
        throw $e;
    }

    $logger->error(
        $e->getMessage(),
        array_merge(
            $this->exceptionContext($e),
            $this->context(),
            ['exception' => $e]
        )
    );
}

可以看到這個 report() 方法的實現(xiàn)就只是通過日志去進行記錄了,沒有別的什么操作,所以它當然不會中斷程序的執(zhí)行啦。

自定義異常類

自定義普通的異常沒有什么好說的,繼承指定的異常對象就行了,比如說 Exception、ErrorException、Throwable 之類的都可以。有趣的是在 Laravel 框架中,我們可以在自定義的異常類中定義好 report() 和 render() 方法,這樣,如果拋出的是我們自定義的異常,那么它們就會直接走這個異常類中對應的 report() 和 render() 方法,就和在全局的 Handle 對象中的 register() 里面定義的 reportable() 和 renderable() 一樣。

// app/Exceptions/ZyBlogException.php
class ZyBlogException extends \Exception
{
    public function report()
    
{
        Log::channel('custom')->error($this->getMessage());
    }

    public function render($request)
    
{
        return "異常錯誤內(nèi)容為:" . $this->getMessage();
    }
}

// routes/web.php
Route::get('error/test'function(){
    throw new \App\Exceptions\ZyBlogException('又有問題了');
});

這樣的自定義異常類是不是非常方便使用呢?

HTTP異常

HTTP 異常主要的體現(xiàn)其實就是我們返回的 HTTP 狀態(tài)碼,比如說 404 找不到頁面,401 未授權,500 錯誤,502 服務不可用之類的。除了系統(tǒng)自己報出的這類錯誤之外,我們也可以手動拋出,這里就可以使用一個 abort() 輔助函數(shù)。

abort(404'沒有找到頁面哦');

在測試的時候我們要把上面在 register() 中寫的 renderable() 給注釋掉,因為我們捕獲了全局的 Exception 并進行響應返回,如果不注釋掉就會以我們自定義的 rederable() 的內(nèi)容進行輸出,這樣我們就看不出效果了。或者我們可以判斷一下傳遞進來的 Exception 對象是不是 Symfony\Component\HttpKernel\Exception\HttpException 對象,如果是的話就不處理,走框架默認的。

$this->renderable(function (\Exception $e, $request){
    if($request->ajax()){
        return response()->json(['code'=>$e->getCode(), 'msg'=>$e->getMessage()]);
    }else{
        if(!($e instanceof HttpException)){
            return response()->view('errors.custom', ['msg'=>$e->getMessage()], 500);
        }
    }
});

正常默認情況下報錯的頁面和上面我們截圖的那個 500 錯誤頁面非常類似,只是內(nèi)容變成了 404 | NOT FOUND ,并且 http_code 也變成了 404 。如果想要自定義一個錯誤頁面,可以直接在 resource/views/errors 中定義一個 404.blade.php 文件。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>這是404頁面</title>
    </head>
    <body>
        這是404頁面
        {{ $exception->getMessage() }}
    </body>
</html>

這個頁面中的錯誤信息會通過 $exception 直接帶進來,同樣地,我們還可以在這里直接定義好 403、500 之類的錯誤頁面。這里的頁面模板命名是固定的,如果需要自定義文件名的話,那么就還是要使用我們的 renderable() 來操作了。

異常處理過程

其實對于 PHP 的異常處理過程我們在之前的文章,也就是前面說過的文末的那三條鏈接中的內(nèi)容都已經(jīng)詳細地學習過了。現(xiàn)在主要的疑問是在于 Laravel 框架中是如何去捕獲這些全局的異常和錯誤信息的,是使用我們熟悉的 set_error_handler()、set_exception_handler() 這些函數(shù)嗎?帶著這個問題,我們就來剖析一下 Laravel 源碼是如何處理這些情況的。

在之前講過的 【Laravel系列6.3】框架啟動與服務容器源碼https://mp.weixin.qq.com/s/gavAityVdFU4BgLVf_KCDA 中,vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php 的啟動加載數(shù)組里面就有一個 vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php 服務提供者。這玩意其實從名字就能看出來,控制異常情況的服務提供者嘛。話不多說,直接進去看看吧。

public function bootstrap(Application $app)
{
    self::$reservedMemory = str_repeat('x'10240);

    $this->app = $app;

    error_reporting(-1);

    set_error_handler([$this'handleError']);

    set_exception_handler([$this'handleException']);

    register_shutdown_function([$this'handleShutdown']);

    if (! $app->environment('testing')) {
        ini_set('display_errors''Off');
    }
}

熟悉的配方,熟悉的味道,還需要我再多說什么嗎?接下來就是看看異常和錯誤處理所定義的全局處理函數(shù)了。我們從錯誤處理看看起,同樣在當前這個文件中的 handleError() 方法。

public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
    if (error_reporting() & $level) {
        throw new ErrorException($message, 0, $level, $file, $line);
    }
}

它會將錯誤信息轉換成 ErrorException 再次進行拋出,這次拋出后就進入了異常的處理流程,錯誤這一塊就沒什么多說的了。

public function handleException(Throwable $e)
{
    try {
        self::$reservedMemory = null;

        $this->getExceptionHandler()->report($e);
    } catch (Exception $e) {
        //
    }

    if ($this->app->runningInConsole()) {
        $this->renderForConsole($e);
    } else {
        $this->renderHttpResponse($e);
    }
}

protected function getExceptionHandler()
{
    return $this->app->make(ExceptionHandler::class);
}

protected function renderHttpResponse(Throwable $e)
{
    $this->getExceptionHandler()->render($this->app['request'], $e)->send();
}

在異常處理中,我們可以看到它會調(diào)用 getExceptionHandler() 方法獲取異常處理實例,這個實例是通過服務容器加載的,它就是我們上面學習過的那個 app/Exceptions/Handler.php 對象的實例。通過這個實例及其父類的 report() 方法報告異常,記錄日志,然后通過 render() 方法返回輸出錯誤結果到響應流中,一套異常處理過程就這樣走完了。

簡單不?驚喜不?就是這么 easy ,這系列到現(xiàn)在為止最簡單的源碼分析了吧。不過內(nèi)部的處理其實還更為復雜一些,app/Exceptions/Handler.php 所繼承的 vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php 類中的 report() 和 render() 方法的實現(xiàn)才是更重要的內(nèi)容,大家可以自己再深入的分析一下,比如說 reportable() 和 renderable() 是怎么在 report() 和 render() 中起作用的,總體來說都還是比較簡單的。

總結

上篇學習完日志,這篇學習完異常和錯誤處理,整個調(diào)試診斷方面的內(nèi)容也就完成了,這也是每個框架中最重要的內(nèi)容,不僅限于 Laravel 框架?,F(xiàn)在大部分的框架的處理方式也都是類似的,將錯誤集中到一起進行記錄以及報出。其實到這里相信大家對于框架的源碼已經(jīng)非常熟悉了,后面的內(nèi)容在源碼分析這一塊我們也不會太深入的學習,更多的會以應用為主,畢竟這些附加功能本身就都是集成于整個服務容器和管道應用中的。

參考文檔:

https://learnku.com/docs/laravel/8.x/errors/9375


一起搞懂PHP的錯誤和異常(一)

一起搞懂PHP的錯誤和異常(二)

一起搞懂PHP的錯誤和異常(三)

本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
php – 如何獲取Laravel 5中執(zhí)行的查詢? DB :: getQueryLog()返回空數(shù)組
laravel結合easyWeChat的使用
【log】laravel中的錯誤與日志
編程語言laravel大型項目系列教程(六)之優(yōu)化、單元測試以及部署
詳解用vue.js和laravel實現(xiàn)微信授權登陸
4. 使用Model Factories快速生成測試數(shù)據(jù)
更多類似文章 >>
生活服務
熱點新聞
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服