DevCore.net

PHP

set_error_handler(): Warnungen zentral abfangen

set_error_handler() ist praktisch, wenn du PHP-Fehler nicht einfach irgendwo verstreut behandeln willst. Stattdessen reagierst du an einer zentralen Stelle auf Warnungen, Notices oder eigene Fehler.

set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
    error_log("[$severity] $message in $file:$line");

    return true;
});

Warum das nützlich ist: Du bekommst einheitliches Logging und kannst viele alte Warnungen in saubere Exceptions umwandeln.

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

Besonders hilfreich bei:

  • Legacy-Code
  • Bibliotheken mit schwacher Fehlerbehandlung
  • Projekten, in denen du Fehler lieber gesammelt als stillschweigend behandeln willst

Wichtig: Nicht jeder Fehler sollte blind zu einer Exception werden. In produktiven Systemen lohnt sich ein klarer Plan, welche Fehler du hart behandelst und welche nur geloggt werden.

register_shutdown_function(): Die letzte Chance im Request

register_shutdown_function() läuft ganz am Ende des Skripts — auch dann, wenn vorher etwas richtig schiefgelaufen ist. Genau deshalb ist die Funktion so wertvoll für Logging und Notfall-Handling.

register_shutdown_function(function (): void {
    $error = error_get_last();

    if ($error === null) {
        return;
    }

    error_log($error['message'] . ' in ' . $error['file'] . ':' . $error['line']);
});

Warum das nützlich ist: Du kannst fatale Fehler noch erkennen, bevor der Request komplett verschwindet, und bekommst wenigstens ein sauberes Log.

Typische Einsatzfälle:

  • Fehlerprotokollierung
  • Cleanup von temporären Dateien
  • letzte Benachrichtigung bei kritischen Abbrüchen

Wichtig: Die Shutdown-Funktion ist keine Wunderwaffe. Wenn schon ein fataler Fehler passiert ist, ist dein Spielraum begrenzt — aber für Diagnose und Aufräumen oft genau richtig.

set_exception_handler(): Wenn nichts mehr abgefangen wird

Wenn irgendwo eine Exception durchrutscht, ist set_exception_handler() dein letzter sauberer Anlaufpunkt. Genau dafür ist die Funktion stark: Du definierst einmal zentral, was bei einem unbehandelten Fehler passieren soll.

set_exception_handler(function (Throwable $e): void {
    error_log($e->__toString());
    http_response_code(500);
    echo 'Es ist ein Fehler aufgetreten.';
});

Warum das nützlich ist: Du brauchst nicht überall denselben Fallback-Code und bekommst trotzdem einheitliches Logging sowie eine kontrollierte Antwort an den Browser.

Besonders praktisch bei:

  • APIs
  • kleinen Projekten ohne großes Framework
  • Bootstrap-Dateien, die den ganzen Request initialisieren

Wichtig: Der Handler selbst sollte sehr robust sein. Wenn dort wieder ein Fehler passiert, wird die eigentliche Ursache schnell schwerer nachvollziehbar.

Server-Timing: Performance-Daten direkt in den DevTools

Mit dem Server-Timing HTTP-Header verschickst du Performance-Metriken direkt an den Browser. Diese tauchen sofort im DevTools unter Network → Timings auf.

Format:

Server-Timing: db;dur=123;desc="Query"

In PHP:

$start = microtime(true);
// ... Datenbankabfrage oder andere Operation ...
$duration = (microtime(true) - $start) * 1000; // Dauer
header("Server-Timing: db;dur=$duration;desc=\"Database\"", true);

Live Demo: https://server-timing-hono.deno.dev/ – öffne DevTools, schau in Network → Timing

Große Datenmengen in PHP: Speicher sparen mit Generatoren

Statt erst ein riesiges Array aufzubauen, kannst du Werte mit yield einzeln erzeugen und direkt weiterverarbeiten.

function userIds(PDO $db): Generator {
    $stmt = $db->query('SELECT id FROM users');

    while (($id = $stmt->fetchColumn()) !== false) {
        yield (int) $id;
    }
}

foreach (userIds($db) as $id) {
    processUser($id);
}

Ohne Generatoren landet oft alles zuerst in einem Array, etwa per fetchAll() oder durch manuelles Sammeln. Das kostet bei vielen Datensätzen schnell unnötig viel RAM.

Vorteile: Weniger Speicherverbrauch, schnellerer Start der Verarbeitung, sauberere Trennung zwischen Datenquelle und Verarbeitung.

Wichtig: Generatoren sind ideal für sequentielle Verarbeitung. Wenn du alle Werte mehrfach brauchst oder zufällig darauf zugreifen willst, ist ein echtes Array oft immer noch die richtige Wahl.

Guard Clauses: Weniger Verschachtelung, klarere Logik

Die Idee: Prüfe Ausnahmefälle zuerst und verlasse die Funktion sofort. So bleibt alles sauber.

// ❌ Tief verschachtelt
function createInvoice(?User $user, array $items) {
    if ($user) {
        if ($user->isActive()) {
            if ($items) {
                return Invoice::fromItems($user, $items);
            }
        }
    }

    return null;
}

// ✅ Guard Clauses
function createInvoice(?User $user, array $items) {
    if (!$user) {
        return null;
    }

    if (!$user->isActive()) {
        return null;
    }

    if (!$items) {
        return null;
    }

    return Invoice::fromItems($user, $items);
}

Vorteil: Weniger Einrückung, schneller erfassbar, leichter zu testen.

User-Agent-Debugging mit geheimem Key

Wenn du im Frontend schnell debuggen willst, nutze einen geheimen Hash im User-Agent.

Nur wenn der Hash stimmt, gibt das Backend Debug-Infos aus (z. B. Timing, Cache-Hit/Miss, interne Steps).

$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$token = $_ENV['UA_DEBUG_TOKEN'] ?? '';
$debug = $token !== '' && str_contains($ua, 'DevCoreDebug/' . $token);

if ($debug) {
    header('X-Debug-Info: ' . json_encode(['dbMs' => 42, 'cache' => 'MISS']));
}

Warum gut: Du erzeugst keinen Debug-Noise für normale User, siehst direkt im echten Frontend, was passiert, und kannst alles ohne extra UI-Flag aktivieren.

Wichtig: Das ist kein Security-Feature, sondern nur ein Debug-Schalter; gib keine sensiblen Daten aus und wechsle den Token regelmäßig.

If-Bedingungen optimieren: Richtige Reihenfolge spart Zeit

Die Idee: Wenn du mehrere Bedingungen mit || kombiniertst, stoppt PHP sobald eine wahr ist (Short-Circuit-Evaluation). Ordne deine Checks strategisch:

// ❌ Schlecht: Teure DB-Abfrage zuerst
if ($this->expensiveDbQuery() || $variable === '123') { }

// ✅ Gut: Schnelle Variablenprüfung zuerst
if ($variable === '123' || $this->expensiveDbQuery()) { }

Erspart dir eine ganze DB-Abfrage, wenn die erste Bedingung passt. Mit && funktioniert es genauso – auch hier sollten billige Checks zuerst kommen.

Faustregel: Billige Operationen zuerst, teure Operationen zuletzt – das gilt für beide || und &&. So einfach ist Leistungsoptimierung!

Formularverarbeitung mit Array-Namen vereinfachen

Komplizierte Formularverarbeitung? Nutze HTML-Arrays!

<!-- HTML -->
<input name="user[name]">
<input name="user[email]">
<input name="product[sku][]">
<input name="product[sku][]">
// PHP - automatisch strukturiert!
$_POST['user']['name']        // "John"
$_POST['user']['email']       // "[email protected]"
$_POST['product']['sku'][0]   // "SKU123"
$_POST['product']['sku'][1]   // "SKU456"

Vorteil: Automatische Strukturierung, kein manuelles Parsing, weniger Code.

Gold-Regel: Immer $_POST validieren bevor du es nutzt!