Manejo de errores en PHP al usar MVC

12

He estado usando mucho Codeigniter recientemente, pero una cosa que me molesta es manejar los errores y mostrarlos al usuario. Nunca he sido bueno manejando errores sin que se ensucie. Mi principal preocupación es cuando se devuelven errores al usuario.

Es una buena práctica usar excepciones y lanzar / atrapar excepciones en lugar de devolver 0 o 1 desde las funciones y luego usar if / else para manejar los errores. De este modo, es más fácil informar al usuario sobre el problema.

Tiendo a alejarme de las excepciones. Mi tutor de Java en la universidad hace algunos años me dijo que "las excepciones no deberían usarse en el código de producción, es más para la depuración". Tengo la sensación de que estaba mintiendo.

Pero, en un ejemplo, tengo un código que agrega un usuario a una base de datos. Durante el proceso, más de 1 cosa podría salir mal, como un problema de la base de datos, una entrada duplicada, un problema del servidor, etc. Cuando ocurre un problema durante el registro, el usuario debe saberlo.

¿Cuál es la mejor manera de manejar los errores en PHP, teniendo en cuenta que estoy usando un marco MVC?

    
pregunta James Jeffery 04.02.2012 - 21:38

3 respuestas

13
  

¿Es una buena práctica usar excepciones y lanzar / atrapar excepciones?   en lugar de devolver 0 o 1 desde las funciones y luego usar if / else para   Manejar los errores. Por lo tanto, haciendo más fácil informar al usuario sobre la   problema.

¡Sí, sí, sí!

Si desea tener un código limpio, debería usar casi exclusivamente excepciones y no molestarse en usar códigos de error. Los códigos de error no tienen sentido. Casi siempre están vinculados a una constante numérica que no revela mucha información. Puede hacer que su código sea ilegible y dificultarán la propagación de datos junto con el error.

Sin embargo, las excepciones son clases y pueden contener cualquier información que te guste. Así que el usuario ingresó una entrada incorrecta, como 'abc' para un campo numérico. Con un código de error, no podría propagar esta información al manejador del error sin mucho burbujeo. Algo que las excepciones prevén de forma gratuita. Además, las excepciones le permiten tener valores de retorno significativos en funciones y métodos, mientras que todavía tiene una manera de fallar con elegancia. Aún mejor, las excepciones se propagan directamente al lugar donde desea manejarlas. Imagina la cantidad de código de espagueti que necesitarás para propagar un código de error con datos significativos a un manejador de una o dos capas arriba.

Además, las excepciones se expresan mucho más semánticamente que los códigos de error. Los códigos de error conducen a un código spaghetti donde el manejo de excepciones lleva a un código limpio.

Además, es fácil olvidar los códigos de estado. En lenguajes como Java, se ve obligado a manejar excepciones (algo que, por ejemplo, C # pierde).

  

¿Cuál es la mejor manera de manejar los errores en PHP, teniendo en cuenta que estoy   utilizando un marco MVC.

Use excepciones y manejelas en sus controladores.

    
respondido por el Falcon 04.02.2012 - 23:20
13
  

Es una buena práctica usar excepciones y lanzar / atrapar excepciones en lugar de devolver 0 o 1 desde las funciones y luego usar if / else para manejar los errores. De este modo, es más fácil informar al usuario sobre el problema.

No, no, no!

No mezclar excepciones y errores. Las excepciones son, bueno, excepcionales. Los errores no lo son. Cuando le pide a un usuario que ingrese una cantidad de un producto, y el usuario ingresa "hola", es un error. No es una excepción: no hay nada excepcional en ver una entrada no válida del usuario. ¿Por qué no se pueden usar excepciones en casos no excepcionales, como al validar una entrada? Otras personas ya lo explicaron y mostraron una alternativa válida para la validación de entrada.

Esto también significa que al usuario no le importan sus excepciones , y que mostrar las excepciones es tanto hostil como peligroso . Por ejemplo, una excepción durante la ejecución de una consulta SQL a menudo revela la consulta en sí. ¿Está seguro de que quiere correr el riesgo de mostrar ese mensaje a todos?

  

más de una cosa puede salir mal, como un problema en la base de datos, una entrada duplicada, un problema en el servidor, etc. Cuando ocurre un problema durante el registro, el usuario debe saberlo.

Mal. Como usuario, no necesito conocer los problemas de la base de datos, las entradas duplicadas, etc. Realmente no me importan los problemas de sus . Lo que hago lo que necesito saber es que ingresé un nombre de usuario que ya existe. Como ya se dijo, una entrada incorrecta de mi parte debe desencadenar un error, no una excepción.

¿Cómo dar salida a esos errores? Depende del contexto. Para un nombre de usuario ya usado, me gustaría ver una pequeña bandera roja cerca del nombre de usuario, incluso antes de enviar el formulario, diciendo que el nombre de usuario ya está en uso. Sin JavaScript, debe aparecer la misma bandera después del envío.

Paraotroserrores,mostraríaunapáginacompletaconunerror,oelegiríaotraformadeinformaralusuariodequealgosaliómal(porejemplo,unmensajequeapareceráyluegodesapareceráenlapartesuperiordelapágina).Lapreguntaserelacionamáscon experiencia del usuario que con la programación.

Desde el punto de vista de los programadores, dependiendo del tipo de error, lo propagará de diferentes maneras. Por ejemplo, en el caso de que ya se haya tomado un nombre de usuario, una solicitud AJAX a http://example.com/?ajax=1&user-exists=John devolverá un objeto JSON que indica:

  • Que el usuario ya existe,
  • El mensaje de error para mostrar al usuario.

El segundo punto es importante: desea asegurarse de que aparezca el mismo mensaje al enviar el formulario con JavaScript deshabilitado y al escribir un nombre de usuario duplicado con JavaScript habilitado. ¡No desea duplicar el texto del mensaje de error en el código fuente del lado del servidor y en JavaScript!

Esta es en realidad la técnica utilizada por los sitios web de Stack Exhange. Por ejemplo, si intento actualizar mi propia respuesta, la respuesta AJAX contiene el error que se muestra:

{"Success":false,"Warning":false,"NewScore":0,"Message":"You can't vote for your own post.",
"Refresh":false}

También puede elegir otro enfoque y preestablecer los errores en la página HTML antes de que se complete el formulario. Pros: no tienes que enviar el mensaje de error en la respuesta AJAX. Contras: ¿qué pasa con la accesibilidad? Intente navegar por la página sin CSS y verá aparecer todos los errores posibles.

    
respondido por el Arseni Mourzenko 04.02.2012 - 22:11
6

Considera esta pequeña clase práctica:

class FunkyFile {               

    private $path;
    private $contents = null;

    public function __construct($path) { 
        $this->setPath($path); 
    }

    private function setPath($path) {
        if( !is_file($path) || !is_readable($path) ) 
            throw new \InvalidArgumentException("Hm, that's not a valid file!");

        $this->path = realpath($path);
        return $this; 
    }

    public function getContents() {
        if( is_null($this->contents) ) {
            $this->contents = @file_get_contents( $this->path );
            if($this->contents === false) 
                throw new \Exception("Hm, I can't read the file, for some reason!");                                 
        }

        return $this->contents;            
    }

}

Eso es un uso perfectamente perfecto de las excepciones. Desde la perspectiva de FunkyFile's , no se puede hacer absolutamente nada para remediar la situación si la ruta no es válida o si falla file_get_contents . Una situación verdaderamente excepcional;)

¿Pero hay algún valor para que su usuario sepa que se ha topado con una ruta de archivo incorrecta, en algún lugar de su código? Por ejemplo:

class Welcome extends Controller {

    public function index() {

        /**
         * Ah, let's show user this file she asked for
         */                 
        try {
            $file = new File("HelloWorld.txt");
            $contents = $file->getContents();   
            echo $contents;
        } catch(\Exception $e) {
            log($e->getMessage());

            echo "Sorry, I'm having a bad day!"; 
        }                           
    }        
}

Además de decirle a la gente que tienes un mal día, tus opciones son:

  1. Fallback

    ¿Tiene otra forma de obtener la información? En mi ejemplo simple anterior, no parece probable, pero considere un esquema de base de datos maestro / esclavo. Es posible que el maestro no haya respondido, pero quizás, solo tal vez, el esclavo todavía esté ahí fuera (o viceversa).

  2. ¿Es culpa del usuario?

    ¿El usuario envió una entrada defectuosa? Bueno, cuéntaselo a ella. Puede ladrar un mensaje de error o ser amable y acompañarlo con un formulario para que pueda escribir la ruta correcta.

  3. ¿Es tu culpa?

    Y por usted, me refiero a cualquier cosa que no sea no el usuario, por lo que va desde que usted escribe una ruta de archivo incorrecta, hasta que algo va mal en su servidor. Estrictamente hablando, es hora de un 503 error HTTP , ya que, bueno, el servicio no está disponible. CI tiene una función show_404() , puede crear fácilmente show_503() .

Un consejo, debe tener en cuenta las excepciones deshonestas. CodeIgniter es un pedazo de código desordenado y nunca se sabe cuándo aparecerá una excepción. De manera similar, puede olvidarse de sus propias excepciones, y la opción más segura es implementar un controlador de excepciones de captura de todos. En PHP, puedes hacer eso con set_exception_handler :

function FunkyExceptionHandler($exception) {
    if(ENVIRONMENT == "production") {
        log($e->getMessage());
        show_503();
    } else {
        echo "Uncaught exception: " , $exception->getMessage(), "\n";
    }   
}

set_exception_handler("FunkyExceptionHandler");

Y también puede encargarse de errores deshonestos, a través de set_error_handler . Puede escribir el mismo controlador que para las excepciones o, alternativamente, convertir todos los errores a ErrorException y dejar que el controlador de excepciones los resuelva:

function FunkyErrorHandler($errno, $errstr, $errfile, $errline) {
    // will be caught by FunkyExceptionHandler if not handled
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("FunkyErrorHandler");
    
respondido por el yannis 04.02.2012 - 23:21

Lea otras preguntas en las etiquetas