php異常處理

這篇文章主要對php中的錯誤處理進行簡單的記錄

php一開始被設計為一門面向過程的語言,所以異常處理沒有使用像Java一樣的 try / catch 機制,出錯時直接顯示到頁面上,或者記錄到web服務器的錯誤日志中,并且php的錯誤分成了很多的級別,例如E_ERROR、E_WARNING、E_PARSE、E_NOTICE等等,對于像E_ERROR、E_PARSE這樣的嚴重錯誤,php會直接終止腳本的運行。線下開發(fā)時,我們一般會設置error_reporting(-1)ini_set('display_errors', 'on')來報告并顯示所有異常,但是線上運行的項目,出于安全的考慮,我們一般會隱藏錯誤日志的頁面輸出,如果php的配置文件中開啟了log_error,那么相關(guān)的錯誤日志可以在PHP的錯誤日志中查詢,但是如果我們是多項目呢,通過PHP錯誤日志來排錯,顯然不科學,那么我們?nèi)绾蜗窨蚣苤幸粯觼韮?yōu)雅的捕捉異常呢?相信有一定研發(fā)經(jīng)驗的同學,一定聽說過或者用過大名鼎鼎的laravel框架,那么它是如何處理異常的呢?通過閱讀源碼,有興趣可以在Illuminate\Foundation\Bootstrap\HandleExceptions.php查看具體實現(xiàn)。這里只對其中的核心部分進行分享,其中主要有三個PHP內(nèi)置函數(shù)的調(diào)用:set_error_handler、set_exception_handler和register_shutdown_function

set_error_handler函數(shù)

set_error_handler來注冊自己的錯誤處理方法來代替php的標準錯誤處理方式(輸出到頁面或者記錄到日志),但是一些嚴重錯誤是無法通過這種方式來處理的,具體我們來看手冊對該方法的介紹

mixed set_error_handler ( callable error_handler [, interror_types = E_ALL | E_STRICT ] )
設置一個用戶的函數(shù)(error_handler)來處理腳本中出現(xiàn)的錯誤。
本函數(shù)可以用你自己定義的方式來處理運行中的錯誤, 例如,在應用程序中嚴重錯誤發(fā)生時,或者在特定條件下觸發(fā)了一個錯誤(使用 trigger_error()),你需要對數(shù)據(jù)/文件做清理回收。
重要的是要記住 error_types 里指定的錯誤類型都會繞過 PHP 標準錯誤處理程序, 除非回調(diào)函數(shù)返回了 FALSE。 error_reporting() 設置將不會起到作用而你的錯誤處理函數(shù)繼續(xù)會被調(diào)用 —— 不過你仍然可以獲取 error_reporting 的當前值,并做適當處理。 需要特別注意的是帶 @ error-control operator 前綴的語句發(fā)生錯誤時,這個值會是 0。
同時注意,在需要時你有責任使用 die()。 如果錯誤處理程序返回了,腳本將會繼續(xù)執(zhí)行發(fā)生錯誤的后一行。
以下級別的錯誤不能由用戶定義的函數(shù)來處理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 調(diào)用 set_error_handler() 函數(shù)所在文件中產(chǎn)生的大多數(shù) E_STRICT。

set_exception_handler函數(shù)

set_exception_handler注冊異常處理函數(shù),這樣當有未被catch的異常產(chǎn)生時,系統(tǒng)會為我們自動調(diào)用注冊的處理函數(shù)來處理。相關(guān)詳細描述請查看官網(wǎng)文檔set_exception_handler函數(shù)文檔

register_shutdown_function函數(shù)

register_shutdown_function注冊一個會在php中止時執(zhí)行的函數(shù)注冊一個 callback ,它會在腳本執(zhí)行完成或者 exit()后被調(diào)用。關(guān)詳細描述請查看官網(wǎng)文檔register_shutdown_function函數(shù)文檔

最后直接上干貨

<?php

class myErrorException
{
    private $fileRootName;

    private $fileName;

    public function __construct(string $fileName = '/tmp/php/', bool $bugType = false)
    {
        $this->fileRootName = $fileName . date('Y-m-d') . '.log';
        if (!$bugType) {
            $this->checkDir();
            $this->errorHandle();
        }
    }

    private function checkDir(){
        $this->fileName = $this->fileRootName . date('Y-m-d') . '.log';
        if (!file_exists($this->fileName) && !is_dir($this->fileRootName)) {
            @mkdir($this->fileRootName, '0777', true);
        }
    }

    /**
     * 注冊全局的錯誤處理函數(shù)
     */
    private function errorHandle()
    {
        // 注冊自己的錯誤處理方法來代替php的標準錯誤處理方式(輸出到頁面或者記錄到日志),但是一些嚴重錯誤是無法通過這種方式來處理的
        set_error_handler([$this, '_handleError']);

        // 注冊異常處理函數(shù),這樣當有未被catch的異常產(chǎn)生時,系統(tǒng)會為我們自動調(diào)用注冊的處理函數(shù)來處理。
        set_exception_handler([$this, '_handleException']);

        // 注冊捕捉致命錯誤
        register_shutdown_function([$this, '_handleShutdown']);
    }

    /**
     * 錯誤處理
     * @param int $level
     * @param string $message
     * @param string $file
     * @param int $line
     * @param array $context
     */
    public function _handleError(int $level, string $message, string $file = '', int $line = 0, array $context = [])
    {
        if (error_reporting() & $level) {
            $this->_logHandle($message, $level, $file, $line);
        }
    }

    /**
     * 未捕捉的異常
     * @param \Throwable $e
     */
    public function _handleException(\Throwable $e)
    {
        if (!$e instanceof \Error) {
            $this->_logHandle($e->getMessage(), $e->getCode(), $e->getFile(), $e->getLine());
        } else {
            $this->_logHandle($e->getMessage(), $e->getCode(), $e->getFile(), $e->getLine());
        }
    }

    /**
     * 致命錯誤
     */
    public function _handleShutdown()
    {
        $error = error_get_last();
        if (!is_null($error)) {
            $this->_logHandle($error['message'], $error['type'], $error['file'], $error['line']);
        }
    }

    /**
     * 日志記錄格式
     * @param $message
     * @param $level
     * @param $file
     * @param $line
     */
    private function _logHandle($message, $level, $file, $line)
    {
        $logData = [
            'message' => $message,
            'level' => $level,
            'file' => $file,
            'line' => $line
        ];
        $logStr = date('Y-m-d H:i:s') . " error => " . json_encode($logData);
        file_put_contents($this->fileRootName, json_encode($logStr));
        throw new \Error($message, 500);
    }
}

$logObj = new myErrorException();
new testBugA();

die('test');

此時上面的PHP代碼網(wǎng)頁中顯示:

webLog.png

然后服務器日志中顯示:

linuxLog.png

du -h / --max-depth=2 | sort -nr | head -5

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 異常與錯誤的區(qū)別 關(guān)于異常處理這一塊,在官方的手冊上介紹的不夠詳細,所以我在這里再做一個相對詳細一點的總結(jié)...
    四月不見閱讀 2,496評論 0 21
  • 本文介紹php開源庫BooBoo,是一個處理php異常和錯誤的開源庫,通過簡單的分析代碼,我們知道了實際項目中怎么...
    小聰明李良才閱讀 841評論 0 6
  • 異常(Exception)用于在指定的錯誤發(fā)生時改變腳本的正常流程。 什么是異常? PHP 5 提供了一種新的面向...
    josephok閱讀 614評論 0 7
  • 先上代碼: 總結(jié): 1.在php中我們平常用的try,catch只能捕獲我們主動拋出的異常,當然除非你的框架已經(jīng)幫...
    Best博客閱讀 214評論 0 0
  • 異常(Exception)用于在指定的錯誤發(fā)生時改變腳本的正常流程。 什么是異常?PHP 5 提供了一種新的面向?qū)?..
    林路同閱讀 735評論 0 0

友情鏈接更多精彩內(nèi)容