1. Code
  2. PHP

Whoops! PHP-Fehler für coole Kinder

Whoops ist eine kleine Bibliothek, die als Composer-Paket verfügbar ist und Ihnen hilft, Fehler und Ausnahmen in Ihren PHP-Projekten zu behandeln.
Scroll to top

German (Deutsch) translation by Tatsiana Bochkareva (you can also view the original English article)

Whoops ist eine kleine Bibliothek, die als Composer-Paket verfügbar ist und Ihnen hilft, Fehler und Ausnahmen in Ihren PHP-Projekten zu behandeln.

Nach dem Auspacken erhalten Sie jedes Mal eine elegante, intuitive und informative Fehlerseite, wenn in Ihrer Anwendung etwas schief geht. Noch besser ist, dass dies ein sehr einfaches, aber flexibles Werkzeugset ist, mit dem Fehler auf eine Weise behandelt werden können, die für alles, was Sie tun, Sinn macht.

Die Hauptmerkmale der Bibliothek sind:

  • Detaillierte und intuitive Seite für Fehler und Ausnahmen
  • Codeansicht für alle Frames
  • Konzentrieren Sie sich auf die Fehler-/Ausnahmeanalyse mithilfe benutzerdefinierter, einfacher Middleware/Handler
  • Unterstützung für JSON- und AJAX-Anfragen
  • Eingeschlossene Anbieter für Silex- und Zend-Projekte über die gebündelten Anbieter und Teil des Laravel 4-Kerns
  • Saubere, kompakte und getestete Codebasis ohne zusätzliche Abhängigkeiten
imageimageimage

Whoops erreicht dies durch ein System gestapelter Handler. Sie teilen Whoops mit, welche Handler Sie verwenden möchten (Sie können aus den enthaltenen Handlern auswählen oder Ihre eigenen erstellen), und wenn etwas passiert, erhalten alle Handler in der Reihenfolge eine Chance, etwas zu tun - dies kann alles sein, von der Analyse der Fehler (Whoops erleichtert das Extrahieren aussagekräftiger Informationen aus einem Fehler oder einer Ausnahme) und das Anzeigen hilfreicher Fehlerbildschirme (wie der integrierte PrettyPageHandler, mit dem Sie die oben abgebildete cool aussehende Seite erhalten).

Probieren wir es zuerst aus, indem wir uns die Grundlagen ansehen und dann versuchen, unseren eigenen Handler mit Whoops und dem Laravel-Framework zu erstellen. In dieser kurzen Anleitung gehe ich davon aus, dass Sie mit PHP mäßig vertraut sind und von Composer gehört haben. Wenn dies nicht der Fall ist, lesen Sie es hier bei Nettuts+ nach.


Whoops installieren

Erstellen Sie ein Verzeichnis für Ihr Projekt, ändern Sie es, erstellen Sie eine composer.json-Datei mit der Whoops-Anforderung und installieren Sie sie. Whoops (ab Version 1.0.5) hat keine Abhängigkeiten, daher dauert dies nur eine Sekunde.

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

Verwenden von Whoops: Die Grundlagen

Um diese elegante Fehlerseite in Aktion zu sehen, richten wir Whoops ein und stellen sicher, dass etwas kaputt geht, indem wir eine Ausnahme in unserem Code auslösen. Erstellen Sie eine Datei im Verzeichnis Ihres Projekts. Nehmen wir für dieses Handbuch an, es heißt index.php.

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

Da wir Whoops mit Composer installiert haben und es PSR-0-kompatibel ist, müssen wir nur den Composer-Autoloader benötigen und können die Bibliothek in unserem eigenen Code verwenden!

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
?>

Wenn bereits ein Webserver ausgeführt wird, greifen Sie auf die gerade erstellte Datei zu. Vergessen Sie nicht: Wenn Sie PHP 5.4 verwenden, können Sie den integrierten Entwicklungsserver wie folgt nutzen:

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

Folgendes erhalten Sie:

imageimageimage

Ziemlich ordentlich, oder? Handler selbst können Optionen zur Änderung oder Erweiterung ihres Verhaltens verfügbar machen. Sie können beispielsweise unter anderem den Titel der Standardfehlerseite festlegen und sogar zusätzliche Informationen einfügen:

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

Da dies lediglich ein regulärer Whoops-Handler ist, können wir auch mit anderen Handlern kombinieren, um dynamischere Ergebnisse zu erzielen. Stellen Sie sich vor, Sie arbeiten an einer AJAX + JSON-gesteuerten Website. Im Moment, wenn Ihre Anwendung irgendwie fehlschlagen würde, würden Sie eine Menge bösen HTML-Codes erhalten, wenn Sie JSON erwarten würden. Keine große Sache:

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!");

Das ist es. Wenn nun während einer AJAX-Anforderung etwas fehlschlägt, antwortet Whoops mit einer JSON-Antwort, in der der Fehler aufgeführt ist. Wenn es sich NICHT um eine AJAX-Anforderung handelt, wird weiterhin die reguläre Fehlerseite angezeigt. Wenn ein Fehler auftritt, filtert Whoops jeden der registrierten Handler (beginnend mit dem zuletzt zu registrierenden Handler) und gibt ihnen die Möglichkeit, die Anfrage zu analysieren, zu ändern und zu beantworten.

Nachdem Sie eine allgemeine Vorstellung davon haben, wie Whoops funktioniert, können Sie einen eigenen Handler mit Whoops und dem Laravel 4-Framework erstellen.


Whoops und Laravel 4

Laravel 4-Bundles Whoops als zentraler Ausnahmebehandler, standardmäßig im Entwicklungsmodus aktiviert, einschließlich eines benutzerdefinierten Farbschemas von Dayle Rees:

imageimageimage

Wenn Sie Laravel noch nicht installiert haben, gehen Sie zu den Installationsschritten. Laravel wird ausführlich auf Nettuts+ und Tuts+ Premium behandelt, sodass Sie hier reichlich Training finden, wenn Sie sich weiter vertiefen möchten.

Für die nächsten Schritte gehe ich davon aus, dass Sie mit den Grundlagen von Laravel 4 einigermaßen vertraut sind. Auch wenn dies nicht der Fall ist, sollte es dennoch leicht zu befolgen sein.

Wenn Sie sich im Entwicklungsmodus (Debug) befinden, ist Whoops über den IoC-Container als whoops verfügbar und mit einem von zwei Handlern voreingestellt: PrettyPageHandler oder JsonResponseHandler als whoops.handler (dieselben zwei, über die wir gerade gesprochen haben). Beide Handler stellen nützliche zusätzliche Methoden bereit, wie Sie oben mit dem PrettyPageHandler gesehen haben. Durch den Zugriff auf diese Dienste können wir beginnen, unsere Whoops-Erfahrung innerhalb des Frameworks anzupassen.

Lassen Sie uns der Einfachheit halber in Ihrer Datei app/route.php in den Whoops-Dienst einbinden und einen benutzerdefinierten Seitentitel für unsere Fehlerseiten festlegen:

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
?>

Tipp: Whoops unterstützt standardmäßig einige Editoren und ermöglicht es Ihnen, die Unterstützung nach Ihren Wünschen zu implementieren. Lesen Sie hier mehr darüber.

Wenn Sie jetzt auf Ihre Laravel-Anwendung zugreifen, wird eine Fehlermeldung mit Ihrem benutzerdefinierten Seitentitel angezeigt. Wenn Sie auf den Dateipfad über dem Codefeld klicken, sollte die referenzierte Datei direkt in Ihrem Editor oder einer IDE Ihrer Wahl geöffnet werden. Wie cool ist das? Da wir den Handler erreichen können, der bereits vom Laravel-Kern eingerichtet wurde, können wir auch die anderen Funktionen verwenden, die wir oben kennengelernt haben. Beispielsweise können wir benutzerdefinierte Tabellen (mit PrettyPageHandler::addDataTable) mit nützlichen Informationen zu unserer Anwendung hinzufügen.

Schauen wir uns noch ein Beispiel an. Dies ist unser erster Versuch, einen eigenen benutzerdefinierten Handler zu schreiben. Wir möchten alle Stapelrahmen für eine Ausnahme abrufen und alles entfernen, was nicht Teil unseres Anwendungscodes ist. Klingt einfach genug, oder?

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
?>

Tipp: Sie müssen Handler::DONE nicht zurückgeben - dies dient nur einem semantischen Zweck. Wenn Sie möchten, dass Whoops keine zusätzlichen Handler mehr nach Ihnen ausführt, schreiben Sie return Handler::LAST_HANDLER. Wenn Whoops die Skriptausführung nach Ihrem Handler beenden soll, geben Sie Handler::QUIT zurück.

Sie können sehen, dass es bemerkenswert prägnant ist. Die pushHandler-Methode von Whoops \ Run akzeptiert einen Abschluss, der bis zu drei Argumente empfängt: das Ausnahmeobjekt, einen Ausnahmeinspektor, der einige Dienstprogrammmethoden, wie Sie es erraten haben, Ausnahmen überprüft, und die Whoops \ Run-Instanz, die die Ausnahme erfasst hat. Über diesen Handler verwenden wir den Ausnahmeinspektor, um die Stapelrahmen innerhalb eines übersichtlichen FrameCollection-Objekts zu extrahieren:

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
?>

Tipp: Whoops konvertiert Verschlüsse intern in einen speziellen Handler: Whoops\Handler\CallbackHandler.

Sie können den Inhalt dieser Klasse zählen, iterieren, zuordnen und filtern, wobei der interessante, aber wichtige Aspekt darin besteht, dass die Zuordnungs- und Filteroperationen das Objekt an Ort und Stelle mutieren. Dies bedeutet, dass beide Vorgänge die ursprüngliche Instanz direkt ändern, anstatt eine neue Sammlung zu erstellen. Wie ist das wichtig? Dies bedeutet, dass Handler leichter Änderungen vornehmen können, die sich nach unten auf alle anderen Handler im Stapel ausbreiten. Genau das haben wir oben mit unserem einfachen Handler gemacht. Wenn Sie das Skript jetzt erneut ausführen, wird eine kürzere Liste von Stapelrahmen angezeigt, die nur den in unserem Anwendungsverzeichnis enthaltenen Code betrifft.

imageimageimage

Das Frame-Objekt selbst (Whoops\Exception\Frame) stellt eine Reihe von Methoden zur Verfügung, mit denen Informationen zum Frame-Inhalt (Dateipfad, Zeilennummer, Methoden- oder Funktionsaufruf, Klassenname usw.) und gesammelt werden können Methoden, mit denen Sie Kommentare an einzelne Stapelrahmen anhängen können. Ein Frame-Kommentar ist eine nützliche Funktion in Whoops, mit der Handler zusätzliche Informationen bereitstellen können, die sie aus einer Ausnahme sammeln, indem sie Notizen direkt an einzelne Frames im Stapel anhängen. Handler wie beispielsweise PrettyPageHandler können diese Kommentare dann sammeln und zusammen mit dem Dateipfad und der Zeilennummer des Frames anzeigen.

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

Rahmenkommentare erhalten möglicherweise auch ein zweites scope argument. Wenn Sie mehrere benutzerdefinierte Handler haben, können Sie beispielsweise Frame-Kommentare nach diesem Argument filtern, um nur die Informationen zu erfassen, die Sie interessieren.

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
?>

Von Interesse ist auch, dass der PrettyPageHandler die Frame-Kommentare natürlich vor der Anzeige HTML-maskiert, aber URIs im Hauptteil des Kommentars intelligent erfasst und in anklickbare Ankerelemente konvertiert. Möchten Sie Frames mit der Dokumentation oder mit GitHub-Repositorys verknüpfen? Es ist einfach genug; Lassen Sie uns für dieses Beispiel unsere eigene Handlerklasse erstellen.

Tipp: Wenn Sie Ihre eigene Klasse anstelle eines Verschlusses verwenden, haben Sie zusätzliche Kontrolle über Ihren Handler - ganz zu schweigen davon, dass es einfacher ist, automatisierte Tests durchzuführen. Ihre benutzerdefinierten Handlerklassen müssen die Schnittstelle Whoops\Handler\HandlerInterface implementieren. Sie können jedoch stattdessen einfach die Klasse Whoops\Handler\Handler erweitern und die Methode für fehlende handle implementieren, wie im folgenden Beispiel gezeigt.

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
?>

Das war's, soweit unser Handler geht. Platzieren Sie diese Klasse irgendwo in Ihrem Projekt, und Sie müssen sie nur noch aktivieren und ausprobieren:

1
<?php
2
# app/routes.php

3
// ...

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

6
?>
imageimageimage

Mit nur einer Handvoll Codezeilen haben wir unseren Fehlerseiten eine zusätzliche Ebene von (teilweise nutzlosen, aber hey, es ist ein Beispiel) Funktionen hinzugefügt. Hier sind einige zusätzliche Ideen, wenn Sie nach einer Herausforderung suchen:

  • Packen Sie Ihren benutzerdefinierten Fehlerbehandler als Laravel-Dienstanbieter ein.
  • Verwenden Sie Git, um Ihr Projekt zu verwalten? Erstellen Sie einen benutzerdefinierten Handler, der sich in git-blame einhakt, um zu bestimmen, wer die letzte Person war, die diese Datei berührt, die immer wieder eine Ausnahme auslöst (und sie anschreit), direkt von der Fehlerseite.
  • Wenn Sie sich mutig fühlen, verwenden Sie den PHP-Parser von nikic, um den problematischen Code zu analysieren und Vorschläge für Korrekturen zu machen (ich verspreche, es ist nicht so kompliziert, wie es sich anhört).

Abschließende Gedanken

Ich hoffe, dass dieser kurze Leitfaden Ihnen geholfen hat, die Möglichkeiten zu verstehen, die diese Bibliothek in Ihren täglichen Projekten bietet. Weitere Informationen finden Sie in der vollständigen API-Dokumentation.

Whoops ist Framework-unabhängig, leicht und meiner Meinung nach sehr leistungsfähig in seiner Einfachheit und konzentriert sich auf das Mischen und Anpassen kleiner Werkzeuge. Es ist auch Open Source und offen für Vorschläge und Verbesserungen. Wenn Sie einen Beitrag leisten oder einen Fehler melden möchten, besuchen Sie das offizielle Repository!