大联大
直播中

潘佼佼

8年用户 190经验值
私信 关注
[经验]

TP5自定义异常的处理方法

一、PHP 异常处理
异常处理通常是防止未知错误产生所采取的处理措施。异常处理的好处是不用再绞尽脑汁去考虑各种错误,这为处理某一类错误提供了一个很有效的方法,使编程效率大大提高。当异常被触发时,通常会发生:
  • 当前代码状态被保存
  • 代码执行被切换到预定义的异常处理器函数
  • 根据情况,处理器也许会从保存的代码状态重新开始执行代码,终止脚本执行,或从代码中另外的位置继续执行脚本

二、异常

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类 留空使用 thinkexceptionHandle
'exception_handle'       => 'appcommonexceptionExceptionHandler',
配置 exception_handle 后,异常将由默认异常处理类 thinkexceptionHandle 变为自定义异常处理类 appcommonexceptionExceptionHandler 进行处理。


1. 自定义异常类

需要抛出指定的异常信息时,异常信息包含在异常类中,所以需要自定义异常类。
下面给出一个自定义的异常类例子,该异常类为 API 项目所自定义的异常类,该异常类继承自 thinkException 包含的异常信息有:
  • 错误信息 $message :用于描述 API 错误信息。
  • 错误码 $code :错误异常的错误码,用于开发者查找错误代码。
  • 状态码 $status :返回的 Http 响应状态码。
  • $previous :异常链中前一个异常。
namespace appcommonexception;

use thinkException;

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. 自定义异常处理类

自定义异常处理类需要继承默认异常处理类 thinkexceptionHandle ,并且重新实现 render 方法:
use Exception;                          
use thinkexceptionHandle;            
use thinkexceptionHttpException;      

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 方法进行处理,否则根据自定义异常类进行调用不同方法进行处理。


参考资料
  • 0 完全开发手册
  • PHP错误异常处理详解



更多回帖

发帖
×
20
完善资料,
赚取积分