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

时间:2010-03-12 00:15:46

标签: php exception

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

class a
{
    public function __toString()
    {
        throw new Exception();
    }
}

$a = new a();
echo $a;

上面的代码产生了这个:

Fatal error: Method a::__toString() must not throw an exception in /var/www/localhost/htdocs/index.php on line 12

我被指向http://php.net/manual/en/migration52.incompatible.php描述了这种行为,但为什么呢?有没有理由这样做?

这里的任何人都知道吗?

在bug跟踪器上,php-dev-team像往常一样只是看手册:http://bugs.php.net/50699

6 个答案:

答案 0 :(得分:45)

经过几次搜索,我发现了这一点,其中说:

  

Johannes解释说没有办法确保Zend Engine 正确处理强制转换为字符串期间抛出的异常,并且除非Engine的大部分内容不会改变被重写。他补充说,过去曾就此类问题进行过讨论,并建议Guilherme检查档案。

上面引用的Johannes是PHP 5.3发布管理器,所以它可能是“官方”的解释,因为您可能会发现PHP为什么会这样做。

该部分继续提及:

  奇怪的是,

__toString()会接受trigger_error()

因此,__toString()内的错误报告并非全部丢失。

答案 1 :(得分:10)

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

答案 2 :(得分: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;
    }
}

这假定定义了一个异常处理程序,大多数框架就是这种情况。与trigger_error方法一样,执行此操作将违反try..catch 的目的,但仍然比使用echo转储输出要好得多。此外,许多框架将错误转换为异常,因此trigger_error无论如何都不会工作。

作为额外的奖励,您将获得完整的堆栈跟踪,就像正常的异常和您选择的框架的正常开发行为一样。

在Laravel中工作得非常好,而且我非常确定它可以在几乎所有现代PHP框架中工作。

相关截图:
note :在此示例中,output()方法调用了__toString()

__toString() exception caught by Laravel exception handler

答案 3 :(得分:1)

从php 7.4开始,似乎允许从__toString()引发异常。我进行了php7.2兼容性检查,并说了这一点,并指出了教义StaticReflectionClassStaticReflectionProperty

请找到有关提案https://wiki.php.net/rfc/tostring_exceptions

的更多信息

答案 4 :(得分:-1)

我认为这一决定的理由从未公布过。看起来像是一些内部架构限制。

在更抽象的层面上,它是有道理的。对象应该能够返回自身的字符串表示,没有理由让这种操作失败。

答案 5 :(得分:-1)

我找到了简单的解决方案:

当错误转换为字符串时,只返回__toString中的非字符串类型:NULL,FALSE甚至异常。

这将导致这样的输出(在php -a interactive SHELL中):

Catchable fatal error: Method MyClass::__toString() must return a string value in php shell code on line 1