Skip to main content

05.异常统一处理

php 异常处理以前是这样的。

try {
// may be thrwo exception
// ... do sth
} catch(\Exception $e) {
// catch the exception
// ... do sth
} finally {
// to run ,don`t care of any gexception
// ... do sth
}
tip

以上有什么问吗? 有。太他妈烦了,我要有10个地方可能抛异常,10个地方这样整也不好, 太 烦了。 那能不能代码复用,统一处理,还可以做下日志记录啥的?嘿嘿__!行的! 既然要玩那就做个正经些的demo。 上composer.

1创建demo

  • 1.1 make demo && cd demo; 创建demo项目,并进入项目根目录
  • 1.2 composer init;或许要输入姓名和邮箱其它啥也不用管,一路开车enter.
  • 1.3 修改composer.json,注册本地项目命名空间; 新建字段autoload,配置自动引入目录app;
{
"name": "nginx/demo",
"authors": [
{
"name": "wuchuheng",
"email": "wuchuheng@163.com"
}
],
"autoload": {
"psr-4":{
"app\\": "app/"
}
},
"require": {}
}

composer update 更新下 这样为只要载入vender/autoload.php,就根据命名空间来加载类。

1.2 创建入口文件和异常处理器

1.2.1在根据目录新建入口文件start.php

/**
* 入口文件
*
* @filename start.php
* @author wuchuheng<wuchuheng@163.com>
* @date 2019/07/28
*/

require_once __DIR__ . "/vendor/autoload.php";

// 指定render方法来抛出异常
set_exception_handler([(new \app\exception\HandlerException()), 'render']);

//抛一个异常试试
throw new \app\exception\HandlerException('hello');

1.2.2 新建处理器文件app\exception\HandlerException.php

/**
* 异常处理器Handler
*
* @filename HandlerException.php
* @author wuchuheng <wuchuheng@163.com>
* @date 2019/07/28
*/
namespace app\exception;

class HandlerException extends \Exception
{
/**
* 异常抛出
*
*/
public function render($e)
{
echo $e->getMessage();
}

}

行运结果:

到了这里就已经达到我们的期望,只要在入口文件指定异常的处理位置,那么全局的异常处理 将统一到指定的方法处理,统一输出,如是rest API那就输出json, webhtml文本, cli模式等等,是不是不用写很多?

2 进一步封装

tip

异常很多种,常见有验证异常服务器异常等情况发生。所以要把这些异常写成一个个可复用的类 来简化信息系统的简化。哪种情况的异常就抛出哪个,大大提高代码的可读性.我以下我就根据 rest API接口模式来撸下代码。

2.1 创建rest API 异常基类

新建文件 app/excetpion/RestBaseException.php

/**
* Rest API 异常基类
*
* @filename RestBaseException.php
* @author wuchuheng <wuchuheng@163.com>
* @date 2019/07/28
*/

namespace app\exception;

class RestBaseException extends\Exception
{
public $code = 404; // http 状态码
public $errorCode = 4000; // 接口错误码
public $msg = ''; // 错误详情


/**
* 格式化参数
*
*/
public function __construct(array $args = [])
{
if (array_key_exists('code', $args)) {
$this->code = $args['code'];
}
if (array_key_exists('errorCode', $args)) {
$this->errorCode = $args['errorCode'];
}
if (array_key_exists('msg', $args)) {
$this->msg = $args['msg'];
}
}


}


2.2 验证异常类

/**
* 验证异常类
*
* @filename ValidateException.php
* @author wuchuheng <wuchuheng@163.com>
* @date 2019/07/28
*/

namespace app\exception;

use \app\exception\RestBaseException;

class ValidateException extends RestBaseException
{
public $code = 404;
public $errorCode = 4000;
public $msg = '验证异常';
}

2.3 修改异常处理器类

/**
* 异常处理器Handler
*
* @filename HandlerException.php
* @author wuchuheng <wuchuheng@163.com>
* @date 2019/07/28
*/
namespace app\exception;

use \app\exception\RestBaseException;

class HandlerException extends \Exception
{
/**
* 异常抛出
*
*/
public function render($e)
{
// RestApi 异常处理
if ($e instanceof RestBaseException) {
echo '抛出的是Rest API扔异常';
echo '错误码是:' . $e->errroCode . PHP_EOL;
echo '错误信息是:' . $e->msg. PHP_EOL;
echo '状态码是:' . $e->code. PHP_EOL;
}
}

}

2.4 现在可以抛出了

/**
* 入口文件
*
* @filename start.php
* @author wuchuheng<wuchuheng@163.com>
* @date 2019/07/28
*/

require_once __DIR__ . "/vendor/autoload.php";

// 指定render方法来抛出异常
set_exception_handler([(new \app\exception\HandlerException()), 'render']);

//抛一个异常试试
/* throw new \app\exception\HandlerException('hello'); */
throw new \app\exception\ValidateException([
'code'=> 74110, //气死110
'errorCode'=> 111,
'msg'=> '这里是首页,正在抛出一个验证异常,有人吗?',
]);

// 什么也不写就使用默认的定义好的参数抛出
throw new \app\exception\ValidateException();

运行结果:

整体的思路是这样的,首先,不管理是抛出的是什么类,这个类必须是Exception 的子类。 而在入口文件指定好异常类,已经指定好异常的触发处理方法。然后,抛过去的类,在由于是 RestBaseException的子类,判断成立,就进入到里面了。

3 扩展性

这里要说下异常的扩展性,扩展是可以横向的,文中只是列出一种。可以根据开发的情况, 如是html 的异常那就,新增一个HtmlBaseException类,让html超文本的所以异常类 根据上面的例子来扩容,并在异常处理器添加下判断并把处理业务写进去就行了。 所以整体上来看,异常处理已经听可以划分为一个功能模块来看待。 如 可以这么写:

    /**
* 异常抛出
*
*/
public function render($e)
{
// RestApi 异常处理
if ($e instanceof RestBaseException) {
echo '抛出的是Rest API扔异常';
echo '错误码是:' . $e->errorCode . PHP_EOL;
echo '错误信息是:' . $e->msg. PHP_EOL;
echo 'http状态码是:' . $e->code. PHP_EOL;
//....
// log 做下日志处理啥的
// return json 返回数据 json
}
// http 异常正理
//if ($e instanceof HttpBaseException)
// 系统异常正理
//if ($e instanceof SystemBaseException)

}