我有一些代码首先通过UDP发送消息,如果消息被截断,它会引发异常并通过TCP重试。如果 调用生成异常,我想抛出两个异常来表示代码失败的确切方式。
理想情况下,我会接受第二个异常并将其$previous
设置为第一个异常并重新抛出:
try {
$this->sendQueryUDP($packet);
return $this->getResponseUDP($packet->getID());
} catch(TruncatedUDPPacketException $u) {
try {
return $this->sendQueryTCP($packet);
} catch(\Exception $t) {
$t->setPrevious($u); // this function doesn't exist!
throw $t;
}
}
但是,我可以看到设置$previous
的唯一方法是在Exception
的构造函数中。
有没有更好的方法来实现这一目标?
答案 0 :(得分:2)
我不会称之为更好,但这是一种方法:
private function setPreviousException($e, $prev) {
$reflection = new ReflectionClass($e);
$prop = $reflection->getProperty('previous');
$prop->setAccessible(true);
$prop->setValue($e, $prev);
$prop->setAccessible(false);
return $e;
}
然后
try {
$this->sendQueryUDP($packet);
return $this->getResponseUDP($packet->getID());
} catch(TruncatedUDPPacketException $u) {
try {
return $this->sendQueryTCP($packet);
} catch(\Exception $t) {
$this->setPreviousException($t, $u);
throw $t;
}
}
答案 1 :(得分:1)
要添加到@ dave的答案,我发现ReflectionClass
不一定“折叠”扩展类,并且在$previous
后代的类上设置Exception
会导致错误。例如:
class FooException extends Exception {}
$one = new Exception('one');
$two = new FooException('two');
var_dump(setPreviousException($two, $one));
生成:
PHP Fatal error: Uncaught exception 'ReflectionException' with message 'Property previous does not exist'
为了解决这个问题,我需要使用ReflectionClass::getParentClass()
向下钻取到父类,如下所示:
protected function setPreviousException(\Exception $e, \Exception $prev) {
$reflection = new \ReflectionClass($e);
while( ! $reflection->hasProperty('previous') ) {
$reflection = $reflection->getParentClass();
}
$prop = $reflection->getProperty('previous');
$prop->setAccessible('true');
$prop->setValue($e, $prev);
$prop->setAccessible('false');
return $e;
}
我还与其他一些PHP开发人员进行了一些反复交流,并使用这样的代码覆盖Exception::$previous
具有“改变历史”的副作用,除非你确定$e
永远不会有先前的异常,设置你可能不会做这样的事情。
同样,在实践中,这并不一定使调试更清晰。由后一个异常生成的堆栈跟踪包含有用信息,来自初始处理异常的跟踪通常会使事情混乱。
答案 2 :(得分:0)
另一个魔术般的方法。
private function setPreviousException(\Throwable $e, \Throwable $prev): \Throwable
{
try {
try {
throw $prev;
} finally {
throw $e;
}
} catch (\Thowable $e) {
return $e;
}
}
它具有一些优点:
parent::$previous
。$previous
链的尾部