1. Code
  2. PHP

Whoops! Errores de PHP para Cool Kids

Scroll to top

Spanish (Español) translation by CYC (you can also view the original English article)

Whoops es una pequeña biblioteca, disponible como paquete Composer , que te ayuda a manejar errores y excepciones en tus proyectos PHP.

Fuera de la caja, obtienes una página de error elegante, intuitiva e informativa cada vez que algo sale mal en tu aplicación. Aún mejor, todo esto es un conjunto de herramientas muy sencillo, pero flexible, para tratar los errores de una manera que tenga sentido para lo que sea que esté haciendo.

Las principales características de la biblioteca son:

  • Página detallada e intuitiva para errores y excepciones.
  • Vista de código para todos los marcos
  • Céntrese en el análisis de errores / excepciones a través del uso de dispositivos intermedios / manejadores simples y personalizados
  • Soporte para solicitudes JSON y AJAX.
  • Proveedores incluidos para los proyectos Silex y Zend a través de los proveedores incluidos, e incluidos como parte del núcleo de Laravel 4
  • Base de código limpia, compacta y probada, sin dependencias adicionales
imageimageimage

Whoops logra esto a través de un sistema de manejadores apilados. Usted le dice a Whoops qué manejadores desea usar (puede elegir entre los manejadores incluidos o hacer los suyos propios), y si sucede algo, todos los manejadores reciben, en orden, una oportunidad de hacer algo, esto puede ser cualquier cosa, desde analizar el error (Whoops hace que sea más fácil extraer información significativa de un error o una excepción), para mostrar pantallas de error útiles (como el PrettyPageHandler incorporado , que le da la apariencia atractiva de la página de arriba).

Vamos a intentarlo, primero, observando lo básico, y luego intentando construir nuestro propio controlador con Whoops y el marco de trabajo de Laravel. Para esta breve guía, asumiré que te sientes moderadamente cómodo con PHP y que has oído hablar de Composer. Si este no es el caso, lea sobre esto aquí en Nettuts + .


Instalando Whoops

Cree un directorio para su proyecto, cámbielo, cree un archivo composer.json con el requisito Whoops e instálelo. Whoops (a partir de la versión 1.0.5) no tiene dependencias, por lo que solo tomará un segundo.

1
$ cd /path/to/your/project
2
$ composer require filp/whoops 1.*
3
$ composer install

Usando Whoops: Lo básico

Para ver esa elegante página de errores en acción, configuremos Whoops y aseguremos que algo se rompa lanzando una excepción dentro de nuestro código. Crea un archivo dentro del directorio de tu proyecto; para esta guía, digamos que se llama, index.php .

1
$ cd /path/to/your/project
2
$ your-favorite-editor index.php

Debido a que instalamos Whoops with Composer, y es compatible con PSR-0, todo lo que necesitamos hacer es requerir el autocargador Composer y estamos listos para comenzar a usar la biblioteca dentro de nuestro propio código.

1
<?php
2
# index.php

3
require __DIR__ . "/vendor/autoload.php";
4
5
$whoops = new Whoops\Run();
6
$whoops->pushHandler(new Whoops\Handler\PrettyPageHandler());
7
8
// Set Whoops as the default error and exception handler used by PHP:

9
$whoops->register();  
10
11
throw new RuntimeException("Oopsie!");
12
?>

Si ya tiene un servidor web en ejecución, continúe y acceda al archivo que acaba de crear. No olvide: si está utilizando PHP 5.4, puede aprovechar el servidor de desarrollo incorporado, de esta manera:

1
$ cd /path/to/your/project
2
$ php -S localhost:8080

Esto es lo que obtendrás:

imageimageimage

Bastante limpio, ¿verdad? Los manejadores, ellos mismos, pueden exponer opciones para modificar o aumentar su comportamiento. Por ejemplo, entre otras cosas, puede establecer el título de la página de error predeterminada e incluso insertar información adicional:

1
<?php
2
# index.php

3
$whoops = new Whoops\Run();
4
5
// Configure the PrettyPageHandler:

6
$errorPage = new Whoops\Handler\PrettyPageHandler();
7
8
$errorPage->setPageTitle("It's broken!"); // Set the page's title

9
$errorPage->setEditor("sublime");         // Set the editor used for the "Open" link

10
$errorPage->addDataTable("Extra Info", array(
11
      "stuff"     => 123,
12
      "foo"       => "bar",
13
      "useful-id" => "baloney"
14
));
15
16
$whoops->pushHandler($errorPage);
17
$whoops->register();
18
19
throw new RuntimeException("Oopsie!");
20
?>
imageimageimage

Además, dado que esto es simplemente un controlador Whoops regular, podemos mezclar y combinar con otros controladores para lograr resultados más dinámicos. Imaginemos que estás trabajando en un sitio web basado en AJAX + JSON. En este momento, si su aplicación fallara de alguna manera, obtendría un montón de HTML desagradable en el futuro, cuando esperaba a JSON. No es gran cosa:

1
<?php
2
# index.php

3
$whoops->pushHandler(new Whoops\Handler\PrettyPageHandler());
4
$whoops->pushHandler(new Whoops\Handler\JsonResponseHandler());
5
6
$whoops->register();
7
8
throw new RuntimeException("Oopsie!");

Eso es. Ahora, si algo falla durante una solicitud AJAX, Whoops responderá con una respuesta JSON que detalla el error. Si NO es una solicitud de AJAX, continuará viendo la página de error regular. Si ocurre un error, Whoops se filtrará a través de cada uno de los manejadores registrados (comenzando en el último manejador que se registrará), y les dará la oportunidad de analizar, modificar y responder a la solicitud.

Ahora que tiene una idea general de cómo funciona Whoops, vamos a intentar construir nuestro propio controlador con Whoops y el marco Laravel 4 .


Whoops y Laravel 4

Laravel 4 agrupa a Whoops como un controlador de excepción central, habilitado de forma predeterminada en el modo de desarrollo, incluyendo un esquema de color personalizado por Dayle Rees :

imageimageimage

Si no ha instalado laravel sin embargo, la cabeza de más y siga los pasos de la instalación. Laravel tiene una amplia cobertura en Nettuts + y Tuts + Premium , por lo que encontrarás mucho entrenamiento aquí, si deseas profundizar más.

Para los próximos pasos, asumiré que te sientes un poco cómodo con los conceptos básicos de Laravel 4. Sin embargo, aunque no lo estés, debería ser fácil de seguir.

Si está en modo de desarrollo (depuración), Whoops está disponible a través del contenedor IoC como whoops , y preajustado con uno de los dos manejadores: PrettyPageHandler o JsonResponseHandler , como whoops.handler (los mismos dos de los que acabamos de hablar). Ambos de estos controladores exponen métodos adicionales útiles, como ha visto anteriormente con el PrettyPageHandler . Al acceder a estos servicios, podemos comenzar a personalizar nuestra experiencia de Whoops dentro del marco.

En aras de la simplicidad, en su archivo app / route.php , vamos a conectarnos al servicio Whoops y establecer un título de página personalizado para nuestras páginas de error:

1
<?php
2
#app/routes.php

3
4
/*

5
|--------------------------------------------------------------------------

6
| Application Routes

7
|--------------------------------------------------------------------------

8
*/
9
10
use Whoops\Handler\PrettyPageHandler;
11
12
// Use the Laravel IoC container to get the Whoops\Run instance, if whoops

13
// is available (which will be the case, by default, in the dev

14
// environment)

15
if(App::bound("whoops")) {
16
    // Retrieve the whoops handler in charge of displaying exceptions:

17
    $whoopsDisplayHandler = App::make("whoops.handler");
18
19
    // Laravel will use the PrettyPageHandler by default, unless this

20
    // is an AJAX request, in which case it'll use the JsonResponseHandler:

21
    if($whoopsDisplayHandler instanceof PrettyPageHandler) {
22
23
        // Set a custom page title for our error page:

24
        $whoopsDisplayHandler->setPageTitle("Houston, we've got a problem!");
25
26
        // Set the "open:" link for files to our editor of choice:

27
        $whoopsDisplayHandler->setEditor("sublime");
28
    }
29
}
30
31
Route::get('/', function()
32
{
33
    // Force the execution to fail by throwing an exception:

34
    throw new RuntimeException("Oopsie!");
35
});
36
?>

Sugerencia: Whoops admite algunos editores de forma predeterminada y le permite implementar el soporte para el suyo como desee. Lea más sobre esto aquí.

Si ahora accede a su aplicación Laravel, recibirá un mensaje de error con el título de su página personalizada. Si hace clic en la ruta del archivo sobre el cuadro de código, debería abrir el archivo de referencia directamente en su editor o IDE de su elección. ¿Cuan genial es eso? Además, dado que podemos alcanzar el controlador ya configurado por el núcleo de Laravel, podemos utilizar las otras características que hemos aprendido anteriormente. Por ejemplo, podemos agregar tablas personalizadas (con PrettyPageHandler :: addDataTable ) con información útil sobre nuestra aplicación.

Vamos a ver un ejemplo más. Este será nuestro primer intento de escribir nuestro propio controlador personalizado. Queremos obtener todos los marcos de pila para una excepción y eliminar todo lo que no sea parte de nuestro código de aplicación. Suena bastante simple, ¿verdad?

1
<?php
2
#app/routes.php

3
4
/*

5
|--------------------------------------------------------------------------

6
| Application Routes

7
|--------------------------------------------------------------------------

8
*/
9
10
use Whoops\Handler\Handler;
11
12
// Use the Laravel IoC to get the Whoops\Run instance, if whoops

13
// is available (which will be the case, by default, in the dev

14
// environment)

15
if(App::bound("whoops")) {
16
    $whoops = App::make("whoops");
17
18
    $whoops->pushHandler(function($exception, $exceptionInspector, $runInstance) {
19
        // Get the collection of stack frames for the current exception:

20
        $frames = $exceptionInspector->getFrames();
21
22
        // Filter existing frames so we only keep the ones inside the app/ folder

23
        $frames->filter(function($frame) {
24
            $filePath = $frame->getFile();
25
26
            // Match any file path containing /app/...

27
            return preg_match("/\/app\/.+/i", $filePath);
28
        });
29
30
        return Handler::DONE;
31
    });
32
}
33
34
Route::get('/', function()
35
{
36
    // Force the execution to fail by throwing an exception:

37
    throw new RuntimeException("Oopsie!");
38
});
39
?>

Sugerencia: en realidad no tiene que devolver Handler :: DONE : esto solo tiene un propósito semántico. Si desea que Whoops deje de ejecutar cualquier controlador adicional después del suyo, escriba return Handler :: LAST_HANDLER . Si desea que Whoops salga de la ejecución del script después de su controlador, devuelva Handler :: QUIT .

Se puede ver que es muy conciso. El método pushHandler de Whoops \ Run acepta un cierre que recibe hasta tres argumentos: el objeto de excepción, un inspector de excepciones, que expone algunos métodos de utilidad a, lo adivinó, inspecciona excepciones y la instancia de Whoops \ Run que capturó la excepción. A través de este controlador, usamos el inspector de excepciones para extraer los marcos de la pila, todo dentro de un objeto FrameCollection limpio :

1
<?php
2
$frames = $exceptionInspector->getFrames(); // #=> Whoops\Exception\FrameCollection;

3
count($frames); #=> int

4
5
foreach($frames as $frame) {
6
    get_class($frame); // #=> Whoops\Exception\Frame

7
8
    print $frame->getFile() . ":" . $frame->getLine() . "\n";
9
        #=> "/path/to/file.php:123"

10
}
11
?>

Consejo: Whoops convierte internamente los cierres en un controlador especial: Whoops \ Handler \ CallbackHandler .

Puede contar, iterar, mapear y filtrar los contenidos de esta clase, con el aspecto interesante pero importante de que las operaciones de mapeo y filtro mutan el objeto en el lugar. Esto significa que ambas operaciones modifican la instancia original directamente, en lugar de crear una nueva colección. ¿Cómo es esto importante? Significa que los manejadores pueden realizar cambios más fácilmente que se propagan hacia abajo a todos los demás manejadores de la pila. Esto es exactamente lo que hicimos con nuestro simple controlador arriba. Si ahora vuelve a ejecutar el script, verá que obtenemos una lista más corta de marcos de pila, solo en relación con el código que reside en nuestro directorio de aplicaciones.

imageimageimage

En cuanto al objeto Frame, en sí mismo ( Whoops \ Exception \ Frame ), expone un conjunto de métodos para recopilar información sobre el contenido del marco (la ruta del archivo, el número de línea, el método o la llamada de función, el nombre de clase, etc.) y Métodos que le permiten adjuntar comentarios a marcos de pila individuales. Un comentario de marco es una característica útil en Whoops que permite a los manejadores proporcionar información adicional que recopilan de una excepción al adjuntar notas directamente a marcos individuales en la pila. Los manejadores como PrettyPageHandler , por ejemplo, pueden reunir esos comentarios y mostrarlos junto con la ruta del archivo del marco y el número de línea.

1
<?php
2
$whoops->pushHandler(function($exception, $exceptionInspector, $runInstance) {
3
    foreach($exceptionInspector->getFrames() as $i => $frame) {
4
        $frame->addComment("This is frame number {$i}");
5
    }
6
7
    return Handler::DONE;
8
});
9
?>
imageimageimage

Los comentarios de marco también pueden recibir un segundo argumento de alcance . Si tiene varios manejadores personalizados, puede, por ejemplo, filtrar los comentarios de marco por este argumento para recopilar solo la información que le interesa.

1
<?php
2
$frames = $exceptionInspector->getFrames();
3
foreach($frames as $frame) {
4
    // Was this frame within a controller class? (ends in Controller)

5
    $className = $frame->getClass();
6
    if(substr($className, -10) == "Controller") {
7
        $frame->addComment("This frame is inside a controller: $className", "controller-error");
8
    }
9
10
    // Later, in another handler, get all comments within the 'controller-errors' scope:

11
    $controllerErrors = $frame->getComments("controller-errors"); // #=> array

12
}
13
?>

También de interés, el PrettyPageHandler HTML naturalmente escapa de los comentarios del marco antes de mostrarlos, pero capturará de manera inteligente los URI en el cuerpo del comentario y los convertirá en elementos de anclaje en los que se puede hacer clic. ¿Desea vincular marcos a la documentación o a los repositorios de GitHub? Es bastante fácil; vamos a crear nuestra propia clase de controlador para este ejemplo.

Sugerencia: usar su propia clase en lugar de un cierre le brinda un control adicional sobre su manejador, sin mencionar que hace que sea más fácil cubrirlo con pruebas automatizadas. Sus clases de manejadores personalizados deben implementar la interfaz Whoops \ Handler \ HandlerInterface , pero en su lugar, simplemente puede extender la clase Whoops \ Handler \ Handler e implementar el método de manejador faltante , como se muestra en el siguiente ejemplo.

1
<?php
2
# LaravelGithubLinkHandler.php

3
4
use Whoops\Handler\Handler as BaseHandler;
5
6
/**

7
 * When possible, link laravel source files to their location

8
 * on the laravel/framework repo @ github.com

9
 */
10
class LaravelGithubLinkHandler extends BaseHandler
11
{
12
    private $repoBase = "https://github.com/laravel/framework/blob/master";
13
14
    /**

15
     * @return int

16
     */
17
    public function handle()
18
    {
19
        $frames = $this->getInspector()->getFrames();
20
21
        foreach($frames as $frame) {
22
            $file = $frame->getFile();
23
            $line = $frame->getLine();
24
25
            // Some frames may not have a file path, for example, if it ocurred within

26
            // a Closure, so we'll need to check for that:

27
            if(!$file) continue;
28
29
            // Check if the file path for this frame was within the laravel/framework

30
            // directory, inside the Composer vendor/ directory, and use a regex capture

31
            // to extract just the parts we want:

32
            if(preg_match("/\/vendor\/laravel\/framework\/(.+)$/", $file, $matches)) {
33
                $path = $matches[1]; // First match is whole path, second is our capture

34
                $url  = "{$this->repoBase}/$path";
35
36
                // We can also link directly to a line number, if we have it. Github

37
                // supports this by appending #L<line number> to the end of the URL:

38
                if($line !== null) {
39
                    $url .= "#L{$line}";
40
                }
41
42
                $frame->addComment($url, "github-linker");
43
            }
44
        }
45
46
        return Handler::DONE;
47
    }
48
}
49
?>

Eso es todo, en lo que respecta a nuestro controlador. Ponga esa clase en algún lugar de su proyecto, y todo lo que queda por hacer es habilitar y probar:

1
<?php
2
# app/routes.php

3
// ...

4
$whoops->pushHandler(new LaravelGithubLinkHandler());
5
// ...

6
?>
imageimageimage

Con solo un puñado de líneas de código, hemos agregado una capa adicional de funcionalidad (posiblemente inútil, pero bueno, es un ejemplo) a nuestras páginas de error. Aquí hay algunas ideas adicionales, si estás buscando un desafío:

  • Empaque su controlador de errores personalizado como proveedor de servicios Laravel.
  • ¿Estás utilizando Git para gestionar tu proyecto? Cree un controlador personalizado que se enganche a Git-Culpe para determinar quién fue la última persona en tocar ese archivo que sigue lanzando una excepción (y gritándoles), directamente desde la página de error.
  • Si te sientes valiente, usa PHP-Parser de nikic para analizar el código problemático, y proporciona sugerencias para arreglos (prometo que no es tan complicado como parece).

Pensamientos finales

Espero que esta breve guía le haya ayudado a comprender mejor el tipo de posibilidades que esta biblioteca habilita en sus proyectos diarios. Para más información, consulte la documentación completa de la API.

Whoops es un marco agnóstico, ligero y, creo, bastante poderoso en su simplicidad y se centra en mezclar y combinar herramientas pequeñas. También es de código abierto y está abierto a sugerencias y mejoras. Si desea contribuir o informar un error, diríjase al repositorio oficial .