当前位置: 代码迷 >> Web前端 >> 错误处理的实现
  详细解决方案

错误处理的实现

热度:114   发布时间:2013-10-10 14:14:51.0
异常处理的实现

异常的定义

异常用于处理用于指定的错误发生时改变脚本的正常流程。理解这句话需要知道两点:什么是程序指定的错误,什么是改变脚本的正常流程。指定的错误不同于语法错误,或者程序逻辑错误,它是一个特殊约束性事件。如在游戏中用户购买东西时如果金币不足,程序就会提示玩家金币不足,这就是异常,需要特殊处理。使用其他程序模块处理这个异常的动作就叫做 改变脚本的正常流程。

处理异常实现

异常处理的基本实现代码如下:
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;
            }
        }
    });

异常处理的流程: