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, web就html文本,
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)  
        
    }