【问题标题】:Creating Class Inheritance Dynamically in PHP 5.3在 PHP 5.3 中动态创建类继承
【发布时间】:2011-12-28 03:30:03
【问题描述】:

我在继承和标准 PHP 库 (SPL) 提供的 hierarchy of Exceptions 方面遇到了一个棘手的问题。

我目前正在 PHP 中为基于 REST 的 API 构建一个帮助程序库。这些 API 可以以 JSON 对象的形式返回它们自己的错误消息,并且这些对象包含 PHP 异常提供的属性之外的信息。这是一个简单的例子:

{"error":{"time":"2011-11-11T16:11:56.230-05:00","message":"error message","internalCode":10}}

有时,“消息”包含可能受益于额外解析的内部结构。我喜欢抛出一个特定的异常子类的想法,就像这样:

$error = $json->error;
throw new UnexpectedValueException($error->message, $error-internalCode);

以后可以选择性地捕获:

catch (UnexpectedValueException $e)
{
  ...
}

现在我们遇到了一个难题:我想扩展 SPL 异常对象,以便它们可以具有“时间”属性,并执行额外的“消息”解析。但是,我想在它们的级别 扩展它们,而不是创建基 Exception 类的扩展,以便保留选择性捕获异常的能力。最后,如果可能的话,我想避免创建 13 个不同的子类(异常类型的数量defined in the SPL)。

理想情况下,我可以从父 customException 对象开始:

class customException
{
  public $time;
  public $message;
  public $internalCode;

  public function __construct($time, $message, $internalCode)
  {
    $this->time = $time;
    $this->message = $message;
    $this->internalCode = $internalCode;
  }

  public function parseMessage()
  {
    // Do some parsing of message
    return $parsedMessage;
  }
}

然后,我将有一个可以像这样调用的工厂类:

class ExceptionFactory
{
  static public function createException(Exception $e, $exceptionParent)
  {
    $json = json_decode($e->message);
    return new customException($json->time, $json->message, $json->internalCode) extends $exceptionParent;  // Won't work, but hopefully you get the idea
  }
}

阅读php dynamic class inheritance 后,我可能可以使用eval() 到达那里,但这对我来说感觉不对。如果我必须编写 13 个子类,那么我会发现自己想要对所需的父类 $exceptionParentcustomException 使用多重继承。你会建议我如何解决这个困境?提前感谢您的想法!

【问题讨论】:

    标签: php exception inheritance dynamic


    【解决方案1】:

    有类似的东西:

    class MyException extends \Exception {
      const EXCEPTION_TYPE_FOO = 1;
      const EXCEPTION_TYPE_BAR = 2;
      const EXCEPTION_TYPE_JSON_MESSAGE = 3;
    
      $protected $_data = array();
      $protected $_exceptionType = null;
    
      public function __construct( $type = null ) {
        if( null !== $type ) 
          $this->_exceptionType = $type;
      }
    
      public function __get( $name ) { 
        if( isset($this->_data[$name]) ) {
          if( $name == 'message' ) {
            switch( $this->_exceptionType ) {
               case MyException::EXCEPTION_TYPE_JSON_MESSAGE:
                 return json_decode($this->_data[$name]);
               // other exception types
               default:
                  return $this->_data[$name];
            }
          } 
          return $this->_data[$name];
        }
    
        return null;
      }
    
      public function __set( $name, $value ) {
        $this->_data[$name] = $value;
      }
    }
    

    所以现在你可以:

    $e = new MyException(MyException::EXCEPTION_TYPE_JSON_MESSAGE);
    $e->time = time();
    $e->code = '404';
    $e->message = json_encode(array('testing'));
    

    当你抓住它时

    catch( MyException $e ) {
      print_r( gettype($e->message) );
    }
    

    应该返回数组。

    我没有测试代码,我只是写了它,但你明白了。

    【讨论】:

    • 谢谢,莫比乌斯。这是一个聪明的解决方案!而且我认为您已经发现了一个关键特性:已捕获的异常类型。在您的示例中,您正在捕获MyException,然后检查类型。我开始怀疑我将不得不编写 13 个类,以便我可以在需要时专门捕获每个类。否则,执行catch (UnexpectedValueException $e) 的其他人的代码也会捕获我的自定义异常。对于多次编辑此评论,我深表歉意:我是新来的!
    • 老实说,我个人可能会写 13 个例外。我想它更干净。捕获 MyException 而不显式定义它的唯一方法是 catch( Exception $e ),它是所有异常的父级。您所描述的情况只有在您从 UnexpectedValueException 扩展 MyException 时才会存在
    • 我认为你是对的。我在发布我的问题后去跑步,我认为如果我这样做可能会产生误解:catch (UnexpectedValueException $e) { print_r($e->time); }。另一个开发人员不明白time 的来源。编写子类更干净,这是一件好事。这也意味着我可以通过 phpdoc 记录我的意图,这更好。谢谢你的洞察力,莫比乌斯!
    【解决方案2】:

    一种常见的解决方案是使用“标记界面”来指示“他们的级别”

    interface MyExceptionLevel extends ParentExceptionLevel {}
    
    class MyException extends Exception implements MyExceptionLevel{}
    
    try {
      // code
    } catch (MyException $e) {}
    
    // or
    
    try {
      // code
    } catch (MyExceptionLevel $e) {}
    

    我建议不要使用过多的魔法,尤其是在错误/异常处理等敏感点上。

    【讨论】:

    • 感谢有关“魔术”的建议 KingCrunch。很有可能是我想多了这个问题。我开始倾向于编写 13 个自定义类,每个类都扩展了 SPL Exception hierarchy 的一个节点。由于parseMessage() 将成为标准,我可以使用public static function parseMessage($message) 创建一个parentException 类。这样 13 个子类中的每一个都可以执行以下操作:public function parseMessage($message) { return parentException::parseMessage($message); }
    猜你喜欢
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-01
    • 2011-03-12
    • 2020-04-20
    相关资源
    最近更新 更多