我遇到了一个关于继承和标准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个子类,那么我将发现自己希望对所需的父类$exceptionParent
和customException
使用多重继承。你建议我如何解决这个困境?提前感谢您的意见!
设置如下:
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) {}
我建议不要使用太多的魔法,特别是在错误/异常处理这样明智的地方。