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