2009-05-08 26 views
86

Tal vez me falta algo en el manual de PHP, pero ¿cuál es exactamente la diferencia entre un error y una excepción? La única diferencia que puedo ver es que los errores y las excepciones se manejan de manera diferente. ¿Pero qué causa una excepción y qué causa un error?PHP: ¿excepciones contra errores?

Respuesta

70

Las excepciones son thrown - están destinadas a ser capturadas. Los errores son generalmente irrecuperables. Digamos, por ejemplo, que tienes un bloque de código que insertará una fila en una base de datos. Es posible que esta llamada falle (ID duplicada); querrá tener un "Error" que en este caso es una "Excepción". Cuando se va a insertar estas filas, puede hacer algo como esto ejecución del programa

try { 
    $row->insert(); 
    $inserted = true; 
} catch (Exception $e) { 
    echo "There was an error inserting the row - ".$e->getMessage(); 
    $inserted = false; 
} 

echo "Some more stuff"; 

continuará - porque 'atrapado' en la excepción. Una excepción se tratará como un error a menos que se capture. Le permitirá continuar la ejecución del programa después de que también falle.

+22

'Los errores son generalmente irrecuperables' <- en realidad, esto no es así t realmente cierto. 'E_ERROR' y' E_PARSE' son los dos errores irrecuperables más comunes (hay un par de otros) pero la gran mayoría de los errores que verá en dev son recuperables ('E_NOTICE',' E_WARNING' et al). Lamentablemente, el manejo de errores de PHP es un completo desastre: todo tipo de cosas desencadenan errores innecesariamente (la gran mayoría de las funciones del sistema de archivos, por ejemplo). En general, las excepciones son "la forma de OOP", pero desafortunadamente algunas de las API de OOP nativas de PHP usan errores en lugar de excepciones :-( – DaveRandom

+2

Normalmente no desea repetir los errores para el usuario ... –

+1

@DaveRandom E_NOTICE, E_WARNING son Por definición, ¿no son "errores"? Siempre pensé que eran "mensajes" PHP que se muestran para notificar al programador que algo podría estar mal con el código que escribió. – slhsen

1

Las excepciones se lanzan intencionalmente por código utilizando un lanzamiento, errores ... no tanto.

Los errores se producen como resultado de algo que no se maneja normalmente. (Errores IO, errores TCP/IP, errores de referencia nulos)

+1

Esto no es necesariamente cierto. En muchos casos, los errores se verifican y los códigos de retorno se envían intencionalmente de regreso según corresponda. De hecho, ese es el caso de todos los lenguajes no orientados a objetos. Las excepciones son solo eso, también, excepciones a la regla. En ambos casos, algo va mal, se nota y debe ser manejado. La carga de archivos PHP es un ejemplo del manejo intencional de errores a través de códigos de retorno - http://php.net/manual/en/features.file-upload.errors.php – evan

8

Una cosa para agregar aquí es sobre el manejo de excepciones y errores. A los efectos del desarrollador de la aplicación, tanto los errores como las excepciones son "cosas malas" que desea registrar para conocer los problemas que tiene su aplicación, de modo que sus clientes tengan una mejor experiencia a largo plazo.

Así que tiene sentido escribir un controlador de errores que haga lo mismo que lo que hace para las excepciones.

+0

¡Gracias por proporcionar el enlace! –

+0

@Alex Weinstein: el enlace está roto –

48

Por lo general, set_error_handler a una función que toma el error y arroja una excepción para que ocurra lo que ocurra, simplemente tendré que lidiar con excepciones. No más @file_get_contents simplemente agradable y limpio try/catch.

En situaciones de depuración, también tengo un manejador de excepciones que genera una página similar a asp.net. Estoy publicando esto en la carretera, pero si lo solicito, publicaré la fuente de ejemplo más adelante.

edición:

adición según lo prometido, he cortado y pegado algunos de mi código juntos para hacer una muestra. Guarde el siguiente archivo en mi estación de trabajo, you can NO LONGER see the results here (porque el enlace está roto).

<?php 

define('DEBUG', true); 

class ErrorOrWarningException extends Exception 
{ 
    protected $_Context = null; 
    public function getContext() 
    { 
     return $this->_Context; 
    } 
    public function setContext($value) 
    { 
     $this->_Context = $value; 
    } 

    public function __construct($code, $message, $file, $line, $context) 
    { 
     parent::__construct($message, $code); 

     $this->file = $file; 
     $this->line = $line; 
     $this->setContext($context); 
    } 
} 

/** 
* Inspire to write perfect code. everything is an exception, even minor warnings. 
**/ 
function error_to_exception($code, $message, $file, $line, $context) 
{ 
    throw new ErrorOrWarningException($code, $message, $file, $line, $context); 
} 
set_error_handler('error_to_exception'); 

function global_exception_handler($ex) 
{ 
    ob_start(); 
    dump_exception($ex); 
    $dump = ob_get_clean(); 
    // send email of dump to administrator?... 

    // if we are in debug mode we are allowed to dump exceptions to the browser. 
    if (defined('DEBUG') && DEBUG == true) 
    { 
     echo $dump; 
    } 
    else // if we are in production we give our visitor a nice message without all the details. 
    { 
     echo file_get_contents('static/errors/fatalexception.html'); 
    } 
    exit; 
} 

function dump_exception(Exception $ex) 
{ 
    $file = $ex->getFile(); 
    $line = $ex->getLine(); 

    if (file_exists($file)) 
    { 
     $lines = file($file); 
    } 

?><html> 
    <head> 
     <title><?= $ex->getMessage(); ?></title> 
     <style type="text/css"> 
      body { 
       width : 800px; 
       margin : auto; 
      } 

      ul.code { 
       border : inset 1px; 
      } 
      ul.code li { 
       white-space: pre ; 
       list-style-type : none; 
       font-family : monospace; 
      } 
      ul.code li.line { 
       color : red; 
      } 

      table.trace { 
       width : 100%; 
       border-collapse : collapse; 
       border : solid 1px black; 
      } 
      table.thead tr { 
       background : rgb(240,240,240); 
      } 
      table.trace tr.odd { 
       background : white; 
      } 
      table.trace tr.even { 
       background : rgb(250,250,250); 
      } 
      table.trace td { 
       padding : 2px 4px 2px 4px; 
      } 
     </style> 
    </head> 
    <body> 
     <h1>Uncaught <?= get_class($ex); ?></h1> 
     <h2><?= $ex->getMessage(); ?></h2> 
     <p> 
      An uncaught <?= get_class($ex); ?> was thrown on line <?= $line; ?> of file <?= basename($file); ?> that prevented further execution of this request. 
     </p> 
     <h2>Where it happened:</h2> 
     <? if (isset($lines)) : ?> 
     <code><?= $file; ?></code> 
     <ul class="code"> 
      <? for($i = $line - 3; $i < $line + 3; $i ++) : ?> 
       <? if ($i > 0 && $i < count($lines)) : ?> 
        <? if ($i == $line-1) : ?> 
         <li class="line"><?= str_replace("\n", "", $lines[$i]); ?></li> 
        <? else : ?> 
         <li><?= str_replace("\n", "", $lines[$i]); ?></li> 
        <? endif; ?> 
       <? endif; ?> 
      <? endfor; ?> 
     </ul> 
     <? endif; ?> 

     <? if (is_array($ex->getTrace())) : ?> 
     <h2>Stack trace:</h2> 
      <table class="trace"> 
       <thead> 
        <tr> 
         <td>File</td> 
         <td>Line</td> 
         <td>Class</td> 
         <td>Function</td> 
         <td>Arguments</td> 
        </tr> 
       </thead> 
       <tbody> 
       <? foreach ($ex->getTrace() as $i => $trace) : ?> 
        <tr class="<?= $i % 2 == 0 ? 'even' : 'odd'; ?>"> 
         <td><?= isset($trace[ 'file' ]) ? basename($trace[ 'file' ]) : ''; ?></td> 
         <td><?= isset($trace[ 'line' ]) ? $trace[ 'line' ] : ''; ?></td> 
         <td><?= isset($trace[ 'class' ]) ? $trace[ 'class' ] : ''; ?></td> 
         <td><?= isset($trace[ 'function' ]) ? $trace[ 'function' ] : ''; ?></td> 
         <td> 
          <? if(isset($trace[ 'args' ])) : ?> 
           <? foreach ($trace[ 'args' ] as $i => $arg) : ?> 
            <span title="<?= var_export($arg, true); ?>"><?= gettype($arg); ?></span> 
            <?= $i < count($trace['args']) -1 ? ',' : ''; ?> 
           <? endforeach; ?> 
          <? else : ?> 
          NULL 
          <? endif; ?> 
         </td> 
        </tr> 
       <? endforeach;?> 
       </tbody> 
      </table> 
     <? else : ?> 
      <pre><?= $ex->getTraceAsString(); ?></pre> 
     <? endif; ?> 
    </body> 
</html><? // back in php 
} 
set_exception_handler('global_exception_handler'); 

class X 
{ 
    function __construct() 
    { 
     trigger_error('Whoops!', E_USER_NOTICE);  
    } 
} 

$x = new X(); 

throw new Exception('Execution will never get here'); 

?> 
+0

Eso sería útil. Cualquier cosa para facilitar los tiempos que estoy hecho para tratar con PHP ayudará. :-) –

+0

Buen código, gracias. Sin embargo, no entiendo de dónde viene la clase X, ¿y cuál es su propósito? – Alec

+0

todo lo que está debajo de "set_exception_handler ('global_exception_handler');" es solo demo, no lo necesitará, solo para mostrar lo que sucedería en una situación de error que normalmente no es de excepción. – Kris

5

Re: "pero ¿qué es exactamente la diferencia entre un error y una excepción?"

Aquí hay muchas buenas respuestas sobre las diferencias. Añadiré algo de lo que aún no se ha hablado: el rendimiento. Específicamente, esto es por la diferencia entre lanzar/manejar excepciones y manejar un código de retorno (ya sea éxito o algún error). Por lo general, en php, esto significa devolver false o null, pero pueden ser más detallados, como con la carga de archivos: http://php.net/manual/en/features.file-upload.errors.php ¡Incluso podría devolver un objeto Exception!

He realizado algunas ejecuciones de rendimiento en diferentes idiomas/sistemas. En términos generales, el manejo de excepciones es aproximadamente 10,000 veces más lento que la verificación de un código de retorno de error.

Por lo tanto, si absolutamente, positivamente necesita para terminar la ejecución antes de que comenzara - bueno, estás de suerte porque viaje en el tiempo no existe. Sin desplazamiento en el tiempo, los códigos de retorno son la opción más rápida disponible.

Editar:

PHP está muy optimizado para el manejo de excepciones. Las pruebas del mundo real muestran que lanzar una excepción es solo 2-10 veces más lento que devolver un valor.

+2

Claro, pero la cantidad de ciclos perdidos al arrojar Excepciones está más que compensada por los poderes descriptivos adicionales que obtienes con Excepciones. Puede lanzar tipos específicos de excepciones, incluso agregar datos a la excepción para contener los códigos de error. Dudo seriamente tu reclamo de 10,000 * también. Incluso si tiene razón acerca de la diferencia de tiempo, el tiempo invertido en devolver y en contra de la nueva Execption, throw, catch en cualquier escenario del mundo real es probablemente tan minúsculo en comparación con el código ejecutado que definitivamente es una optimización prematura. Lanzar excepciones, son más agradables de tratar el 90% del tiempo. – gnarf

+1

1. 10,000x es preciso, con algunas variaciones basadas en el lenguaje y las opciones del compilador. 2. No tiene que devolver nulo/falso. Puede devolver un número - hasta códigos de retorno MAX_ULONG allí mismo. Alternativamente, puede devolver una cadena fallida y simplemente verificar si hay una cadena exitosa o int o null. 3. En escenarios del mundo real, cada ciclo de reloj cuenta. Facebook tiene 552 millones de usuarios activos diarios. Suponiendo que las excepciones sean solo 2 veces y que el usuario/pase de verificación tome .001s, eso significa que se ahorrará 153 horas de procesamiento cada día. En 10,000x ahorra 175 años. Solo para verificar los intentos de inicio de sesión, todos los días. – evan

+0

@evan: FYI, aquí probaron el código con excepciones y no parece ser más lento: http://stackoverflow.com/a/445094/260080 –

0

Una vez que se define set_error_handler(), el controlador de errores es similar a Exception. Vea el código a continuación:

<?php 
function handleErrors($e_code) { 
    echo "error code: " . $e_code . "<br>"; 
} 

set_error_handler("handleErrors"); 

trigger_error("trigger a fatal error", E_USER_ERROR); 
echo "after error."; //it would run if set_error_handler is defined, otherwise, it wouldn't show 
?> 
4

Creo que lo que está buscando es eso;

Los errores son las cosas estándar que estamos acostumbrados, como eco de una variable $ que no lo existe.
Las excepciones son solo de PHP 5 en adelante y se aplican cuando se trata de objetos.

que sea sencillo:

Las excepciones son los errores que se obtiene cuando se trata de objetos. La instrucción try/catch le permite hacer algo al respecto, y se usa de forma muy similar a la instrucción if/else. Intenta hacer esto, si hay algún problema, no importa, hazlo.

Si no "detecta" una excepción, se convierte en un error estándar.

Los errores son los errores básicos de php que generalmente detienen el script.

try/catch se utiliza a menudo para establecer conexiones de base de datos como DOP, que está bien si desea redirigir el guión o hacer algo más si el trabajo de conexión no funciona. Pero si solo desea mostrar el mensaje de error y detener el script, entonces no lo necesita, la excepción no detectada se convierte en un error fatal. O también puede usar una configuración de manejo de errores en todo el sitio.

Espero que ayude

+1

Las excepciones también se pueden usar con código de procedimiento en PHP. –

1

tengo la intención de darle una discusión más inusual de control de errores.

I construyó un muy buen gestor de errores en un lenguaje hace años, y aunque algunos de los nombres han cambiado, los principios de procesamiento de errores son los mismos hoy. Tenía un SO multitarea personalizado y tuve que ser capaz de recuperarme de los errores de datos en todos los niveles sin pérdidas de memoria, crecimiento de la pila o fallas. Entonces, lo que sigue es mi comprensión de cómo los errores y las excepciones deben operar y cómo difieren. Solo diré que no entiendo cómo funcionan las partes internas de la captura de prueba, así que estoy adivinando hasta cierto punto.

Lo primero que sucede bajo las cubiertas para el procesamiento de errores es pasar de un estado de programa a otro. ¿Cómo se hace eso? Voy a llegar a eso.

Históricamente, los errores son mayores y más simple, y las excepciones son más recientes y un poco más complejo y capaz. Los errores funcionan bien hasta que tenga que hacerlos burbujear, lo que es equivalente a pasarle un problema difícil a su supervisor.

Los errores pueden ser números, como números de error, y algunas veces con una o más cadenas asociadas. Por ejemplo, si se produce un error de lectura de archivo, es posible que pueda informar de qué se trata y, posiblemente, fallar correctamente.(Hay, es un paso adelante de simplemente estrellarse como en los viejos tiempos).

Lo que no se suele decir sobre las excepciones es que las excepciones son objetos en capas en una pila especial especial. Es como una pila de retorno para el flujo del programa, pero tiene un estado de retorno solo para intentos de error y capturas. (Solía ​​llamarlos ePush y ePop, y? Abort fue un lanzamiento condicional que se ePop y recuperar a ese nivel, mientras Abort era un dado completo o una salida.)

En la parte inferior de la pila está la información sobre el llamante inicial, el objeto que conoce el estado cuando se inició el intento externo, que a menudo es cuando se inició el programa. Además, o la siguiente capa en la pila, siendo los hijos, y los padres los padres, es el objeto de excepción del próximo bloque try/catch interno.

Si prueba en una oportunidad, está apilando la prueba interna en la parte superior de la prueba externa. Cuando se produce un error en el intento interno y la captura interna no puede manejarlo o el error se lanza al intento externo, el control pasa al bloque catch externo (objeto) para ver si puede manejar el error, es decir, su supervisor. Lo que realmente hace esta pila de errores es marcar y restaurar el flujo del programa y el estado del sistema, es decir, permite que un programa no bloquee la pila de retorno y estropee otras cosas (datos) cuando las cosas ir mal. Por lo tanto, también guarda el estado de otros recursos, como los grupos de asignación de memoria, y así puede limpiarlos cuando se realiza la captura. En general, esto puede ser algo muy complicado, y es por eso que el manejo de excepciones a menudo es lento. En general, un poco de estado necesita entrar en estos bloques de excepción.

Así que un bloque try/catch establece un estado al que se puede volver si todo lo demás se arruina. Es como un padre. Cuando nuestras vidas se arruinan podemos volver a caer en el regazo de nuestros padres y ellos volverán a estar bien.

Espero que no te haya decepcionado.

8

La respuesta merece hablar sobre el elefante en la habitación

errores es la vieja manera de manejar una condición de error en tiempo de ejecución. Normalmente, el código haría una llamada a algo como set_error_handler antes de ejecutar algún código. Siguiendo la tradición de interrupciones de lenguaje ensamblador. Así es como se vería algún código BASIC.

on error :divide_error 

print 1/0 
print "this won't print" 

:divide_error 

if errcode = X 
    print "divide by zero error" 

era difícil asegurarse de que set_error_handler se llamaría con el valor correcto. Y lo que es peor, una llamada podría hacerse a un procedimiento separado que cambiaría el controlador de errores. Además, muchas veces las llamadas se intercalaron con llamadas y manipuladores set_error_handler. Fue fácil para el código perder el control rápidamente. El manejo de excepciones vino al rescate al formalizar la sintaxis y la semántica de lo que el buen código realmente estaba haciendo.

try { 
    print 1/0; 
    print "this won't print"; 
} catch (DivideByZeroException $e) { 
    print "divide by zero error"; 
} 

No hay función separada o riesgo de llamar al controlador de error incorrecto. Ahora se garantiza que el código está en el mismo lugar. Además obtenemos mejores mensajes de error.

PHP solía tener solo el manejo de errores, cuando muchos otros lenguajes ya habían evolucionado al modelo preferible de manejo de excepciones. Finalmente, los creadores de PHP implementaron el manejo de excepciones. Pero es probable que sean compatibles con el código anterior, mantuvieron el manejo de errores y proporcionaron una manera de hacer que el manejo de errores se viera como el manejo de excepciones. Excepto por eso, no hay garantía de que algún código no pueda restablecer el manejador de errores, que era precisamente lo que el manejo de excepciones debía proporcionar.

Respuesta final

Los errores que se han codificado antes de la implementación de manejo de excepciones, son errores sigue siendo probable. Nuevos errores son probables excepciones. Pero no hay ningún diseño o lógica a la cual se hayan cometido errores y que sean excepciones. Simplemente se basa en lo que estaba disponible en el momento en que fue codificado, y la preferencia del programador que lo codifica.

+1

Esta es la verdadera razón por la cual las excepciones y los errores coexisten. Si está diseñado desde cero, php solo debe incluir uno u otro. –

5

Como se indica en otras respuestas, la configuración del manejador de errores para el lanzador de excepción es la mejor manera de manejar los errores en PHP. Yo uso un poco de configuración más simple:

set_error_handler(function ($errno, $errstr, $errfile, $errline) { 
     if (error_reporting()) { 
       throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); 
     } 
}); 

Tenga en cuenta el cheque error_reporting() para mantener @ de trabajo del operador. Además, no hay necesidad de definir una excepción personalizada, PHP tiene una buena clase para eso.

El gran beneficio de arrojar excepciones es que la excepción tiene un rastro de pila asociado a ellos, por lo que es fácil encontrar dónde está el problema.

Cuestiones relacionadas