Почему невозможно сгенерировать исключение из __toString ()? - PullRequest
44 голосов
/ 12 марта 2010

Почему невозможно сгенерировать исключение из __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, где это поведение описано, но почему? Есть ли для этого причины?

Может быть, кто-нибудь здесь знает это?

На багтрекере php-dev-team как обычно ничего не говорит, но видит руководство: http://bugs.php.net/50699

Ответы [ 6 ]

46 голосов
/ 12 марта 2010

После пары поисков я нашел это, которое говорит:

Йоханнес объяснил, что нет способа гарантировать, что исключение, сгенерированное во время приведения к строке, будет правильно обрабатываться Zend Engine , и что это не изменится, если не будут обработаны большие части Engine. переписаны. Он добавил, что в прошлом обсуждались подобные вопросы, и предложил Гильерме проверить архивы.

Йоханнес , упомянутый выше, является менеджером релизов PHP 5.3, так что это, вероятно, столь же "официальное" объяснение, как вы можете найти, почему PHP ведет себя таким образом.

В этом разделе упоминается:

__toString(), как ни странно, примет trigger_error () .

Так что не все потеряно с точки зрения сообщений об ошибках в __toString().

10 голосов
/ 12 марта 2010

Я думаю, что __toString является хакерским и поэтому существует вне типичного стека. Брошенное исключение не будет знать, куда идти.

8 голосов
/ 05 декабря 2014

в ответ на принятый ответ я предложил (возможно) лучший способ обработки исключений внутри __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 все равно не будет работать.

В качестве дополнительного бонуса вы получите полную трассировку стека, как с обычными исключениями, так и с обычным поведением dev-production вашего фреймворка.

Очень хорошо работает в Laravel, и я уверен, что он будет работать практически во всех современных PHP-фреймворках.

Снимок экрана:
note : в этом примере output() вызывается методом __toString().

__toString() exception caught by Laravel exception handler

1 голос
/ 28 июня 2019

Похоже, что с php 7.4 разрешено выбрасывание исключения из __toString (). У меня была проверка совместимости php7.2, и он сказал так и указал Doctrine StaticReflectionClass и StaticReflectionProperty .

Более подробная информация о предложении https://wiki.php.net/rfc/tostring_exceptions

.
0 голосов
/ 25 декабря 2014

Я нашел простое решение:

Просто возвращайте что-то вроде нестрокового типа в __toString, когда происходит преобразование ошибки в строку: NULL, FALSE или даже Exception.

Это приведет к выводу следующим образом (в php -a интерактивная оболочка):

Catchable fatal error: Method MyClass::__toString() must return a string value in php shell code on line 1
0 голосов
/ 12 марта 2010

Я не думаю, что обоснование этого решения было когда-либо опубликовано. Похоже на некоторое внутреннее архитектурное ограничение.

На более абстрактном уровне это имеет смысл. Объект должен иметь возможность возвращать строковое представление самого себя, без каких-либо причин сбоя такого рода действия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...