为什么不能从__toString()抛出异常?

zer*_*kms 44 php exception

为什么不能从__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().

  • @zerkms - 这是真的,它不是替代品.也许如果有足够多的人发表意见,他们就会改写Zend Engine.:) (4认同)
  • 嘿,thanx.但是trigger_error()不能替换try/catch,因为它是全局的,而try/catch是具体的. (2认同)
  • 这值得一个大的WAT (2认同)
  • 实际上,如果你显式调用__toString(),那么**echo $ a - > __ toString();**将成功传递异常作为致命错误,其中**echo $ a;**不会. (2认同)

Mat*_*chu 10

我猜这将__toString是hackish,因此存在于典型堆栈之外.因此,抛出的异常将不知道去哪里.

  • @Sprog我认为正在应用*hackish*术语,它的实现是hackish. (9认同)
  • 特别是因为`echo $ a - > __ toString()`可以抛出异常,但`echo $ a`不能. (3认同)
  • __toString不是hackish,它是提供对象的字符串表示的完全标准方式.(字符串)$ a和$ a - > __ toString()之间的区别在于引擎处理您的调用的方式,它与$ x-> something和$ x - > __ call("something")之间的差异相同.一个是直接调用对象中的函数(一个恰好以__开头的函数),另一个是由Zend引擎内部处理的魔术方法. (2认同)

tac*_*one 8

为了回应接受的答案,我提出了一种(或许)更好的方法来处理内部异常__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()方法调用.

Laravel异常处理程序捕获__toString()异常


css*_*895 5

从 php 7.4 开始,似乎允许从 __toString() 抛出异常。我进行了 php7.2 兼容性检查,它是这么说的,并指出了 Doctrine StaticReflectionClassStaticReflectionProperty

请查找有关该提案的更多信息https://wiki.php.net/rfc/tostring_exceptions