Kodokon kodokon.com

Exceptions: Hierarchie, finally, globaler Handler

Baue eine vollständige Fehlerstrategie: verkettete fachliche Exceptions, kontrolliertes finally und globale Handler als letzte Verteidigungslinie.

9 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Seit PHP 7 implementiert alles, was geworfen wird, Throwable, mit zwei Zweigen: Error (Fehler der Engine und der Typisierung, wie TypeError) und Exception (Fehler der Anwendung). Error fängst du nur an den Grenzen des Programms. Auf Anwendungsseite liefert die SPL eine fertige Hierarchie: LogicException signalisiert einen Entwicklerfehler, der behoben werden muss; RuntimeException einen Zwischenfall zur Laufzeit, der behandelt werden muss. Der professionelle Reflex: Leite deine fachlichen Exceptions von diesen Basen ab, eine pro erkennbarer Situation.

PHP
<?php

declare(strict_types=1);

abstract class AppException extends RuntimeException
{
}

final class UserNotFound extends AppException
{
    public static function withEmail(
        string $email
    ): self {
        return new self("No user for $email");
    }
}

try {
    throw UserNotFound::withEmail('lea@example.com');
} catch (UserNotFound $e) {
    echo $e->getMessage();
}
Fachliche Hierarchie: Du fängst UserNotFound, AppException oder RuntimeException, je nach Ebene.

try/catch lohnt sich nur, wenn du weißt, was du mit dem Fehler anfangen sollst - andernfalls lass ihn nach oben durchreichen. Drei Werkzeuge verfeinern die Kontrolle: Das Multi-Catch catch (JsonException | ValueError $e) fasst identische Behandlungen zusammen; der Parameter previous verkettet die ursprüngliche Ursache, wenn du eine technische Exception in eine fachliche übersetzt; finally läuft in jedem Fall - Erfolg, Exception, sogar bei einem vorzeitigen return - ideal, um eine Ressource freizugeben.

PHP
<?php

declare(strict_types=1);

final class PayloadInvalid extends RuntimeException
{
}

function decode(string $json): mixed
{
    try {
        return json_decode(
            $json,
            true,
            flags: JSON_THROW_ON_ERROR,
        );
    } catch (JsonException $e) {
        throw new PayloadInvalid(
            'Invalid JSON payload',
            previous: $e,
        );
    } finally {
        echo "decode() finished\n";
    }
}

try {
    decode('{broken');
} catch (PayloadInvalid $e) {
    echo $e->getPrevious()?->getMessage();
}
Exception-Übersetzung: Die technische Ursache bleibt über getPrevious() zugänglich.

Am Ende der Kette schließen zwei globale Handler das Ganze ab. set_exception_handler fängt alles ab, was nicht behandelt wurde: strukturiertes Logging, eine generische 500-Antwort - ohne jemals die Nachricht oder den Trace an den Nutzer preiszugeben. set_error_handler wandelt die alten Fehler (Warnings, Notices) in ErrorException um und vereinheitlicht so die beiden Welten: Alles wird zur Exception, alles folgt demselben Behandlungsweg.

PHP
<?php

declare(strict_types=1);

set_error_handler(function (
    int $severity,
    string $message,
    string $file,
    int $line,
): bool {
    throw new ErrorException(
        $message,
        0,
        $severity,
        $file,
        $line,
    );
});

set_exception_handler(function (Throwable $e): void {
    error_log($e->getMessage());
    http_response_code(500);
    echo 'A technical error occurred.';
});

trigger_error('Legacy warning', E_USER_WARNING);
echo 'Never reached';
Die Warnung wird zur Exception, ungefangen: Der globale Handler antwortet, dann stoppt das Skript.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Was haben Error und Exception im modernen PHP gemeinsam?
    • Error erbt von Exception
    • Beide implementieren das Interface Throwable
    • Beide sind der PHP-Engine vorbehalten
  2. Wann läuft der finally-Block?
    • Nur wenn keine Exception geworfen wurde
    • Nur nach einem catch
    • In jedem Fall: Erfolg, gefangene oder nicht gefangene Exception, vorzeitiges return
  3. Wofür ist der Parameter previous einer Exception da?
    • Die ursprüngliche Exception bewahren, wenn du sie in eine Exception höherer Ebene übersetzt
    • Verhindern, dass die Exception den Aufrufstapel hochwandert
    • Die Exception festlegen, die beim nächsten Fehlschlag geworfen wird