# Fehler und Exceptions in PHP

> Fehler und Exceptions in PHP: Throwable, Error und Exception, try/catch/finally, globale Handler und sichere Einstellungen für Fehlerausgabe und Logs.

Quelle: https://phpsos.de/php/fehler-und-exceptions/  
Stand: 25.10.2026  
Anbieter: PHP SOS, PHP-Notfallhilfe und Server-Sicherheit. Soforthilfe per WhatsApp: https://wa.me/491736494914?text=Hallo%20PHP%20SOS%2C%20wir%20brauchen%20Hilfe%20mit%20unserem%20PHP-Server.%20Firma%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kurz gesagt:** PHP meldet Probleme auf zwei Wegen: als klassische Fehler wie Warnungen und Hinweise, die über `error_reporting` gesteuert werden, und als Exceptions, die sich mit `try` und `catch` abfangen lassen. Seit PHP 7 werfen auch viele interne Fehler ein Objekt der Klasse `Error`; gemeinsam mit `Exception` implementiert es das Interface `Throwable`. In der Produktion gilt: Fehler protokollieren, nie im Browser anzeigen. Auf PHP 8.5 gehört wegen PHP2Shell außerdem `fatal_error_backtraces=0` zu den Übergangsmaßnahmen.

## Zwei Welten: klassische Fehler und Exceptions

PHP stammt aus einer Zeit, in der Fehler vor allem als Meldung ausgegeben wurden: Hinweise (`E_NOTICE`), Warnungen (`E_WARNING`), veraltete Funktionen (`E_DEPRECATED`) und fatale Fehler (`E_ERROR`). Viele dieser Meldungen lassen das Skript weiterlaufen, oft mit falschen oder leeren Daten.

Exceptions arbeiten anders. Sie unterbrechen den normalen Ablauf und springen zum nächsten passenden `catch`-Block. Wird keiner gefunden, endet das Skript. Für Entscheider ist der Unterschied wichtig: Eine stille Warnung kann dazu führen, dass eine Bestellung unvollständig gespeichert wird, ohne dass es jemand merkt. Eine Exception macht das Problem sichtbar und behandelbar.

Ein unterschätzter Nutzen der klassischen Meldungen sind Hinweise vom Typ `E_DEPRECATED`. Sie kündigen an, dass ein Sprachmittel in einer künftigen PHP-Version wegfällt. Wer sie im Log sammelt und regelmäßig auswertet, weiß vor einem Upgrade, welche Stellen angepasst werden müssen, und muss bei einer dringenden Sicherheitslücke nicht erst lange testen.

## Die Throwable-Hierarchie

Alles, was sich mit `throw` werfen und mit `catch` fangen lässt, implementiert `Throwable`. Darunter gibt es zwei Äste:

| Klasse | Zweck | Beispiele für Unterklassen |
|---|---|---|
| `Exception` | erwartbare Fehler der Anwendung und von Erweiterungen | `ErrorException`, `PDOException`, eigene Klassen |
| `Error` | interne Fehler von PHP | `TypeError`, `ValueError`, `ArgumentCountError`, `ArithmeticError`, `DivisionByZeroError`, `UnhandledMatchError` |

Eigene Fehlerklassen leiten von `Exception` ab, nicht von `Error`. Seit PHP 8.0 werfen die meisten internen Funktionen bei ungültigen Parametern einen `TypeError` oder `ValueError`, statt nur eine Warnung auszugeben. `strlen([])` ist ein solcher Fall.

## try, catch und finally in der Praxis

```php
<?php
declare(strict_types=1);

final class ZahlungFehlgeschlagen extends RuntimeException {}

function bezahlen(Bestellung $b, Zahlungsdienst $dienst, LoggerInterface $log): bool
{
    try {
        $dienst->belasten($b->betrag(), $b->referenz());
        return true;
    } catch (ZahlungFehlgeschlagen $e) {
        $log->warning('Zahlung abgelehnt', ['bestellung' => $b->id(), 'exception' => $e]);
        return false;
    } catch (TimeoutException | VerbindungsFehler) {
        // seit PHP 8.0 ist die Variable im catch optional
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}
```

Mehrere Klassen lassen sich mit `|` in einem `catch` zusammenfassen. Der `finally`-Block läuft immer, ob ein Fehler aufgetreten ist oder nicht. Seit PHP 8.0 ist `throw` außerdem ein Ausdruck und kann etwa hinter `??` stehen: `$id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');`.

## Globale Handler: set_error_handler und set_exception_handler

Zwei Funktionen fangen ab, was sonst durchrutschen würde. `set_exception_handler()` registriert eine Funktion für nicht abgefangene Throwables. Laut Handbuch endet das Skript, nachdem sie aufgerufen wurde; sie eignet sich also für Protokollierung und eine neutrale Fehlerseite, nicht zum Weiterarbeiten.

Bewährt hat sich ein Handler, der den vollständigen Fehler mit Zeitpunkt und einer zufälligen Kennung ins Log schreibt und dem Besucher nur eine neutrale Seite mit dieser Kennung zeigt. Der Support kann den Fall dann im Log wiederfinden, ohne dass interne Details nach außen dringen.

`set_error_handler()` fängt klassische Fehler ab. Das Handbuch zeigt, wie man sie in eine `ErrorException` umwandelt, damit Warnungen wie Exceptions behandelt werden:

```php
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // von error_reporting ausgeschlossen
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // Hinweise auf Veraltetes nicht eskalieren
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
```

Fatale Fehler wie ein Speicherüberlauf lassen sich auf diesem Weg nicht abfangen. Was bei einem Überlauf im Inneren passiert, erklärt die Seite [memory_limit und Out-of-Memory](https://phpsos.de/php/out-of-memory/).

## Fehlerausgabe und Protokollierung konfigurieren

Die wichtigsten Einstellungen mit ihren Standardwerten laut Handbuch:

| Einstellung | Standard | Empfehlung Produktion |
|---|---|---|
| `error_reporting` | `E_ALL` seit PHP 8.0.0 | `E_ALL` |
| `display_errors` | `1` | `Off` |
| `display_startup_errors` | `1` seit PHP 8.0.0 | `Off` |
| `log_errors` | `0` | `On` |
| `error_log` | leer (Log des Webservers bzw. der SAPI) | eigene Datei außerhalb des Webroots |
| `fatal_error_backtraces` | `1`, neu in PHP 8.5.0 | vorübergehend `0` (PHP2Shell) |

Seit PHP 8.2.0 legt `error_log_mode` die Dateirechte der Logdatei fest, standardmäßig `0644`. Enthalten die Logs personenbezogene Daten oder Pfade, ist ein strengerer Wert sinnvoll, damit andere Benutzer auf dem Server sie nicht lesen können. Mit `ignore_repeated_errors` lassen sich identische Meldungen aus derselben Datei und Zeile zusammenfassen, damit eine Fehlerschleife das Log nicht überflutet.

Die mitgelieferte `php.ini-production` setzt `display_errors` bereits auf `Off`. Laut Handbuch wirkt `ini_set('display_errors', ...)` zur Laufzeit nicht auf fatale Fehler, und Syntaxfehler treten auf, bevor ein `ini_set()` überhaupt läuft. Diese Werte gehören deshalb in die Serverkonfiguration. Hintergründe stehen im Beitrag [Fehlerausgaben in der Produktion abschalten](https://phpsos.de/blog/display-errors-produktion/); wie Logs ohne Datenleck geführt werden, im Beitrag [Sicher loggen](https://phpsos.de/blog/sicher-loggen-php/).

## Fehlerbehandlung und PHP2Shell

Für PHP 8.5 nennt php.net als eine der Übergangsmaßnahmen gegen PHP2Shell, `fatal_error_backtraces=0` zu setzen und PHP danach neu zu starten. Die übrigen Maßnahmen betreffen `memory_limit`, `post_max_size` und `session.upload_progress.enabled`. Keine davon ersetzt das Update auf PHP 8.5.12, 8.4.27, 8.3.36 oder 8.2.35.

```ini
; nur PHP 8.5, in php.ini oder FPM-Pool, danach alle Worker neu starten
fatal_error_backtraces = 0
```

Fehlerlogs sind nach PHP2Shell außerdem ein wichtiges Beweismittel. Häufen sich ab dem 20.10.2026 Speicherfehler oder abgebrochene Worker, kann das auf Angriffsversuche hindeuten, es kann aber auch harmlose Ursachen haben. Bewahren Sie die Logs auf und werten Sie sie gemeinsam mit den Zugriffslogs aus, wie im Ratgeber [Webserver-Logs nach einem Angriff auswerten](https://phpsos.de/ratgeber/logfiles-auswerten/) beschrieben.

## Wie hilft PHP SOS konkret?

- Wir prüfen die wirksamen Einstellungen für Fehlerausgabe und Protokollierung je Website und stellen sie produktionstauglich ein.
- Wir setzen die PHP2Shell-Übergangsmaßnahmen passend zur PHP-Version und starten alle Worker neu.
- Wir sichern Fehler- und Zugriffslogs ab dem 20.10.2026 und werten sie auf Auffälligkeiten aus.
- Im Rahmen des [Security-Monitorings](https://phpsos.de/leistungen/security-monitoring/) melden wir ungewöhnliche Fehlerhäufungen, statt sie im Log versanden zu lassen.


## Häufige Fragen

**Was ist der Unterschied zwischen Error und Exception?**  
Beide implementieren das Interface Throwable. Exception ist für Fehler gedacht, die eine Anwendung erwartet und behandelt, etwa eine fehlgeschlagene Datenbankverbindung. Error steht für interne Fehler von PHP wie einen TypeError oder eine Division durch null.

**Sollte display_errors in der Produktion an sein?**  
Nein. Fehlermeldungen im Browser verraten Pfade, Abfragen und manchmal Zugangsdaten. In der Produktion gehört display_errors auf Off und log_errors auf On, damit Fehler im Log landen statt beim Besucher.

**Kann man einen Speicherüberlauf mit try/catch abfangen?**  
Nein. Überschreitet ein Skript memory_limit, beendet PHP es mit einem fatalen Fehler, der sich nicht als Exception abfangen lässt. Er erscheint nur im Fehlerlog, sofern log_errors aktiv ist.

**Was ist fatal_error_backtraces?**  
Eine Einstellung, die es seit PHP 8.5.0 gibt und die fatalen Fehlern einen Backtrace beifügt. Sie ist standardmäßig aktiv. Als Übergangsmaßnahme gegen PHP2Shell nennt php.net für PHP 8.5, sie auf 0 zu setzen und PHP danach neu zu starten.

## Quellen

- [PHP-Handbuch: Konfiguration der Fehlerbehandlung](https://www.php.net/manual/de/errorfunc.configuration.php)
- [PHP-Handbuch: Klasse Error](https://www.php.net/manual/de/class.error.php)
- [PHP-Handbuch: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [PHP-Handbuch: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: Sicherheitsmeldungen](https://www.php.net/)
