PHP程式設計師遇到的錯誤與異常下篇之異常

不言
發布: 2023-03-23 20:36:02
原創
1532 人瀏覽過

本篇文章介紹的內容是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 可以自動拋出異常,後面的程式碼可以繼續執行。

PHP程式設計師遇到的錯誤與異常下篇之異常

PHP程式設計師遇到的錯誤與異常下篇之異常

#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基底類別放在最後,基底類別可以呼叫自訂例外類別定義的方法

  4. /**
     * 自定义异常类
     * 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 &#39;this is a test&#39;;
        }
        public function stop()
        {
            exit(&#39;script end...&#39;);
        }
        //自定义其它方法
    }
    
    try{
        echo &#39;出现异常啦&#39;;
        throw new MyException(&#39;测试自定义异常&#39;);
    }catch (MyException $exception){
        echo $exception->getMessage();
        echo $exception;
    }
    //会继续执行
    echo &#39;continue.........&#39;;
    登入後複製
    try{
        throw new MyException(&#39;测试自定义异常&#39;);
    }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.&#39;error.log&#39;;, &#39;错误信息&#39;.&#39; &#39;.date(&#39;Y-m-d H:i:s&#39;)."\r\n", FILE_APPEND);

(2) :error_log(&#39;错误信息&#39;.&#39; &#39;.date(&#39;Y-m-d H:i:s&#39;)."\r\n",3,LOG_PATH.&#39;error.log&#39;);

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__.&#39;/error_observer.log&#39;;
    public function __construct($filename = null)
    {
        if ($filename!==null && is_string($filename)){
            $this->_filename = $filename;
        }
    }

    public function update(Observable_Exception $e)
    {
        $message = "时间:".date(&#39;Y:m:d H:i:s&#39;,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(&#39;content-type:text/html;charset=utf-8&#39;);
require_once &#39;Exception_Observer.php&#39;;
require_once &#39;Logging_Exception_Observer.php&#39;;
require_once &#39;Observable_Exception.php&#39;;

Observable_Exception::attach(new Logging_Exception_Observer());

class MyException extends Observable_Exception{
    public function test()
    {
        echo &#39;this is a test&#39;;
    }
}

try{
    throw new MyException(&#39;出现了异常!&#39;);
}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(&#39;content-type:text/html;charset=utf-8&#39;);
function exceptionHandler_1($e)
{
    echo &#39;自定义异常处理器1<br/>函数名:&#39;.__FUNCTION__.PHP_EOL;
    echo &#39;异常信息:&#39;.$e->getMessage();
}
function exceptionHandler_2($e)
{
    echo &#39;自定义异常处理器2<br/>函数名:&#39;.__FUNCTION__.PHP_EOL;
    echo &#39;异常信息:&#39;.$e->getMessage();
}

set_exception_handler(&#39;exceptionHandler_1&#39;);
//set_exception_handler(&#39;exceptionHandler_2&#39;);
//恢复到上一次定义过的异常处理函数,即exceptionHandler_1
//restore_exception_handler();
//致命错误信息
//restore_exception_handler();
throw new Exception(&#39;测试自定义异常处理器&#39;);

//自定义异常处理器,不会向下继续执行,因为throw之后不会再继续执行;try{} catch{}之后,会继续执行
//回顾:自定义错误处理器会继续执行代码,而手动抛出的错误信息不会继续执行
echo &#39;test&#39;;
登入後複製
/**
 * 自定义异常类处理器
 * Class ExceptionHandler
 */

class ExceptionHandler
{
    protected $_exception;
    protected $_logFile = __DIR__.&#39;/exception_handle.log&#39;;
    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(&#39;ExceptionHandler&#39;,&#39;handle&#39;));
/**
 * try catch不会被自定义异常处理!!!!
 */
try{
    throw new Exception(&#39;this is a test&#39;);
}catch (Exception $exception) {
    echo $exception->getMessage();
}
throw new Exception(&#39;测试自定义的异常处理器&#39;);
登入後複製

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(&#39;exception_error_handler&#39;);

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(&#39;ErrorToException&#39;,&#39;handle&#39;));
set_error_handler(array(&#39;ErrorToException&#39;,&#39;handle&#39;),E_USER_WARNING|E_WARNING);

try{
    echo $test;//notice,不会被处理
    echo gettype();//warning
    //手动触发错误
    trigger_error(&#39;test&#39;,E_USER_WARNING);
}catch (Exception $exception){
    echo $exception->getMessage();
}
登入後複製

3.3 PHP页面重定向实现

header(&#39;Content-type:text/html;charset=utf-8&#39;);
class ExceptionRedirectHandler{
    protected $_exception;
    protected $_logFile = __DIR__.&#39;redirect.log&#39;;
    public $redirect=&#39;404.html&#39;;
    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(&#39;HTTP/1.1 307 Temporary Redirect&#39;); //临时重定向
        header(&#39;Cache-Control:no-cache,must-revalidate&#39;);//no-cache强制向源服务器再次验证,must-revalidate可缓存但必须再向源服务器进行确认
        header(&#39;Expires: Sat, 28 Mar 2016 13:28:48 GMT&#39;); //资源失效的时间
        header(&#39;Location:&#39;.$self->redirect); //跳转
    }
    public function log(){
        error_log($this->_exception->getMessage().PHP_EOL,3,$this->_logFile);
    }
}
set_exception_handler(array(&#39;ExceptionRedirectHandler&#39;,&#39;handle&#39;));
$link=@mysqli_connect(&#39;127.0.0.1&#39;,&#39;root&#39;,&#39;1234561&#39;);
if(!$link){
    throw new Exception(&#39;数据库连接出错啦&#39;);
}
登入後複製

完!

参考课程视频:那些年你遇到的错误与异常

相关推荐:

PHP程序员遇到的错误与异常上篇之错误

以上是PHP程式設計師遇到的錯誤與異常下篇之異常的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!