Thinkphp5的自定义异常处理及其底层实现

排版可能更好一点的永久原文链接:Thinkphp5的自定义异常处理及其底层实现

前言

在很多php的项目中,错误信息的输出返回都通过异常抛出的方式进行,特别是在后端校验过程中,或者后端处理过程中突然遇到需要中断程序执行的情况,相比返回['code' => 999, 'message' => 'error'],抛出异常无疑更简单明了
众所周知,php里边自定义异常处理函数需要用到内置方法 set_exception_handler,而在 Thinkphp5 中可以由开发者自定义异常处理类进行接管

正文

涉及到的配置参数及类

如果需要自定义异常处理,则需要在 app.php 中配置 exception_handle 参数
参数支持回调模式和自定义类,正如官方文档所说

'exception_handle'       => function($e) {
  // 参数验证错误
  if ($e instanceof \think\exception\ValidateException) {
  	return json($e->getError(), 422);
  }

  // 请求异常
  if ($e instanceof \think\exception\HttpException && request()->isAjax()) {
  	return response($e->getMessage(), $e->getStatusCode());
  }
},
// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle'       => '\\app\\common\\exception\\Http',

自定义类需要继承 think\exception\Handle 并实现 render(),支持 return new Response() 直接向前端响应数据

比如在我的项目中,在 app/common/exception 下定义了 BaseException extends \think\Exception,以后不同模块的自定义异常类都会继承 BaseException,在自定义异常处理类中判断 $e instanceof BaseException 即可进行个性化处理

app/common/exception/BaseException.php

<?php
/**
 * Created by PhpStorm.
 * User: 94468
 * Date: 2020/2/10
 * Time: 2:12
 */

namespace app\common\exception;


use think\Exception;

class BaseException extends Exception
{
}

app/common/exception/Http.php 中实现了控制器抛出的验证器异常和基于 BaseException 的异常,并统一格式输出和错误日志记录,在实际使用过程中就特别方便

app/common/exception/Http.php

<?php
/**
 * Created by PhpStorm.
 * User: 94468
 * Date: 2020/2/10
 * Time: 0:33
 */

namespace app\common\exception;


use Exception;
use think\exception\ErrorException;
use think\exception\Handle;
use think\exception\ValidateException;
use think\facade\Log;

class Http extends Handle
{
    public function render(Exception $e)
    {
        if ($e instanceof ValidateException) {
            Log::info('Exception-Validate:' . $e->getMessage());
            return json([
                'code' => 888,
                'message' => $e->getMessage(),
                'data' => [],
            ]);
        }

        if ($e instanceof BaseException) {
            Log::error('Exception-Logic:' . $e->getMessage());
            return json([
                'code' => $e->getCode() ?: 777,
                'message' => $e->getMessage(),
                'data' => [],
            ]);
        }

        if ($e instanceof ErrorException) {
            Log::error('Exception-Error:' . $e->getMessage());
            return json([
                'code' => $e->getCode() ?: 666,
                'message' => $e->getMessage(),
                'data' => [],
            ]);
        }
        Log::error('Unknown-Error:' . $e->getMessage());
        return json([
            'code' => $e->getCode() ?: 555,
            'message' => env('APP_DEBUG') ? $e->getMessage() : '系统错误',
            'data' => []
        ]);
//        return parent::render($e);
    }
}

底层是如何实现的

首先,定位到 thinkphp/library/think/Error.php 错误处理类,在其 register() 中可看到对 php 的异常处理函数注册方法set_exception_handler 的调用

	/**
     * 注册异常处理
     * @access public
     * @return void
     */
    public static function register()
    {
        error_reporting(E_ALL);
        set_error_handler([__CLASS__, 'appError']);
        set_exception_handler([__CLASS__, 'appException']);
        register_shutdown_function([__CLASS__, 'appShutdown']);
    }

再顺着看 appException 方法,发现使用了 self::getExceptionHandler() 获取异常处理对象,对象是由变量 self::$exceptionHandler 决定的,默认 \\think\\exception\\Handle

    /**
     * 注册异常处理
     * @access public
     * @return void
     */
    public static function register()
    {
        error_reporting(E_ALL);
        set_error_handler([__CLASS__, 'appError']);
        set_exception_handler([__CLASS__, 'appException']);
        register_shutdown_function([__CLASS__, 'appShutdown']);
    }
    /**
     * Exception Handler
     * @access public
     * @param  \Exception|\Throwable $e
     */
    public static function appException($e)
    {
        if (!$e instanceof \Exception) {
            $e = new ThrowableError($e);
        }

        self::getExceptionHandler()->report($e);

        if (PHP_SAPI == 'cli') {
            self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
        } else {
            self::getExceptionHandler()->render($e)->send();
        }
	}

看到这里并没有看到 app.php 中的 exception.handler 如何发挥作用的,那么继续往下看,由于 self::$exceptionHandlersetExceptionHandler 决定,透过编辑器可以找到其调用方如下图所示:

thinkphp/library/think/App.phpinitialize() 中,可看到如下所示代码

// 注册异常处理类
if ($this->config('app.exception_handle')) {
	Error::setExceptionHandler($this->config('app.exception_handle'));
}

initialize() 是在应用启动时(执行 run())执行的初始化函数,
public/index.php

<?php
// [ 应用入口文件 ]
namespace think;

// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';

// 支持事先使用静态方法设置Request对象和Config对象

// 执行应用并响应
Container::get('app')->run()->send();

App.php

   /**
     * 执行应用程序
     * @access public
     * @return Response
     * @throws Exception
     */
    public function run()
    {
        try {
            // 初始化应用
            $this->initialize();

            // 监听app_init
            $this->hook->listen('app_init');
		}
	}

以及在同文件的 containerConfigUpdate() 中也能看到类似代码

$config = $this->config->get();

// 注册异常处理类
if ($config['app']['exception_handle']) {
	Error::setExceptionHandler($config['app']['exception_handle']);
}

这个函数可以顺着 initialize() -> init() -> containerConfigUpdate 找到,作用是对不同模块中的配置进行一次更新

public function init($model)
{
	// 定位模块目录
	$module = $module ? $module . DIRECTORY_SEPARATOR : '';
	$path   = $this->appPath . $module;

	// 加载初始化文件
	if (is_file($path . 'init.php')) {
		include $path . 'init.php';
	} elseif (is_file($this->runtimePath . $module . 'init.php')) {
		include $this->runtimePath . $module . 'init.php';
	} else {
		// ... 中间代码
	}
	// ... 中间代码
	$this->setModulePath($path);

	if ($module) {
		// 对容器中的对象实例进行配置更新
		$this->containerConfigUpdate($module);
	}
}

总结

php中使用 set_exception_handler 处理异常,而Thinkphp5中可在 app.php 中自定义 excpetion_handler 处理类的方式统一方便的处理异常

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
thinkphp6中,可以通过以下方式自定义抛出异常: 1. 创建自定义异常类 首先,我们需要创建一个自定义的异常类,继承自think\Exception类,例如: ```php namespace app\exception; use think\Exception; class MyException extends Exception { protected $message = '自定义异常信息'; protected $code = 10001; } ``` 在上面的例子中,我们创建了一个名为MyException自定义异常类,设置了异常信息和异常代码。 2. 抛出自定义异常 在需要抛出异常的地方,可以通过以下方式抛出自定义异常: ```php throw new MyException(); ``` 或者: ```php throw new MyException('自定义异常信息', 10001); ``` 在上面的例子中,我们使用了自定义异常类MyException,并传入了异常信息和异常代码。 3. 异常处理 最后,我们需要在异常处理的地方捕获并处理自定义异常,例如: ```php namespace app\exception; use think\exception\Handle; class ExceptionHandle extends Handle { public function render(\Exception $e) { if ($e instanceof MyException) { // 处理自定义异常 return json(['code' => $e->getCode(), 'msg' => $e->getMessage()]); } // 其他异常交给系统处理 return parent::render($e); } } ``` 在上面的例子中,我们创建了一个名为ExceptionHandle的异常处理类,继承自think\exception\Handle类,重写了render方法,在该方法中捕获并处理自定义异常MyException。 当系统抛出自定义异常时,会调用ExceptionHandle的render方法进行处理,返回一个相应的响应结果。如果抛出的异常不是自定义异常,则交给系统默认的异常处理方式处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值