mitmario.dev

Die drei Sorten Fehler

PHP Sandbox 3 Min Lesezeit 4 BeispieleLektion 1 von 6

PHP kennt drei Sorten von Problemen, und sie unterscheiden sich nicht darin, wie schlimm sie sind. Sie unterscheiden sich darin, was danach passiert.

Eine Warnung ist die gefährlichste

Eine Warnung rechnet falsch weiter
<?php

$noten = ["mia" => 5, "ben" => 4, "tom" => 5, "ida" => 4];

// In der Liste steht "Tom" gross, im Array klein.
$klasse = ["mia", "ben", "Tom", "ida"];

$summe = 0;
foreach ($klasse as $name) {
    $summe += $noten[$name];
}

echo "Summe: ", $summe, "\n";
echo "Durchschnitt: ", number_format($summe / count($klasse), 1, ",", "."), "\n";
echo "Das Programm ist bis hier gekommen.\n";

Eine Warning hält nichts auf. Das Programm rechnet weiter, kommt bis zum Ende und meldet sich mit Exit-Code 0 zurück, also mit demselben Code wie ein völlig sauberer Lauf. Sehen kannst du die Zahl wie in Lektion 1.4 im Terminal: php index.php; echo $? schreibt sie als letzte Zeile hin, und hier ist es eine 0, trotz der Warnung darüber.

Sieh dir an, was dabei herauskommt. In der Liste steht "Tom" groß geschrieben, im Array liegt der Wert unter "tom". PHP findet den Schlüssel nicht, meldet Undefined array key "Tom" auf dem Fehlerkanal und liefert für diese Stelle null. Und null ist beim Addieren eine Null. Die Summe ist 13 statt 18, der Durchschnitt 3,3 statt 4,5, und die letzte Zeile behauptet fröhlich, alles sei gut gegangen.

Das ist der Grund, warum eine Warnung teurer ist als ein Absturz. Ein Absturz fällt sofort auf. Eine falsche Zahl fällt auf, wenn sich jemand beschwert, und bis dahin steht sie in Rechnungen und Berichten. Behandle jede Warnung wie einen Fehler. In diesem Kurs prüft fast jede Aufgabe, dass auf dem Fehlerkanal nichts steht, und genau deshalb.

Ein Error bricht ab

Ein Error bricht mitten im Lauf ab
<?php

echo "Vor der Rechnung\n";

$teiler = 0;
echo intdiv(100, $teiler), "\n";

echo "Diese Zeile wird nie ausgegeben.\n";

Ein Error beendet das Programm sofort. Die erste Zeile wird noch ausgegeben, die letzte nicht mehr. Auf dem Fehlerkanal steht Uncaught DivisionByZeroError: Division by zero, und der Exit-Code ist 255 statt 0. Dasselbe php index.php; echo $? im Terminal endet hier mit einer 255.

Typische Fälle sind eine Division durch null, ein Aufruf einer Funktion, die es nicht gibt, oder ein Wert vom falschen Typ an einem Parameter mit Typangabe. Der Parse-Fehler aus Lektion 1.4 gehört ebenfalls in diese Familie, nur passiert er noch früher: dort startet die Datei gar nicht erst.

Gute Nachricht am Rande: PHP 8 hat viele Fälle, die früher nur eine Warnung waren, zu Fehlern gemacht. Die Division durch null ist einer davon. Vorher lief so ein Programm weiter und lieferte Unsinn, heute hört es auf.

Eine Ausnahme lässt sich fangen

Dieselbe Stelle, diesmal gefangen
<?php

echo "Vor der Rechnung\n";

$teiler = 0;

try {
    echo intdiv(100, $teiler), "\n";
} catch (DivisionByZeroError $fehler) {
    echo "Gefangen: ", $fehler->getMessage(), "\n";
}

echo "Und hier laeuft das Programm weiter.\n";

Und jetzt der Unterschied, um den es im Rest des Abschnitts geht. Dieselbe Zeile, dasselbe Problem, nur steht sie diesmal in einem try-Block. Das Ergebnis: kein Abbruch, Exit-Code 0 (auch das kannst du mit echo $? nachsehen), und der Fehlerkanal bleibt leer. Du hast die Meldung abgefangen und selbst entschieden, was passieren soll.

Eine Exception ist ein Fehler, den jemand absichtlich wirft, damit ihn jemand anderes auffangen kann. Sie bricht das Programm nur dann ab, wenn niemand sie auffängt. Streng genommen sind Error und Exception in PHP zwei Geschwister unter einem gemeinsamen Dach namens Throwable, und beide lassen sich fangen. Wie das im Einzelnen geht, steht in Lektion 7.2.

Fangen heißt aber nicht wegmachen. Wenn du eine Ausnahme abfängst, übernimmst du die Verantwortung für das, was danach kommt. Ein catch-Block, der nichts tut, ist deshalb schlimmer als gar keiner.

Ein Weg, den du kennen solltest, um ihn nicht zu gehen

Das At-Zeichen macht den Fehler unsichtbar, nicht weg
<?php

$posten = ["Kaffee" => 250];

// Ohne @ meldet PHP den fehlenden Schluessel auf dem Fehlerkanal.
$menge = $posten["Tee"];
var_dump($menge);

// Mit @ ist dieselbe Zeile still.
$still = @$posten["Tee"];
var_dump($still);

echo "In beiden Variablen steht dasselbe: nichts.\n";

Ein @ vor einem Ausdruck unterdrückt jede Meldung, die dieser Ausdruck erzeugt. Der Fehlerkanal bleibt leer, alles sieht sauber aus.

Sieh dir trotzdem die beiden var_dump() an: Beide sagen NULL. An der Sache hat sich nichts geändert, nur die Meldung ist weg. Du wirst das @ in fremdem Code sehen, meistens vor Dateizugriffen, und es ist fast immer eine Stelle, an der jemand ein Problem nicht lösen wollte.

Der ehrliche Weg ist der aus 7.2: hinsehen, abfangen, entscheiden.

Zum Mitnehmen

In Lektion 1.4 hast du Fehlermeldungen gelesen. Jetzt bekommen sie Namen, und der Unterschied zwischen ihnen ist der Stoff für den ganzen Abschnitt: Eine Sorte bricht ab, eine läuft weiter, und eine dritte lässt sich abfangen. Nur bei der letzten hast du eine Wahl.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 4 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.