Zum Inhalt springen
Alle Artikel
PHP & Modernisierung

PHP 7 auf 8: Wo es bricht und wie Sie es vorher wissen

Die meisten Fehler beim PHP-Sprung entstehen nicht im Framework, sondern im eigenen Code und in alten Abhängigkeiten.

G Unit Solutions7. Oktober 20268 Min. Lesezeit

Der Sprung von PHP 7 auf 8 ist kein einzelnes Update, sondern mehrere. Zwischen PHP 7.4 und 8.3 liegen vier Minor-Versionen mit eigenen Änderungen an Typen, Fehlerverhalten und Erweiterungen. Die meisten Anwendungen brechen nicht beim Start. Sie brechen an Stellen, die jahrelang unbemerkt funktioniert haben.

Dieser Text zeigt, wo die typischen Bruchstellen liegen und wie Sie sie vor dem Update finden.

Typen werden strenger

PHP 8 prüft Typen genauer, auch bei internen Funktionen. Wer null an einen Parameter übergibt, der keinen null-Wert erlaubt, erhält ab PHP 8.1 eine Deprecation-Warnung. Beispiele sind strpos(), strlen(), trim() oder htmlspecialchars() mit null als Argument. In PHP 7 wurde null still zu einer leeren Zeichenkette umgewandelt.

Interne Funktionen werfen außerdem häufiger TypeError, statt null oder false zurückzugeben. Code, der auf einen Rückgabewert prüft, läuft ins Leere. Dieselbe Änderung trifft Union-Types und Rückgabetypen in eigenen Signaturen. Was PHP 7 toleriert hat, wird zur Laufzeit abgelehnt.

Ein verwandter Punkt betrifft Standardwerte. Signaturen wie function f(string $x = null) waren lange üblich. Wer direkt auf PHP 8.4 oder später geht, findet sie als veraltet markiert. Prüfen Sie solche Stellen, wenn Sie die Signaturen ohnehin anfassen.

Warnungen, die zu Fehlern werden

Ein nicht vorhandener Array-Schlüssel erzeugte in PHP 7 einen Notice-Hinweis. In PHP 8.0 ist daraus eine Warnung geworden: "Undefined array key". Der Zugriff auf eine Eigenschaft eines null-Werts erzeugt eine ähnliche Warnung.

Warnungen sind für sich genommen nicht fatal. Viele Frameworks und Fehlerbehandler wandeln sie jedoch in Exceptions um, etwa der Symfony ErrorHandler im Debug-Modus. Damit wird aus einer jahrelang ignorierten Warnung ein Abbruch. In Produktion bleibt der sichtbare Effekt oft Lograuschen, das echte Fehler überdeckt.

Prüfen Sie deshalb, wie Ihre Anwendung mit E_WARNING und E_DEPRECATED umgeht. Wenn ein Error-Handler daraus Exceptions macht, gehört das vor das Update.

Zeichenketten und Zahlen

PHP 8 hat die Vergleiche zwischen Zeichenketten und Zahlen geändert. 0 == "foo" ergab in PHP 7 true, weil die nicht-numerische Zeichenkette zu 0 konvertiert wurde. In PHP 8 wird stattdessen die Zahl in eine Zeichenkette umgewandelt, das Ergebnis ist false. 0 == "" ist ebenfalls nicht mehr wahr. Der Vergleich "0" == "foo" war schon in PHP 7 falsch und bleibt falsch, weil zwei Zeichenketten in beiden Versionen als Zeichenketten verglichen werden.

Solche Vergleiche tauchen in gewachsenem Code häufig auf: Statusprüfungen, Rabattlogiken, Gutscheincodes, Filter aus Formularen. Suchen Sie nach lockeren Vergleichen mit == in Pfaden, in denen Geld oder Berechtigungen eine Rolle spielen, und ersetzen Sie sie durch ===. Dafür braucht es kein spezielles Werkzeug. Eine gezielte Suche reicht als Anfang.

PHP 8 liefert außerdem str_contains(), str_starts_with() und str_ends_with(). Sie ersetzen viele strpos()-Vergleiche und sind leichter zu lesen. Beim Umbau der kritischen Pfade lassen sich beide Arbeiten verbinden.

Fehler und Exceptions

PHP 8 wirft für ungültige Argumente häufiger ValueError und TypeError. Ein negativer Wert in str_repeat(), ein ungültiger Modus in fopen() oder ein fehlerhafter Aufruf von DateTime führen jetzt zu einer Exception statt zu einem stillen Fehlschlag.

Wichtig ist die Vererbung: TypeError und ValueError erben von Error, nicht von Exception. Ein catch (Exception $e) fängt sie nicht ab. Prüfen Sie, ob Ihre zentralen Fehlerbehandlungen Throwable abfangen. Sonst wird aus einem Eingabefehler ein weißer Bildschirm.

Entfernte Funktionen und Erweiterungen

PHP 8.0 hat Funktionen entfernt, die in älterem Code noch vorkommen: each(), create_function(), money_format(), get_magic_quotes_gpc() und die Konstante FILTER_SANITIZE_MAGIC_QUOTES. Die XML-RPC-Erweiterung wurde aus dem Kern entfernt und liegt seither in PECL.

Auch ältere Sprünge bleiben relevant. mcrypt fehlt seit PHP 7.2, die mysql_*-Funktionen seit PHP 7.0. Wenn Ihre Anwendung von PHP 7.0 kommt und diese Aufrufe nie ersetzt wurden, bricht sie bereits vor PHP 8.

Weitere Deprecations folgen mit jeder Minor-Version: strftime() und die mhash-Familie ab 8.1, utf8_encode() und utf8_decode() ab 8.2, dynamische Eigenschaften ab 8.2. Eine Liste der eigenen Treffer liefert eine Suche im Code zusammen mit den Migrationshinweisen der Zielversion.

Abhängigkeiten setzen die Untergrenze

Bevor Sie über Code sprechen, klären Sie die Versionen. Composer-Pakete und Frameworks bestimmen, welche PHP-Version überhaupt möglich ist.

Laravel verlangt in Version 9 mindestens PHP 8.0, in Version 10 mindestens 8.1, in Version 11 mindestens 8.2. Symfony 6.4 setzt PHP 8.1 voraus. Ein Paket, das ext-mcrypt oder ext-xmlrpc benötigt, blockiert das Update unabhängig vom eigenen Code.

Nützliche Befehle sind composer check-platform-reqs gegen die Zielumgebung, composer why-not php 8.2 für blockierende Pakete und composer audit für bekannte Sicherheitslücken. Mit config.platform.php in der composer.json können Sie die Auflösung gegen die Zielversion testen, ohne sie bereits zu installieren. Prüfen Sie auch die CI-Konfiguration: Eine Pipeline, die noch PHP 7.4 installiert, testet nicht die Zielversion.

Risiken vorher finden

Statische Analyse ist der günstigste Weg, Brüche zu finden. PHPStan und Psalm prüfen Code gegen eine konfigurierte Zielversion. In der phpstan.neon setzen Sie phpVersion auf die gewünschte Version und lassen die Analyse über die kritischen Verzeichnisse laufen. Eine Baseline hält den Anfangsbefund fest, damit neue Fehler sichtbar bleiben.

Rector kann zusätzlich zeigen, welche automatischen Änderungen ein Upgrade-Set vornehmen würde. Ein Lauf mit --dry-run liefert einen Diff, ohne Dateien zu verändern. Das ist kein Ersatz für Tests, aber ein guter Überblick.

Wenn eine Testsuite existiert, lassen Sie sie vor dem Update auf der alten Version laufen. Nur so wissen Sie, ob ein Fehler nach dem Update vom Code oder von der Umgebung kommt.

Testen Sie auf einer Staging-Umgebung mit produktionsnahen Daten. Gleiche PHP-Version, gleiche Erweiterungen, gleiche Konfiguration. Ein anonymisierter Datenbankauszug deckt Fälle auf, die synthetische Testdaten nicht erzeugen.

Definieren Sie außerdem ein Fehlerbudget für die ersten Tage nach dem Update. Beispiel: keine neuen fatalen Fehler, nicht mehr Warnungen als vorher.

Rechnungsrundung, Steuersätze, Rabatte, Zahlungs-Callbacks und Erstattungen sind die Stellen, an denen ein Typfehler echten Schaden verursacht. Diese Pfade brauchen Tests, bevor die Version wechselt.

Fazit

Der Sprung auf PHP 8 bricht selten die Startseite. Er bricht Sonderfälle, die niemand mehr kennt. Wer Zielversion, Abhängigkeiten und kritische Pfade vorher prüft, findet die meisten Probleme, solange sie noch günstig zu beheben sind.

Die passenden Schritte für eine gewachsene Anwendung beschreibt die PHP-Modernisierung. Warum Aufschub die Lage verschlechtert, steht im Beitrag technische Schulden erkennen. Und wenn bereits ein Neuschreiben im Raum steht, hilft der Vergleich modernisieren oder neu schreiben.