Wer eine gewachsene PHP-Anwendung von 7.4 auf 8.x hebt, merkt schnell: Der Sprung ist kein reines Versionsupdate, sondern ein Verhaltenswechsel. Vieles, was jahrelang stillschweigend "irgendwie" funktioniert hat, wird jetzt zum Fehler. Das ist unangenehm, aber es ist kein Grund zur Panik. Ein System, das seit Jahren im Produktivbetrieb läuft, hat seine Fachlogik bereits bewiesen. Die Probleme beim Upgrade stecken fast nie in dieser Logik, sondern im Code drumherum: in nachlässigen Vergleichen, veralteten Signaturen und Bibliotheken, die niemand gepflegt hat.
Dieser Artikel geht die häufigsten PHP 8 Breaking Changes durch, die Ihnen in der Praxis begegnen. Ziel ist nicht Vollständigkeit um jeden Preis, sondern die Fälle, die tatsächlich Produktionsausfälle verursachen.
Warum PHP 8 Breaking Changes vor allem stille Fehler aufdeckt
Der wichtigste Punkt vorweg: PHP 8 macht viele Dinge, die früher eine Warning oder gar nichts waren, zu einem Error oder einer Exception. Ihr Code hat sich nicht verschlechtert. PHP ist nur strenger geworden und weigert sich, zweifelhafte Konstrukte weiter durchzuwinken. Das ist langfristig ein Gewinn, kurzfristig aber die Ursache der meisten Fehlermeldungen.
Die entscheidende Konsequenz: Sie brauchen einen Weg, diese Stellen zu finden, bevor Ihre Nutzer sie finden. Statische Analyse und eine gute Testabdeckung sind hier kein Luxus, sondern die eigentliche Arbeit.
Welche Breaking Changes brechen den Code sofort?
Diese Änderungen führen typischerweise zu Fatal Errors und legen Seiten direkt lahm. Sie gehören zuerst geprüft.
- Ungültige Vergleiche und der neue Sortier-Spaceship. Der berüchtigtste Fall:
0 == "foo"ist in PHP 8 false, vorher war es true. Vor PHP 8 wurde der String in eine Zahl umgewandelt (zu 0), jetzt wird die Zahl in einen String umgewandelt. Prüfen Sie jede lockere Gleichheit gegen Strings, besonders in Validierungen, Berechtigungsprüfungen und Formularlogik. Genau hier entstehen stille Sicherheitslücken. - Entfernte Funktionen und Argumente.
each(),create_function(),money_format()und das Argument-Zählen überfunc_get_args()in alten Mustern sind weg. Auchget_magic_quotes_gpc()und die letzten Reste der altenmysql_*-Funktionen (bereits in PHP 7 entfernt) tauchen in echtem Legacy-Code noch auf. - Der
@-Operator unterdrückt keine fatalen Fehler mehr zuverlässig. Code, der sich auf@verlassen hat, um Fehler zu verstecken, deckt jetzt genau diese versteckten Fehler auf.
Wie ändert sich das Verhalten bei Typen und Fehlern?
Diese Kategorie ist heimtückischer, weil der Code oft weiterläuft, aber anders reagiert.
- TypeError statt stiller Umwandlung bei internen Funktionen. Übergeben Sie
nullan einen String-Parameter einer internen Funktion wiestrlen(null), gibt es ab PHP 8.1 eine Deprecation-Meldung, und bei ungültigen Typen wirft PHP jetzt schneller einenTypeError, statt false zurückzugeben. Funktionen wiearray_key_existssind ebenfalls strikter geworden. - Warnings werden zu Errors. Der Zugriff auf ein nicht existierendes Array-Offset, der Aufruf einer Methode auf
nulloder das Schreiben auf eine Eigenschaft eines Nicht-Objekts eskaliert nun deutlich früher. Der klassische Fehler "Attempt to read property on null" ersetzt die frühere stille null-Rückgabe. - Standard-Fehlermodus bei Datenbanken. Mit dem meist mitgezogenen Update von Treibern wirft PDO in aktuellen Konfigurationen bei MySQL standardmäßig Exceptions (
PDO::ERRMODE_EXCEPTION), statt Fehler stumm zu schlucken. Datenbankfehler, die vorher unbemerkt blieben, werden jetzt sichtbar und müssen abgefangen werden.
Was ändert sich bei Klassen, Methoden und Magic Methods?
Objektorientierter Legacy-Code trifft auf mehrere strengere Regeln gleichzeitig.
- Signatur-Kompatibilität wird erzwungen. Eine überschreibende Methode, deren Signatur nicht zur Elternmethode passt, war früher eine Warning und ist jetzt ein Fatal Error. Das trifft viele alte Framework-Erweiterungen und selbstgebaute Basisklassen.
- Der Konstruktor mit Klassennamen ist endgültig weg. Eine Methode, die genauso heißt wie ihre Klasse, ist kein Konstruktor mehr. Klassen, die noch auf diesem PHP-4-Muster basieren, verlieren ihre Initialisierung stillschweigend.
- Magic Methods brauchen korrekte Signaturen.
__toStringmuss jetzt sauber einen String liefern, und Methoden wie__getoder__setwerden auf ihre erwartete Signatur geprüft. - Reflection- und Serialisierungsverhalten. Wer sich auf Reihenfolge oder Sichtbarkeit interner Strukturen verlassen hat, sollte diese Stellen gezielt testen.
Welche Rolle spielen Frameworks und Abhängigkeiten?
In der Praxis kommt der größte Teil des Schmerzes nicht aus Ihrem eigenen Code, sondern aus Bibliotheken. Ein altes Symfony 3 oder ein Laravel 5 läuft schlicht nicht auf PHP 8, und viele über Composer eingebundene Pakete haben nie ein kompatibles Update erhalten.
Kurzer Praxis-Ablauf, der sich bewährt hat:
- Lassen Sie
composer why-not php 8.2laufen, um blockierende Pakete sichtbar zu machen. - Aktualisieren Sie das Framework in seiner eigenen, dokumentierten Reihenfolge zuerst, dann die Randpakete.
- Ersetzen Sie unmaintainte Pakete gezielt, statt das ganze Projekt neu zu bauen.
Die Reihenfolge ist entscheidend. Wer versucht, PHP-Version und Framework-Version gleichzeitig in einem Rutsch zu ändern, verliert die Fähigkeit zu erkennen, welche Änderung welchen Fehler ausgelöst hat.
Wie gehen Sie das Upgrade ohne Neuentwicklung an?
Wie finde ich alle Breaking Changes in meiner Anwendung?
Setzen Sie PHPStan oder Psalm auf einem hohen Level ein und lassen Sie zusätzlich das Tool Rector mit den PHP-8-Regelsätzen über den Code laufen. Diese Kombination findet die meisten der oben genannten Stellen automatisch, bevor Sie in Produktion gehen.
Der pragmatische Weg ist selten der spektakuläre. Sie stellen die lokale Umgebung auf die Ziel-PHP-Version um, lassen die Testsuite laufen, arbeiten die statische Analyse ab und beheben die Fundstellen inkrementell. Wo Tests fehlen, schreiben Sie zuerst Charakterisierungstests für die kritischen Pfade, damit Sie beim Umbau nicht blind arbeiten.
Der Reflex, ein "altes" System lieber neu zu bauen, ist verständlich, aber meist die teuerste und riskanteste Variante. Ihre Fachlogik funktioniert bereits nachweislich seit Jahren. Ein PHP-8-Upgrade tauscht das Fundament aus, ohne dieses bewährte Wissen wegzuwerfen. Genau bei solchen Migrationen unterstützt LegacyWerk, wenn Sie eine zweite Hand oder einen strukturierten Fahrplan brauchen.