【大树云微课堂】-TP5 自定义异常处理

关键字 :大树云BTCTP5

一、PHP 异常处理

异常处理通常是防止未知错误产生所采取的处理措施。异常处理的好处是不用再绞尽脑汁去考虑各种错误,这为处理某一类错误提供了一个很有效的方法,使编程效率大大提高。当异常被触发时,通常会发生:

  1. 当前代码状态被保存
  2. 代码执行被切换到预定义的异常处理器函数
  3. 根据情况,处理器也许会从保存的代码状态重新开始执行代码,终止脚本执行,或从代码中另外的位置继续执行脚本

二、异常

1. PHP 内置的异常类

Exception 是所有异常的基类:

class Exception
{
    /* 属性 */
    protected string $message ;     // 异常信息 
    protected int $code ;                 // 用户自定义异常代码
    protected string $file ;               // 发生异常的文件名
    protected int $line ;                   // 发生异常的代码行号
    /* 方法 */
    public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )
    final public getMessage ( void ) : string              // 返回异常信息
    final public getPrevious ( void ) : Throwable
    final public getCode ( void ) : int                         // 返回异常代码
    final public getFile ( void ) : string                       // 返回发生异常的文件名
    final public getLine ( void ) : int                           // 返回发生异常的代码行号
    final public getTrace ( void ) : array                    // backtrace() 数组
    final public getTraceAsString ( void ) : string
    public __toString ( void ) : string
    final private __clone ( void ) : void
}

可以用自定义的异常处理类来扩展 PHP 内置的异常处理类,使用自定义的类来扩展内置异常处理类,并且要重新定义构造函数的话,建议同时调用 parent::__construct() 来检查所有的变量是否已被赋值。

2. 异常抛出

异常可以被触发,即抛出,被抛出的异常必须是 Exception 的实例或者 Exception 的子类实例,规定的异常触发的方式为 throw :

throw new \Exception('An Exception occured!');
 

3. 异常处理

如果一个异常没有被捕获,而且又没有使用 set_exception_handler() 作相应的处理的话,那么 PHP 将会产生一个严重的错误,并且输出未能捕获异常 (Uncaught Exception ... ) 的提示信息:

Fatal error: Uncaught exception 'Exception' with message 'An Exception occured!' in /var/www/html/test/index.php on line 5 
Exception: An Exception occured! in /var/www/html/test/index.php on line 5 
Call Stack: 
    0.0005     330680   1. {main}() /var/www/html/test/index.php:0
 

4. 异常捕获

为了避免 Uncaught Exception 错误,需要将可能抛出异常的代码块放入 try 代码块内,如果没有抛出异常,代码继续执行,如果异常被触发,则 try 代码块需要至少有一个相应的 catch 或 finally 代码块匹配:

try {
    throw new Exception('A error is thrown');
} catch(Exception $e) {
    // 处理代码
}

5. 顶层异常处理器

在实际开发过程中,异常捕获仅靠 try 和 catch 是不能满足需求的。这时候需要一个处理所有未被捕获异常的异常处理器,而 set_exception_handler 可以设置用户自定义的异常处理函数:

function ExceptionHandler($e)
{
    echo "Exception: " , $exception->getMessage(); 
}
set_exception_handler('ExceptionHandler');
throw new Exception('Uncaught Exception occured');

三、ThinkPHP 异常处理

在调试模式下,ThinkPHP 的默认异常处理与 PHP 的默认异常处理有所区别。

与 PHP 抛出的单纯错误信息不同,ThinkPHP 调试模式提供了更多的错误信息与细节,更具人性化,利于开发人员快速定位错误。

在调试模式下,ThinkPHP 默认异常处理的错误页面如下所示:

 


默认情况下,ThinkPHP 会对任何错误抛出异常,也可以设置配置文件的 error_reporting 来限制报告的错误级别:

// 异常错误报错级别,
error_reporting(E_ERROR | E_PARSE );


1. 渲染异常信息

ThinkPHP 的异常处理中,Http 请求异常都交由处理函数 render() 进行渲染,将异常信息渲染为 Http 响应进行返回。

首先会检查是否有自定义的异常处理器,如果有定义,则调用自定义异常处理器进行处理,如果没有则根据异常是否为 HttpException 区分处理。

/**
 * Render an exception into an HTTP response.
 *
 * @param  \Exception $e
 * @return Response
 */
public function render(Exception $e)
{
    if ($this->render && $this->render instanceof \Closure) {
        $result = call_user_func_array($this->render, [$e]);
        if ($result) {
            return $result;
        }
    }

    if ($e instanceof HttpException) {
        return $this->renderHttpException($e);
    } else {
        return $this->convertExceptionToResponse($e);
    }
}

四、自定义异常处理

虽然 ThinkPHP 异常处理具有更详细的错误信息,但是在生产环境往往不希望将详细的错误信息泄漏出去,防止暴露自身接口给用户,但又可以提供快速追溯问题的提示给开发人员,需要抛出的指定的异常信息ㄍ所以开发者需要自定义异常处理。


框架支持异常由开发者自定义的异常处理类进行处理,只需要配置参数 exception_handle :

// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle'       => 'app\common\exception\ExceptionHandler',

配置 exception_handle 后,异常将由默认异常处理类 \think\exception\Handle 变为自定义异常处理类 app\common\exception\ExceptionHandler 进行处理。

1. 自定义异常类

需要抛出指定的异常信息时,异常信息包含在异常类中,所以需要自定义异常类。

下面给出一个自定义的异常类例子,该异常类为 API 项目所自定义的异常类,该异常类继承自 think\Exception 包含的异常信息有:

  1. 错误信息 $message :用于描述 API 错误信息。
  2. 错误码 $code :错误异常的错误码,用于开发者查找错误代码。
  3. 状态码 $status :返回的 Http 响应状态码。
  4. $previous :异常链中前一个异常。

namespace app\common\exception;

use think\Exception;

class ApiException extends Exception
{
    // 状态码
    public $status;

    // 构造函数
    public function __construct($message, $code = 0, int $status = 404, ?\Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
        $this->status = $status;
    }

    public function getStatus($status)
    {
        return $this->status;
    }
}

2. 抛出异常

在程序运行中,如果检测到已知错误,即可抛出具有提示意义的自定义异常类,中断程序运行,并由异常处理类处理后返回期望的响应信息。
例如用户输入参数不符合规范,参数验证不通过,可以抛出参数验证异常:

function validateParams($class, $sence = '', $data)
{
    try {
        // 调用验证器
        $validate = validate($class);

        // 检查结果
        $result = $validate->hasScene($sence) ? $validate->check($data, [], $sence) : true;

        // 检查返回的信息,如果没有就不返回
        if (!$result) {
            throw new ApiException($validate->getError(), 30003, 400);
        }
    } catch (ClassNotFoundException $e) {
        return;
    }
}

3. 自定义异常处理类

自定义异常处理类需要继承默认异常处理类 \think\exception\Handle ,并且重新实现 render 方法:

use Exception;                         
use think\exception\Handle;            
use think\exception\HttpException;     

class ExceptionHandler extends Handle
{
    // 异常处理接管
    public function render(Exception $e)
    {
        // 如果调试模式,异常交由系统处理
        if (config('app_debug') == true) {
            return parent::render($e);
        }

        // 异常分类处理
        if ($e instanceof ApiException) {
            return $this->apiError($e);
        } else if ($e instanceof HttpException) {
            return $this->httpError($e);
        } else if ($e instanceof ValidateException) {
            return json($e->getError(), 40000, 400);
        } else {
            return $this->serverError($e);
        }
    }

    // Api 异常处理
    public function apiError($e)
    {
        $result = [
            'error_code' => $e->getCode(),
            'message' => $e->getMessage(),
            'data' => [
                'requestUrl' => request()->url(),
                'requestParams' => request()->param()
            ]
        ];
        return json($result, $e->getStatus());
    }
}

在 render 方法中,通过 config('app_debug') 判断是否开启了调试模式,如果开启了就将异常交由默认异常处理类 render 方法进行处理,否则根据自定义异常类进行调用不同方法进行处理。

参考资料


更多大樹雲資源,請參照:

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★博文作者未开放评论功能