在PHP 5.3中动态创建类继承


Creating Class Inheritance Dynamically in PHP 5.3

我遇到了一个关于继承和标准PHP库(SPL)提供的异常层次结构的棘手问题。

我目前正在用PHP为基于rest的api构建一个助手库。这些api可以以JSON对象的形式返回它们自己的错误消息,这些对象包含PHP Exception提供的属性之外的信息。下面是一个简单的例子:

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

偶尔,"message"包含可以从额外解析中受益的内部结构。我喜欢抛出Exception的特定子类的想法,像这样:

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

以后可以选择性地捕获:

catch (UnexpectedValueException $e)
{
  ...
}

现在我们到了进退两难的境地:我想扩展SPL Exception对象,这样它们就可以有一个"time"属性,并且还可以执行"message"的额外解析。但是,我希望在它们的级别上扩展它们,而不是创建基Exception类的扩展,以便保留有选择地捕获异常的能力。最后,如果可能的话,我希望避免创建13个不同的子类(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动态类继承后,我可能可以通过使用eval()来实现,但这对我来说感觉不对。如果我必须编写13个子类,那么我将发现自己希望对所需的父类$exceptionParentcustomException使用多重继承。你建议我如何解决这个困境?提前感谢您的意见!

设置如下:

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) );
}

应该返回数组。

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

一个常见的解决方案是使用"标记接口"来指示"它们的级别"

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

我建议不要使用太多的魔法,特别是在错误/异常处理这样明智的地方。