Почему невозможно исключить исключение из __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()
.
Я нашел простое решение:
Просто верните что-то вроде типа 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
Я не думаю, что обоснование этого решения когда-либо публиковалось. Похоже, какое-то внутреннее архитектурное ограничение.
На более абстрактном уровне это имеет смысл. Объект должен иметь возможность возвращать строковое представление самого себя, без причины для такого рода действий.