PHP異常和錯誤


一、PHP的異常和錯誤

異常:在程序運行中不符合預期的情況及與正常流程不同的情況。一種不正常的情況,就是按照正常邏輯不該出錯,但任然出錯的情況,這屬於邏輯和業務流程的一種中斷,而不是語法錯誤。PHP只有主動 throw 后,才能捕獲異常(一般情況下是這樣,也有一些異常PHP可以自動捕獲)。

基礎知識:https://www.cnblogs.com/cshaptx4869/p/11210837.html

錯誤:屬於自身問題,是一種非法語法或環境問題導致,讓編譯器無法通過檢查甚至無法運行的情況。(簡單說就是使腳本運行不正常的情況)

 基礎知識:https://www.cnblogs.com/cshaptx4869/p/11216353.html

 

二、錯誤的級別

大致分為幾類:

1、deprecated,最低級別的錯誤,表示“不推薦、不建議”。一般是由於使用不推薦的、過時的函數或語法造成的。雖不會影響PHP正常流程,但一般情況下建議修正。

2、notice,一般告訴你語法中存在不當的地方。如使用的變量未定義、數組索引是字符時沒有加引號,php視為常量去查找,找不到再視為變量。

不影響PHP正常流程

3、warning,比較高的錯誤,在語法中出現很不恰當的情況下才報此錯誤,比如函數參數不匹配。這種級別的會導致得不到預期結果,故需要修改代碼。

4、fetal error,致命錯誤,直接導致PHP流程終結,后面的代碼不再執行。比如調用一個不存在的方法,此錯誤必須處理。

5、prase error,最高級別的錯誤,語法解析錯誤。屬於語法檢查階段錯誤,導致PHP無法通過語法檢查。

PHP手冊中一共定義了16個級別的錯誤,最常見的就這幾個。

level 可能的值:

         值      常量                       描述
1     E_ERROR           致命的運行錯誤。錯誤無法恢復,暫停執行腳本。
2     E_WARNING         運行時警告(非致命性錯誤)。非致命的運行錯誤,腳本執行不會停止。
4     E_PARSE           編譯時解析錯誤。解析錯誤只由分析器產生。
8     E_NOTICE          運行時提醒(這些經常是你代碼中的bug引起的,也可能是有意的行為造成的。)
16 E_CORE_ERROR PHP 啟動時初始化過程中的致命錯誤。 32 E_CORE_WARNING PHP啟動時初始化過程中的警告(非致命性錯)。
64 E_COMPILE_ERROR 編譯時致命性錯。這就像由Zend腳本引擎生成了一個E_ERROR。 128 E_COMPILE_WARNING 編譯時警告(非致性錯)。這就像由Zend腳本引擎生成了E_WARNING警告。
256 E_USER_ERROR 自定義錯誤消息。像用PHP函數trigger_error(程序員設置E_ERROR) 512 E_USER_WARNING 自定義警告消息。像用PHP函數trigger_error(程序員設的E_WARNING警告) 1024 E_USER_NOTICE 自定義的提醒消息。像由使用PHP函數trigger_error(程序員E_NOTICE集)
2048 E_STRICT 編碼標准化警告。允許PHP建議修改代碼以確保最佳的互操作性向前兼容性。 4096 E_RECOVERABLE_ERROR 開捕致命錯誤。像E_ERROR,但可以通過用戶定義的處理捕獲(又見set_error_handler()) 8191 E_ALL 所有的錯誤和警告(不包括 E_STRICT) (E_STRICT will be part of E_ALL as of PHP 6.0) 16384 E_USER_DEPRECATED 30719 E_ALL

 

推薦博文:http://www.cnblogs.com/zyf-zhaoyafei/p/3649434.html

 

三、PHP中的錯誤處理機制

set_error_handler函數接管PHP錯誤處理,也可以使用trigger_error函數主動拋出一個錯誤。

set_error_handler(error_function, error_types)

設置用戶自定義的錯誤處理函數。函數用於創建運行期間的用戶自己的錯誤處理方法。它需要先創建一個錯誤處理函數,然后設置錯誤級別。

參數描述:

  error_function($errno, $errstr, $errfile, $errline):規定發生錯誤時運行的函數。必須。用戶的函數需要接受兩個參數:錯誤碼和描述錯誤的 string。另外有可能提供三個可選參數:發生錯誤的文件名、發生錯誤的行號 以及發生錯誤的上下文(一個指向錯誤發生時活動符號表的 array。

  支持多種調用:

<?php
     // 直接傳函數名 NonClassFunction
     set_error_handler('function_name'); 

     // 傳 class_name && function_name
     set_error_handler(array('class_name', 'function_name')); 
?>

  error_type:規定在哪個錯誤報告級別會顯示用戶定義的錯誤。可選。默認是  E_ALL | E_STRICT。

注意:使用該函數會完全繞過標准PHP錯誤處理函數(error_reporting(),包括@符),如果有必要,用戶定義的錯誤處理程序必須終止(die())腳本。如果在腳本執行前發生錯誤,由於那時自定義程序還沒有注冊,因此不會用到這個自定義錯誤處理程序。所以一般定義在開頭。且以下級別的錯誤不能由用戶定義的函數來處理:E_ERRORE_PARSE、 E_CORE_ERRORE_CORE_WARNINGE_COMPILE_ERRORE_COMPILE_WARNING,和在調用 set_error_handler() 函數所在文件中產生的大多數 E_STRICT。 只能捕獲系統產生的一些Warning、Notice、Deprecated級別的錯誤。

 

可以在同一個頁面使用restore_error_handler()函數取消自定義函數的接管。

 

register_shutdown_function(),此函數會在PHP程序終止或者die()時觸發一個函數,給PHP一個短暫的回光返照。捕獲PHP的錯誤:Fatal Error、Parse Error等

error_get_last();這個函數可以拿到本次執行產生的所有錯誤。error_get_last();返回數組的信息:
  [type]           - 錯誤類型
  [message] - 錯誤消息
  [file]              - 發生錯誤所在的文件
  [line]             - 發生錯誤所在的行

 

 set_exception_handler(),設置默認的異常處理程序,用在沒有用try/catch塊來捕獲的異常,也就是說不管你拋出的異常有沒有人捕獲,如果沒有人捕獲就會進入到該方法中,並且在回調函數調用后異常會中止。

 

<?php

error_reporting(0);
echo '<pre>';

register_shutdown_function('myShutDown');
set_error_handler('myError');
set_exception_handler('myException');


function myError($code, $msg, $file, $line)
{
    var_dump(compact('code', 'msg', 'file', 'line'));
}

function myShutDown()
{
    $data = error_get_last();
    if(is_null($data)){
        var_dump('nothing error');
    } else {
        var_dump('error',$data);
    }
}

function myException($e)
{
    var_dump('myException:'.$e->getMessage());
}

// require 'a.php';
// throw new Exception("throw exception", 1);
// trigger_error('throw-error', E_USER_ERROR);
try{
    fun();
}catch(Exception $e){
    var_dump('Exception:'.$e->getMessage());
}catch(Throwable $e){ 
  // php7
  var_dump('Throable:'.$e->getMessage()); }finally{ var_dump('finally'); }

 類比 thinkphp5.1 中的錯誤處理機制:

    public static function register()
    {
        error_reporting(E_ALL);
        set_error_handler([__CLASS__, 'appError']);
        set_exception_handler([__CLASS__, 'appException']);
        register_shutdown_function([__CLASS__, 'appShutdown']);
    }

   /**
     * Error Handler
     * @access public
     * @param  integer $errno   錯誤編號
     * @param  integer $errstr  詳細錯誤信息
     * @param  string  $errfile 出錯的文件
     * @param  integer $errline 出錯行號
     * @throws ErrorException
     */
    public static function appError($errno, $errstr, $errfile = '', $errline = 0)
    {
        $exception = new ErrorException($errno, $errstr, $errfile, $errline);
        if (error_reporting() & $errno) {
            // 將錯誤信息托管至 think\exception\ErrorException
            throw $exception;
        }

        self::getExceptionHandler()->report($exception);
    }

   /**
     * Exception Handler
     * @access public
     * @param  \Exception|\Throwable $e
     */
    public static function appException($e)
    {
        if (!$e instanceof \Exception) {
            $e = new ThrowableError($e);
        }

        self::getExceptionHandler()->report($e);

        if (PHP_SAPI == 'cli') {
            self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
        } else {
            self::getExceptionHandler()->render($e)->send();
        }
    }

   /**
     * Shutdown Handler
     * @access public
     */
    public static function appShutdown()
    {
        if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
            // 將錯誤信息托管至think\ErrorException
            $exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);

            self::appException($exception);
        }

        // 寫入日志
        Container::get('log')->save();
    }

demo:

a.php內容:
<?php
    // 模擬Fatal error錯誤
    //test();

    // 模擬用戶產生ERROR錯誤
    //trigger_error('zyf-error', E_USER_ERROR);

    // 模擬語法錯誤
    var_dump(23+-+);

    // 模擬Notice錯誤
    //echo $f;

    // 模擬Warning錯誤
    //echo '123';
    //ob_flush();
    //flush();
    //header("Content-type:text/html;charset=gb2312");


b.php內容:
<?php
    error_reporting(0);
    register_shutdown_function('zyfshutdownfunc');
    function zyfshutdownfunc()
    {
        if ($error = error_get_last()) {
            var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>');
        }
    }

    set_error_handler('zyferror');
    function zyferror($type, $message, $file, $line)
    {
        var_dump('<b>set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .</b><br />');
    }

    require 'a.php';

一般在生產環境下推薦修改php.ini error_report(0)。 

   display_errors 
  錯誤回顯,一般常用語開發模式,但是很多應用在正式環境中也忘記了關閉此選項。錯誤回顯可以暴露出非常多的敏感信息,為攻擊者下一步攻擊提供便利。推薦關閉此選項。 一旦某個產品投入使用,那么第一件事就是應該將display_errors選項關閉,以免因為這些錯誤所透露的路徑、數據庫連接、數據表等信息而遭到黑客攻擊。

         On表示開啟狀態下,若出現錯誤,則報錯,出現錯誤提示。 Off 表示關閉狀態下,若出現錯誤,則提示:服務器錯誤。但是不會出現錯誤提示 

  log_errors 
  在正式環境下用這個就行了,把錯誤信息記錄在日志里。可以關閉錯誤回顯。 某個產品投入使用后,將PHP的log_errors開啟,默認是記錄到WEB服務器的日志文件里,比如Apache的error.log文件。 當然也可以記錄錯誤日志到指定的文件中。另外也可以設定error_log = syslog,使這些錯誤信息記錄到操作系統的日志里。
1 # vim /etc/php.inidisplay_errors = Off
2 log_errors = On
3 error_log = /var/log/php-error.log 

       

  PHP.ini中display_errors = Off失效的解決 
  問題: PHP設置文件php.ini中明明已經設置display_errors = Off,但是在運行過程中,網頁上還是會出現錯誤信息。
  解決: 經 查log_errors= On,據官方的說法,當這個log_errors設置為On,那么必須指定error_log文件,如果沒指定或者指定的文件沒有權限寫入,那么照樣會輸 出到正常的輸出渠道,那么也就使得display_errors 這個指定的Off失效,錯誤信息還是打印了出來。於是將log_errors = Off,問題就解決了。

 

四、PHP7對異常機制的改進

PHP7實現了一個全局的Throwable接口,原來的Exception和部分Error都實現了這個接口,以接口的方式定義了異常的繼承結構。現在大多數的錯誤會被當做Error異常拋出,但還是不夠完善,只有部分錯誤實現了Throwable接口。

這種Error異常可以像Exception異常被第一個匹配的try/catch塊捕獲。如果沒有匹配的catch塊,則調用異常處理函數(set_exception_handler)進行處理。若沒有注冊此函數,則按傳統的方式處理。

<?php
    try {
        test();

    } catch(Throwable $e) {
        echo $e->getMessage() . ' zyf';
    }

    try {
        test();

    } catch(Error $e) {
        echo $e->getMessage() . ' zyf';
    }

 

 

參考文檔: https://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM