PHP程式員遇到的錯誤與異常下篇之異常

來源:互聯網
上載者:User
本篇文章介紹的內容是PHP程式員遇到的錯誤與異常中的異常問題,現在分享給大家,有需要的朋友可以參考一下

上一篇:那些年,PHPer遇到的錯誤與異常:上篇之錯誤

一、PHP中的異常簡介及使用

1.1 異常執行流程

try{    // 需要進行異常處理的程式碼片段;    throw 語句拋出異常;}catch( Exception $e ){    ... }catch( Exception $e ){    // 處理異常}contine.....

未被捕獲的異常會報致命錯誤:Fatal error:Uncaught exception.....

1.2 PHP異常特點

  1. PHP不會主動捕獲異常,需要程式中主動拋出 (throw)異常,才能捕獲。

  2. throw會自動向上拋出

  3. throw之後的語句不會執行

  4. try後必須有catch,否則解析錯誤Parse error

try{    $num1=3;    $num2=0;    if($num2==0){        throw new Exception('0不能當作除數');        echo 'this is a test';//看不到    }else{        $res=$num1/$num2;    }}catch(Exception $e){    echo $e->getMessage();}

1.3 PHP內建異常

Php不像java提供了很多異常類,所以很多異常都會當成錯誤。要想變錯誤為拋出異常,需要手動throw異常對象

PHP內建異常如:PDOExceptionSplFileObject 可以自動拋出異常,後面的代碼可以繼續執行。

1.4 錯誤和異常的區別

1.4.1 異常處理

當異常被拋出,throw後的代碼不會繼續執行PHP 會嘗試尋找匹配的 catch 代碼塊。如果異常沒有被捕獲,而且又沒用使用set_exception_handler() 作相應的處理的話,那麼將發生一個嚴重的錯誤(致命錯誤),並且輸出 “Uncaught Exception” (未捕獲異常)的錯誤訊息。

1.4.2 異常的基本文法結構

try - 需要進行異常處理的代碼應該放入try代碼塊內,以便捕獲潛在的異常。如果沒有觸發異常,則代碼將照常繼續執行。但是如果異常被觸發,會拋出一個異常
throw - 這裡規定如何觸發異常。每一個trythrow 必須對應至少一個 catch。使用多個catch代碼塊可以捕獲不同種類的異常。
catch - catch代碼塊會捕獲異常,並建立一個包含異常資訊的對象

1.4.3 重新拋出異常

有時,當異常被拋出時,也許希望以不同於標準的方式對它進行處理。可以在一個 catch 代碼塊中再次拋出異常。注意再次拋出異常需要try{}catch{},不能直接在catch代碼塊中throw異常

指令碼應該對使用者隱藏系統錯誤。對程式員來說,系統錯誤也許很重要,但是使用者對它們並不感興趣。為了讓使用者更容易使用,您可以再次拋出帶有對使用者比較友好的訊息的異常。
簡而言之:如果拋出了異常,就必須捕獲它。

1.4.4 錯誤與異常的區別

異常:程式運行與預期不太一致

錯誤:觸發的是本身的錯誤

  • 當遇到錯誤的時候,觸發的是本身的錯誤,不會自動的拋出異常。異常可以通過throw語句拋出異常,通過catch捕獲異常,如果未捕獲會產生致命錯誤。

  • 錯誤在發生的時候或觸發的時候,必須馬上對指令碼進行處理。異常可以一一向上傳遞,直到被捕獲,再處理。

  • 錯誤觸發不具有相關代碼或名稱。異常可以自訂處理錯誤資訊(異常的好處就體現出來了),是通過代碼來拋出,捕獲然後處理

二、自訂異常類

2.1 自訂異常類

  1. 自訂異常類只能重寫建構函式和toString兩個函數

  2. 自訂異常類可以增加自己的方法

  3. 多個catch 時,一般Exception基類放在最後,基類可以調用自訂異常類定義的方法

/** * 自訂異常類 * Class MyException */class MyException extends Exception{    public function __construct($message = "", $code = 0, Throwable $previous = null)    {        parent::__construct($message, $code, $previous);    }    public function __toString()    {        $message = "<h2>出現異常了,資訊如下</h2>";        $message .= "<p>".__CLASS__."[{$this->code}]:{$this->message}</p>";        return $message;    }    public function test()    {        echo 'this is a test';    }    public function stop()    {        exit('script end...');    }    //自訂其它方法}try{    echo '出現異常啦';    throw new MyException('測試自訂異常');}catch (MyException $exception){    echo $exception->getMessage();    echo $exception;}//會繼續執行echo 'continue.........';
try{    throw new MyException('測試自訂異常');}catch (Exception $exception){    echo $exception->getMessage();    $exception->test();} catch (MyException $exception){    echo $exception->getMessage();}

2.2 小技巧

//將錯誤用錯誤抑制符吸收,然後拋出異常If(@!fwrite($filename,$data)) throw new exception(自訂異常)PHP_EOL #分行符號

記錄錯誤記錄檔資訊方式:

(1) :file_put_contents(LOG_PATH.'error.log';, '錯誤資訊'.' '.date('Y-m-d H:i:s')."\r\n", FILE_APPEND);

(2) :error_log('錯誤資訊'.' '.date('Y-m-d H:i:s')."\r\n",3,LOG_PATH.'error.log');

2.3 使用觀察者模式處理異常資訊

Exception_Observer.php/** * 給觀察者定義規範 * * Interface Exception_Observer */interface Exception_Observer{    public function update(Observable_Exception $e);}
Observable_Exception.php/** * 定義觀察者 * Class Observable_Exception */class Observable_Exception extends Exception{    //儲存觀察者資訊    public static $_observers = array();    public static function attach(Exception_Observer $observer)    {        self::$_observers[] = $observer;    }    public function __construct($message = "", $code = 0, Throwable $previous = null)    {        parent::__construct($message, $code, $previous);        $this->notify();    }    public function notify()    {        foreach (self::$_observers as $observer) {            $observer->update($this);        }    }}
Logging_Exception_Observer.php/** * 記錄錯誤記錄檔 * Class Logging_Exception_Observer */class Logging_Exception_Observer implements Exception_Observer{    protected $_filename = __DIR__.'/error_observer.log';    public function __construct($filename = null)    {        if ($filename!==null && is_string($filename)){            $this->_filename = $filename;        }    }    public function update(Observable_Exception $e)    {        $message = "時間:".date('Y:m:d H:i:s',time()).PHP_EOL;        $message.= "資訊:".$e->getMessage().PHP_EOL;        $message.= "追蹤資訊:".$e->getTraceAsString().PHP_EOL;        $message.= "檔案:".$e->getFile().PHP_EOL;        $message.= "行號:".$e->getLine().PHP_EOL;        error_log($message,3,$this->_filename);//寫到日誌中    }}
test.php/** *測試 */header('content-type:text/html;charset=utf-8');require_once 'Exception_Observer.php';require_once 'Logging_Exception_Observer.php';require_once 'Observable_Exception.php';Observable_Exception::attach(new Logging_Exception_Observer());class MyException extends Observable_Exception{    public function test()    {        echo 'this is a test';    }}try{    throw new MyException('出現了異常!');}catch (MyException $exception){    echo $exception->getMessage();}

三、自訂異常處理器

3.1 如何自訂異常處理器

3.1.1 自訂異常處理器

  1. 類似set_error_handler接管系統的錯誤處理函數,set_exception_handler接管所有沒有被catch的異常

  2. restore_exception_handlerrestore_error_handler一樣,本質上應該說從異常/錯誤處理函數棧中彈出一個。比如有一個異常處理函數,彈出一個的話,就沒有異常處理函數,如果有異常沒有捕獲,會交由錯誤處理函數,如沒有錯誤處理函數,異常最終會有系統錯誤處理函數處理。如果設定了2個異常處理函數,彈出一個,會交由下面一個異常處理函數處理。

/** * 自訂異常函數處理器 */header('content-type:text/html;charset=utf-8');function exceptionHandler_1($e){    echo '自訂異常處理器1<br/>函數名:'.__FUNCTION__.PHP_EOL;    echo '異常資訊:'.$e->getMessage();}function exceptionHandler_2($e){    echo '自訂異常處理器2<br/>函數名:'.__FUNCTION__.PHP_EOL;    echo '異常資訊:'.$e->getMessage();}set_exception_handler('exceptionHandler_1');//set_exception_handler('exceptionHandler_2');//恢複到上一次定義過的異常處理函數,即exceptionHandler_1//restore_exception_handler();//致命錯誤資訊//restore_exception_handler();throw new Exception('測試自訂異常處理器');//自訂異常處理器,不會向下繼續執行,因為throw之後不會再繼續執行;try{} catch{}之後,會繼續執行//回顧:自訂錯誤處理器會繼續執行代碼,而手動拋出的錯誤資訊不會繼續執行echo 'test';
/** * 自訂異常類處理器 * Class ExceptionHandler */class ExceptionHandler{    protected $_exception;    protected $_logFile = __DIR__.'/exception_handle.log';    public function __construct(Exception $e)    {        $this->_exception = $e;    }    public static function handle(Exception $e)    {        $self = new self($e);        $self->log();        echo $self;    }    public function log()    {        error_log($this->_exception->getMessage().PHP_EOL,3,$this->_logFile);    }    /**     * 魔術方法__toString()     * 快速擷取對象的字串資訊的便捷方式,直接輸出對象引用時自動調用的方法。     * @return string     */    public function __toString()    {        $message = <<<EOF        <!DOCTYPE html>        <html lang="en">        <head>            <meta charset="UTF-8">            <title>Title</title>        </head>        <body>            <h1>出現異常了啊啊啊啊</h1>        </body>        </html>EOF;    return $message;    }}set_exception_handler(array('ExceptionHandler','handle'));/** * try catch不會被自訂異常處理!!!! */try{    throw new Exception('this is a test');}catch (Exception $exception) {    echo $exception->getMessage();}throw new Exception('測試自訂的異常處理器');

3.1.2 錯誤/異常之後是否繼續執行代碼問題總結

異常:

自訂異常處理器不會向下繼續執行,因為 throw之後不會再繼續執行
try{} catch{}之後,會繼續執行

錯誤:

自訂錯誤處理器會繼續執行代碼,而手動拋出的錯誤資訊不會繼續執行

3.2 像處理異常一樣處理PHP錯誤

3.2.1 方式一:ErrorException

/** * 方式一:ErrorException錯誤異常類 * @param $errno * @param $errstr * @param $errfile * @param $errline * @throws ErrorException */function exception_error_handler($errno,$errstr,$errfile,$errline){    throw new ErrorException($errstr,0,$errno,$errfile,$errline);}set_error_handler('exception_error_handler');try{    echo gettype();}catch (Exception $exception){    echo $exception->getMessage();}

3.2.2 方式二:自訂異常類,繼承基類Exception

/** * 方式二:自訂異常類 * Class ErrorToException *///顯示所有的錯誤error_reporting(-1);class ErrorToException extends Exception{    public static function handle($errno,$errstr)    {        throw new self($errstr,0);    }}set_error_handler(array('ErrorToException','handle'));set_error_handler(array('ErrorToException','handle'),E_USER_WARNING|E_WARNING);try{    echo $test;//notice,不會被處理    echo gettype();//warning    //手動觸發錯誤    trigger_error('test',E_USER_WARNING);}catch (Exception $exception){    echo $exception->getMessage();}

3.3 PHP頁面重新導向實現

header('Content-type:text/html;charset=utf-8');class ExceptionRedirectHandler{    protected $_exception;    protected $_logFile = __DIR__.'redirect.log';    public $redirect='404.html';    public function __construct(Exception $e){        $this->_exception=$e;    }    public static function handle(Exception $e){        $self=new self($e);        $self->log();        // ob_end_clean()清除所有的輸出緩衝,最後沒有緩衝的時候會產生通知層級的錯誤        while(@ob_end_clean());        header('HTTP/1.1 307 Temporary Redirect'); //臨時重新導向        header('Cache-Control:no-cache,must-revalidate');//no-cache強制向原始伺服器再次驗證,must-revalidate可緩衝但必須再向原始伺服器進行確認        header('Expires: Sat, 28 Mar 2016 13:28:48 GMT'); //資源失效的時間        header('Location:'.$self->redirect); //跳轉    }    public function log(){        error_log($this->_exception->getMessage().PHP_EOL,3,$this->_logFile);    }}set_exception_handler(array('ExceptionRedirectHandler','handle'));$link=@mysqli_connect('127.0.0.1','root','1234561');if(!$link){    throw new Exception('資料庫連接出錯啦');}

完!

參考課程視頻:那些年你遇到的錯誤與異常

相關推薦:

PHP程式員遇到的錯誤與異常上篇之錯誤

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.