Почему невозможно исключить исключение из __toString ()?

Почему невозможно исключить исключение из __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

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

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

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

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

__toString() будет, как ни странно, принять trigger_error () .

Поэтому не все потеряно с точки зрения сообщения об ошибках в __toString() .

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

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

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

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

Это вызовет такой вывод (в php -a интерактивном SHELL):

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

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

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