45 votes

Pourquoi il est impossible de lever une exception à partir de __toString() ?

Pourquoi il est impossible de lever une exception à partir de __toString() ?

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

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

le code ci-dessus produit ceci :

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

On m'a indiqué http://php.net/manual/en/migration52.incompatible.php où ce comportement est décrit, mais pourquoi ? Y a-t-il des raisons de le faire ?

Peut-être que quelqu'un ici le sait ?

Sur la page de suivi des bogues, l'équipe de développement de php, comme d'habitude, ne dit rien, mais voir le manuel : http://bugs.php.net/50699

47voto

Rob Hruska Points 39151

Après quelques recherches, j'ai trouvé ceci, qui dit :

Johannes a expliqué que il n'y a aucun moyen de s'assurer qu'une exception levée lors d'un cast to string sera gérée correctement par le Zend Engine. et que cela ne changera pas, à moins que de grandes parties du moteur ne soient réécrites. Il ajoute qu'il y a eu des discussions sur ces questions dans le passé, et suggère à Guilherme de vérifier les archives.

En Johannes La référence ci-dessus est le gestionnaire de version de PHP 5.3, donc c'est probablement l'explication la plus "officielle" que vous puissiez trouver sur le comportement de PHP.

La section poursuit en mentionnant :

__toString() acceptera, bizarrement, de trigger_error() .

Tout n'est donc pas perdu en ce qui concerne le signalement des erreurs au sein de l'UE. __toString() .

10voto

Matchu Points 37755

Je pense que __toString est pirate et existe donc en dehors de la pile typique. Une exception lancée, alors, ne saurait pas où aller.

9voto

tacone Points 4362

En réponse à la réponse acceptée, j'ai trouvé une (peut-être) meilleure façon de gérer les exceptions à l'intérieur de l'entreprise __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;
    }
}

Cela suppose qu'il existe un gestionnaire d'exception défini, ce qui est le cas pour la plupart des frameworks. Comme pour les trigger_error méthode, en faisant cela, on défie le but de try..catch mais c'est quand même bien mieux que de vider la sortie avec echo . De plus, de nombreux cadres de travail transforment les erreurs en exceptions, donc trigger_error ne fonctionnera pas de toute façon.

En prime, vous obtiendrez un suivi complet de la pile, ainsi que les exceptions normales et le comportement normal en production de votre framework de choix.

Il fonctionne très bien avec Laravel, et je suis sûr qu'il fonctionnera avec la plupart des frameworks PHP modernes.

Capture d'écran pertinente :
note dans cet exemple, output() est appelé par un __toString() méthode.

__toString() exception caught by Laravel exception handler

5voto

cssBlaster21895 Points 2759

Il semble qu'à partir de la version 7.4 de php, il soit permis de lancer une exception à partir de __toString(). J'ai fait une vérification de la compatibilité avec php 7.2 qui l'a dit et a indiqué la Doctrine StaticReflectionClass y StaticReflectionProperty .

Veuillez trouver plus d'informations sur la proposition https://wiki.php.net/rfc/tostring_exceptions

-1voto

Yuval Adam Points 59423

Je ne pense pas que la justification de cette décision ait jamais été rendue publique. On dirait une limitation architecturale interne.

À un niveau plus abstrait, cela a du sens. Un objet devrait être capable de renvoyer une chaîne de caractères le représentant, il n'y a aucune raison pour que ce type d'action échoue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X