2010-03-12 7 views
43

Pourquoi il est impossible de lancer une exception de __toString()?Pourquoi est-il impossible de lancer une exception 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 

je montrai http://php.net/manual/en/migration52.incompatible.php où ce comportement est décrit, mais pourquoi? Des raisons de faire ça?

Peut être quelqu'un ici sait cela?

A Bug Tracker php-dev-team comme d'habitude, mais ne dit rien voir manuel: http://bugs.php.net/50699

Répondre

45

Après des recherches couple je trouve cela, qui dit:

Johannes a expliqué que il n'y a aucun moyen de veiller à ce qu'une exception levée lors d'une fonte à la chaîne serait traitée correctement par le moteur Zend, et que cela ne changera pas à moins que de grandes parties du moteur ne soient réécrites. Il a ajouté qu'il y a eu des discussions sur de telles questions dans le passé, et a suggéré que Guilherme vérifie les archives.

Le Johannes référencé ci-dessus est le PHP 5.3 Release Manager, il est probablement « officielle » une explication que vous trouverez peut-être la raison pour laquelle PHP se comporte de cette façon.

La section poursuit en mentionnant:

__toString() sera, assez étrangement, accepter trigger_error().

Donc, tout n'est pas perdu en termes de rapport d'erreur dans __toString().

+2

heh, merci. mais trigger_error() ne peut pas remplacer try/catch simplement parce que c'est global et try/catch est concret. – zerkms

+4

@zerkms - C'est vrai, ce n'est pas un remplacement. Peut-être que si suffisamment de gens expriment leurs opinions, ils réécriront le moteur Zend. :) –

+1

aussi, de nombreux frameworks attrapent les erreurs et les relancent comme des exceptions - ce qui ramènerait exactement le même problème. –

10

Je dirais que __toString est hackish et existe donc en dehors de la pile typique. Une exception levée ne saurait donc pas où aller.

+1

oui, la nature hackish est la raison la plus appropriée IMHO. – zerkms

+3

d'autant plus que 'echo $ a -> __ toString()' peut lancer une exception, mais 'echo $ a' ne peut pas. – Ponkadoodle

+1

__toString n'est pas hackish, c'est une manière complètement standard de fournir une représentation de chaîne d'un objet. La différence entre (chaîne) $ a et $ a -> __ toString() est dans la façon dont le moteur gère vos appels, c'est la même chose que les différences entre $ x-> something et $ x -> __ call ("quelque chose") . L'un est un appel direct à une fonction dans l'objet (une fonction qui commence par __) et l'autre est une méthode magique gérée en interne par le moteur Zend. –

-1

Je ne pense pas que la raison de cette décision ait jamais été publiée. On dirait une limitation architecturale interne. Sur un plan plus abstrait, cela a du sens. Un objet devrait être capable de retourner une représentation de chaîne de lui-même, aucune raison pour que ce genre d'action échoue.

+0

"Un objet doit pouvoir renvoyer une représentation de chaîne de lui-même, aucune raison pour que ce type d'action échoue." hehe :-) echo $ a -> __ toString(); le jette ;-) et c'est prévu. dans ce cas, la méthode devrait également être en mesure de retourner quelque chose casted à la chaîne, mais non, nous obtenons une exception non gérée. – zerkms

+1

L'exception que vous obtenez n'est pas lancée de '__toString()' –

+0

Mais je veux dire "Un objet devrait pouvoir retourner une chaîne de caractères de lui-même" devrait être vrai aussi pour "une méthode devrait pouvoir retourner quelque chose à l'écho" comme vous l'avez mentionné. mais c'est faux. ps: l'exception que j'ai reçue a été renvoyée de __toString() – zerkms

8

en réponse à la réponse acceptée, je suis venu avec un (peut-être) meilleure façon de gérer les exceptions à l'intérieur __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 est un gestionnaire d'exception définie, ce qui est le cas pour la plupart des cadres . Comme avec la méthode trigger_error, cela va défier le but de try..catch, mais il est encore mieux que la sortie de dumping avec echo. En outre, de nombreux cadres transforment les erreurs en exceptions, donc trigger_error ne fonctionnera pas de toute façon. Comme bonus supplémentaire, vous obtiendrez une pile-trace complète comme avec les exceptions normales et le comportement normal de dev-production de votre cadre de choix.

Fonctionne très bien à Laravel, et je suis sûr que cela fonctionnera à peu près tous les frameworks PHP modernes là-bas.

Screenshot pertinents:
Note: dans cet exemple, output() est appelé par une méthode __toString().

__toString() exception caught by Laravel exception handler

0

J'ai trouvé une solution simple:

juste retour quelque chose comme type non-chaîne dans __toString lorsque la conversion d'erreur en chaîne se produit: NULL, FALSE ou même exception.

Cela entraînera la sortie comme celui-ci (en php -a shell interactif):

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

Solution pour quoi faire? – zerkms

+0

Son travail pour moi: lors d'une erreur fatale sur la conversion dans __toString est indésirable. Ensuite, l'erreur fatale attrapable peut être traitée à la place juste obtenir une erreur fatale dans __toString – user1633548

+0

Je ne suis pas sûr si vous avez lu la question ou non. Juste pour vous rappeler: j'ai demandé ** POURQUOI ** c'est impossible de le faire. – zerkms

Questions connexes