为什么不能从__toString()抛出异常?
class a
{
public function __toString()
{
throw new Exception();
}
}
$a = new a();
echo $a;
Run Code Online (Sandbox Code Playgroud)
上面的代码产生了这个:
Fatal error: Method a::__toString() must not throw an exception in /var/www/localhost/htdocs/index.php on line 12
Run Code Online (Sandbox Code Playgroud)
我被指向http://php.net/manual/en/migration52.incompatible.php这里描述了这种行为,但为什么呢?有没有理由这样做?
可能有人在这里知道这个吗?
在bug跟踪器上,php-dev-team像往常一样只是看手册:http://bugs.php.net/50699
Rob*_*ska 46
经过几次搜索,我发现了这一点,其中说:
Johannes解释说,没有办法确保Zend Engine正确处理在转换为字符串期间抛出的异常,并且除非重写Engine的大部分,否则这不会改变.他补充说,过去曾就此类问题进行过讨论,并建议Guilherme检查档案.
上面引用的Johannes是PHP 5.3发布管理器,所以它可能是"官方"的解释,因为您可能会发现PHP为什么会这样做.
该部分继续提到:
__toString()奇怪的是,会接受trigger_error().
因此,并非所有内容都在错误报告方面丢失__toString().
Mat*_*chu 10
我猜这将__toString是hackish,因此存在于典型堆栈之外.因此,抛出的异常将不知道去哪里.
为了回应接受的答案,我提出了一种(或许)更好的方法来处理内部异常__toString():
public function __toString()
{
try {
// ... do some stuff
// and try to return a string
$string = $this->doSomeStuff();
if (!is_string($string)) {
// we must throw an exception manually here because if $value
// is not a string, PHP will trigger an error right after the
// return statement, thus escaping our try/catch.
throw new \LogicException(__CLASS__ . "__toString() must return a string");
}
return $string;
} catch (\Exception $exception) {
$previousHandler = set_exception_handler(function (){
});
restore_error_handler();
call_user_func($previousHandler, $exception);
die;
}
}
Run Code Online (Sandbox Code Playgroud)
这假设定义了一个异常处理程序,大多数框架就是这种情况.与trigger_error方法一样,这样做会违反try..catch的目的,但仍然比使用转储输出要好得多echo.此外,许多框架将错误转换为异常,因此trigger_error无论如何都不会起作用.
作为额外的奖励,您将获得完整的堆栈跟踪,就像正常的异常和您选择的框架的正常开发行为一样.
在Laravel中工作得很好,我很确定它几乎可以在所有现代PHP框架中使用.
屏幕截图相关:
注意:在此示例中,output()由__toString()方法调用.

从 php 7.4 开始,似乎允许从 __toString() 抛出异常。我进行了 php7.2 兼容性检查,它是这么说的,并指出了 Doctrine StaticReflectionClass和StaticReflectionProperty。
请查找有关该提案的更多信息https://wiki.php.net/rfc/tostring_exceptions