异常的定义
异常用于处理用于指定的错误发生时改变脚本的正常流程。理解这句话需要知道两点:什么是程序指定的错误,什么是改变脚本的正常流程。指定的错误不同于语法错误,或者程序逻辑错误,它是一个特殊约束性事件。如在游戏中用户购买东西时如果金币不足,程序就会提示玩家金币不足,这就是异常,需要特殊处理。使用其他程序模块处理这个异常的动作就叫做 改变脚本的正常流程。
处理异常实现
异常处理的基本实现代码如下:
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) 还要知道被调用者将会调用什么, 会返回什么错误。
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; } } });
异常处理的流程: