異常處理的實現

異常的定義

異常用於處理用於指定的錯誤發生時改變腳本的正常流程。理解這句話需要知道兩點:什麼是程序指定的錯誤,什麼是改變腳本的正常流程。指定的錯誤不同於語法錯誤,或者程序邏輯錯誤,它是一個特殊約束性事件。如在遊戲中用戶購買東西時如果金幣不足,程序就會提示玩家金幣不足,這就是異常,需要特殊處理。使用其他程序模塊處理這個異常的動作就叫做 改變腳本的正常流程。

處理異常實現

異常處理的基本實現代碼如下:
try {
    $error = 'this is error';
    throw new Exception($error);  // 在try裏面拋出一個異常對象,這個對象的構造函數參數是一個字符串
} catch (Exception $e) {
    echo 'Caught exception' . $e->getMessage(); // 對異常處理
}

echo "process continue" // 異常處理後,程序不會中斷,會繼續執行

注意:拋出一個異常對象,對象的實例化參數是一個字符串
           異常處理後,程序會繼續執行

處理錯誤和異常的區別

在深入探討異常處理之前,我們需要回答一個問題:爲什麼要使用異常呢,其實常用的錯誤處理方式也可以解決。例如購買東西時金塊不足:

function buySomething()
{
    if (checkMoney()) {
        return "金幣不足";
    }
}

這個程序的問題是中斷了程序的執行。因爲我不僅要提示玩家金幣不足,還可能需要引導用戶充值,或者進行其他某些操作。還有一個更重要的問題是,函數嵌套調用的時候,如函數的調用關係:foo1()->foo2()->foo3()->fooN()。如果類似於fooN函數中每個都有錯誤返回值,這個可以看出錯誤處理的問題是: 
1) 一級一級的返回錯誤消息. 
2)每次都要判斷一下是否錯誤. 
3)還要處理不是自己的錯誤. 
4) 每一級都要重複處理. 
5) 還要知道被調用者將會調用什麼, 會返回什麼錯誤。

如果使用異常處理,就不會出現這樣的問題,程序會捕捉這些異常,不需要每次都判斷,而且還可以根據不同類型的異常使用不同的處理方式。

捕獲多個異常

多個異常是指不同類型的異常。異常類可以被擴展。在我們遊戲中,我們自己定義了幾種異常類型:404異常,邏輯異常,SQL異常,框架類的核心異常。不同的異常類型有不同的處理方式。自定義異常類型代碼如下:
class Core_Exception_Logic extends Core_Exception_Abstract
{
}

捕獲多個異常
    function dealException()
    {
        // 獲取到異常對象
        $e = $this->_request->getException();

        if (! $e instanceof Exception) {
            _exit('Access Denied');
        }

        try {

            throw $e;

        } catch (Core_Exception_403 $e) {

            if (! isDebug()) {
                header403();
            }

        } catch (Core_Exception_404 $e) {
            header404();

        } catch (Core_Exception_Logic $e) {
            // 邏輯異常處理方式
        }
        return false;
    }

異常處理方式

當我們獲取到某個異常後,以什麼樣的形式反饋給玩家,在我們的項目中,如果是ajax請求則返回JSON數據,如果不是ajax請求,則直接輸出到頁面中。
catch (Core_Exception_Logic $e) {

            if ($this->isAjax()) {

                $this->jsonx($e->getMessage(), 'error');

            } else {

                _exit($e->getMessage());
            }

        }

知識點:

1)判斷一個請求是否是ajax請求代碼如下
    public function isXmlHttpRequest()
    {
        return (strcasecmp(
            $this->getServer('HTTP_X_REQUESTED_WITH'),
            'XMLHttpRequest'
        ) ==0 ? true:false);
    }

2)關於Json生成代碼
    public function json(array $output)
    {
        header('Content-type: text/json');
        header('Content-type: application/json; charset=UTF-8');
        exit(json_encode($output));
    }

異常的集中捕獲

如果使用異常,我們可能會有這樣的一個疑惑:每次做一個動作的時候都要捕獲拋出的異常,那麼程序會不會出現很多 try {} catch(Exception $e) {} 這樣的語句塊,如果不小心忘了捕獲某個異常,那後果就無法想象。是的,如果使用異常,的確會出現以上的問題,有一個方法可以解決這樣的問題,那就是集中捕獲異常。
集中捕獲的意思是,使用一個方法捕獲一些常見的異常。在很多框架中就實現了這樣的功能,如zendframwork等,原理如下:
public function run($controllerPath = 'Controller')
    {
        try {

            // 插件調用:路由解析前
            Core_Plugin_Broker::invoke('routerStartup');

            // 路由解析,設置當前分發信息
            $this->setDispatchInfo(null);
            if (!$this->_dispatchInfo) {
                throw new Core_Exception_Fatal('No dispatchInfo found');
            }

            // 插件調用:路由解析後
            Core_Plugin_Broker::invoke('routerShutdown');

            // 插件調用:循環分發前
            Core_Plugin_Broker::invoke('dispatchLoopStartup');

            do {

                $this->_dispatched = true;

                // 插件調用:分發前
                Core_Plugin_Broker::invoke('preDispatch');

                // 執行分發
                Core_Dispatcher::getInstance()->dispatch($this->_dispatchInfo, $controllerPath);

                // 插件調用:分發後
                Core_Plugin_Broker::invoke('postDispatch');

            } while (!$this->_dispatched);

            // 插件調用:循環分發後
            Core_Plugin_Broker::invoke('dispatchLoopShutdown');

        } catch (Exception $e) {

            if ($this->_isCli) {
                exit($e);
            }

            // 錯誤、異常處理控制器
            $dispatchInfo = array(
                'controller' => 'Error',
                'action'     => 'error',
                'params'     => array(
                    'exception' => $e,
                ),
            );
            Core_Dispatcher::getInstance()->dispatch($dispatchInfo, $controllerPath);
        }
    }

在程序執行入口處捕獲,這樣,就可以捕獲到所有的異常,同時把捕獲到的異常重新分發到一個集中的控制器裏面處理(即採用捕獲多個異常的方式)。這樣既可以避免異常未捕獲的問題,又可以減少許多代碼,保證代碼的整潔。

頁面處理返回的異常信息

在我們遊戲中,很多時候都是ajax請求,所以,異常信息常常是以json的形式返回給頁面。在觸發某個動作的時候,常常可能會拋一些邏輯異常,比如生命值不足,行動力不足,網絡中斷,或者金幣不足等,那這裏就有個困惑,如果每個ajax返回函數中都要處理這些異常,是不是顯得特別複雜,如果有某些異常忘了處理,那錯誤信息就無法及時提示給用戶。那我們能不能集中處理這些異常信息呢,答案是肯定的。主要原理就是在ajax返回函數內,寫一個函數集中處理這些一次。核心代碼如下:
    $._get(href, function (resp) {

        // 如果響應內容是JSON格式,說明在PHP拋了異常
        if (typeof resp == 'object' && resp.status != undefined) {
            // 優先處理一遍 _getJSON 的響應結果
            if (! handlePriorAjaxResponse(resp)) {
                return false;
            }
        }
    });

異常處理的流程:

 


發佈了41 篇原創文章 · 獲贊 14 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章