2010-03-12 9 views
43

¿Por qué es imposible lanzar una excepción desde __toString()?¿Por qué es imposible lanzar una excepción desde __toString()?

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

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

el código anterior produce esto:

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

Se me mostró http://php.net/manual/en/migration52.incompatible.php donde se describe este comportamiento, pero ¿por qué? ¿Alguna razón para hacer eso?

¿Puede alguien aquí saber esto?

En seguimiento de errores php-dev-equipo como de costumbre, pero no dice nada véase el manual: http://bugs.php.net/50699

Respuesta

45

Después de un par de búsquedas que he encontrado esto, que dice:

Johannes explicó que no hay manera de garantizar que una excepción lanzada durante un elenco de cadena sería manejada correctamente por el motor de Zend, y que esto no cambiará a menos que se reescriban grandes partes del motor. Añadió que ha habido discusiones sobre estos temas en el pasado, y sugirió que Guilherme revise los archivos.

El Johannes referencia más arriba es el Manager versión 5.3 de PHP, por lo que es probablemente lo más "oficial" una explicación como se puede encontrar por qué PHP se comporta de esta manera.

La sección se enciende mencionar:

__toString() será, curiosamente, aceptar trigger_error().

Así que no todo se pierde en términos de informe de error dentro de __toString().

+2

heh, gracias. pero trigger_error() no puede reemplazar try/catch solo porque es global y try/catch es concreto. – zerkms

+4

@zerkms - Es cierto, no es un reemplazo. Quizás si suficientes personas expresan sus opiniones, reescribirán el Zend Engine. :) –

+1

también, muchos frameworks detectan errores y los vuelven a lanzar como excepciones, lo que devolvería exactamente el mismo problema. –

10

Mi conjetura sería que __toString es hacker y por lo tanto existe fuera de la pila típica. Una excepción arrojada, entonces, no sabría a dónde ir.

+1

sí, la naturaleza hacker es la razón más adecuada en mi humilde opinión. – zerkms

+3

especialmente porque 'echo $ a -> __ toString()' puede arrojar una excepción, pero 'echo $ a' no puede. – Ponkadoodle

+1

__toString no es hackish, es una forma completamente estándar de proporcionar una representación de cadena de un objeto. La diferencia entre (string) $ a y $ a -> __ toString() está en la forma en que el motor maneja sus llamadas, es lo mismo que las diferencias entre $ x-> something y $ x -> __ call ("something") . Una es una llamada directa a una función en el objeto (una función que empieza por __) y la otra es un método mágico manejado internamente por el motor Zend. –

-1

No creo que el motivo de esta decisión haya sido publicitado alguna vez. Parece una limitación arquitectónica interna.

En un nivel más abstracto, tiene sentido. Un objeto debería ser capaz de devolver una representación de cadena de sí mismo, no hay razón para que ese tipo de acción falle.

+0

"Un objeto debería ser capaz de devolver una representación de cadena de sí mismo, no hay razón para que ese tipo de acción falle". jeje :-) echo $ a -> __ toString(); lo arroja ;-) y esto es esperado. en este caso, el método también debería poder devolver algo a la cadena, pero no, recibimos una excepción no controlada. – zerkms

+1

La excepción que obtiene no se arroja desde '__toString() ' –

+0

Pero quiero decir" Un objeto debería ser capaz de devolver una representación de cadena de sí mismo "debería ser cierto también para" un método debería ser capaz de devolver algo a eco " como lo mencionaste. pero es falso ps: la excepción que obtuve fue lanzada desde __toString() – zerkms

8

en respuesta a la respuesta aceptada, se me ocurrió una (tal vez) una mejor manera de manejar excepciones dentro __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; 
    } 
} 

esto supone que es un manejador de excepciones definido, que es el caso para la mayoría de los marcos . Al igual que con el método trigger_error, haciendo esto desafiará el propósito de try..catch, pero aún así es mucho mejor que la salida de dumping con echo. Además, muchos frameworks transforman errores en excepciones, por lo que trigger_error no funcionará de todos modos.

Como una ventaja adicional, obtendrás un stack-trace completo como con las excepciones normales y el comportamiento normal de dev-production del framework de tu elección.

Funciona muy bien en Laravel, y estoy bastante seguro de que funcionará en casi todos los frameworks PHP modernos.

pantalla relevante:
nota: en este ejemplo, output() es llamado por un método __toString().

__toString() exception caught by Laravel exception handler

0

He encontrado una solución sencilla:

devuelve otra como de tipo no-cadena en __toString cuando se produce la conversión de error de cadena: NULL, FALSO o incluso Excepción.

Esto provocará una salida como ésta (en php -a interactiva SHELL):

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

¿Solución para qué? – zerkms

+0

Su trabajo para mí: cuando se produce un error fatal en la conversión en __toString no es deseado. Entonces, el error fatal que se puede atrapar se puede procesar, en lugar de obtener solo un error fatal en __toString – user1633548

+0

No estoy seguro de haber leído la pregunta o no. Solo para recordarle: le pregunté ** ¿POR QUÉ **? Es imposible hacerlo. – zerkms

Cuestiones relacionadas