【问题标题】:Set an exception handler in a class for a specific exception subclass在类中为特定异常子类设置异常处理程序
【发布时间】:2016-02-03 16:13:44
【问题描述】:

我在一个 PHP 项目中有一个简单的类,它在其构造函数中使用如下内容:

    @set_exception_handler(array($this, 'exception_handler'));

问题是,它在全局范围内捕获异常,例如:与类完全无关的异常。

是否可以在此处限制仅由此类和/或特定异常子类的实例引发的异常范围,例如:MyClassException

【问题讨论】:

  • 不,这是不可能的,因为异常是全局对象。您必须在类方法中使用普通的 try...catch 块。

标签: php php-5.4


【解决方案1】:

您不能只为自己的异常设置异常处理程序。所有处理程序都在全局范围内工作。但是,您可以创建自己的异常处理程序链并控制所有异常。

<?php

interface ExceptionHandlerInterface
{
    public function supports(\Exception $e);
    public function handle(\Exception $e);
}

class ExceptionHandler implements ExceptionHandlerInterface
{
    public function supports(\Exception $e)
    {
        return $e instanceof \Exception;
    }

    public function handle(\Exception $e)
    {
        throw $e;
    }
}

class MyExceptionHandler implements ExceptionHandlerInterface
{
    public function supports(\Exception $e)
    {
        return $e instanceof MyException;
    }

    public function handle(\Exception $e)
    {
        exit("Oops, this is a my exception.\n");
    }
}

class ExceptionHandlerChain
{
    private $handlers;

    public function addHandler(ExceptionHandlerInterface $handler, $priority)
    {
        // you should sort all handlers with priority
        $this->handlers[] = $handler;
    }

    public function handle(\Exception $e)
    {
        foreach ($this->handlers as $handler) {
            if ($handler->supports($e)) {
                $handler->handle($e);
            }
        }
    }
}


class MyException extends \Exception
{
}

$chain = new ExceptionHandlerChain();
$chain->addHandler(new MyExceptionHandler(), 0);
$chain->addHandler(new ExceptionHandler(), 1024);

set_exception_handler([$chain, 'handle']);

//throw new RuntimeException();
throw new MyException();

【讨论】:

    【解决方案2】:

    正如我在上面的评论中所说,您不能以某种方式将全局异常处理程序限制为特定的异常类型或事件。

    但是,您可以使用 phps 神奇的 __call() 方法来实现类似的功能,而无需在所有类方法中使用普通的 try...catch 块。考虑这个简单的例子:

    <?php
    
    class myException extends Exception {}
    
    class myClass
    {
      public function __call($funcName, $funcArgs)
      {
        try {
          if (method_exists($this, '_'.$funcName)) {
              $this->_myFunction($funcArgs);
          }
        } catch (myException $e) {
          var_dump($e->getMessage());
        }
      }
    
      public function _myFunction($args)
      {
          throw new myException('Ooops');
      }
    }
    
    $myObj = new myClass;
    $myObj->myFunction();
    

    这里的输出显然是:

    string(5) "Ooops"
    

    这里需要指出三点:

    1. _myFunction() 这样的“普通”方法必须实现try...catch
    2. 捕获的异常可以限制为某些类型,并且可以在对象范围内处理
    3. 没有注册全局异常处理程序,因此没有捕获全局异常。

    然而,这种设置的一个巨大缺点是 IDE 无法支持这种方法链,因此您无法获得自动完成和对象方法的签名帮助。

    【讨论】:

    • 这里我没有包括我的 OP 的主要问题是我的类正在扩展内置的 Redis 类。这意味着使用__call() 在这里不起作用,因为方法存在于父调用中并且永远不会被调用。否则这将是完美的。
    猜你喜欢
    • 2012-04-28
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多