Catch Exception,设置上一个,重新抛出

时间:2015-11-06 21:40:31

标签: php exception

我有一些代码首先通过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的构造函数中。

有没有更好的方法来实现这一目标?

3 个答案:

答案 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链的尾部

链接:$previous 引数を取らない例外クラスに強引に例外をチェーンする裏ワザ - Qiita