Legacy PHP

PHP 8.2 bekommt ab Januar keine Sicherheitsupdates mehr: was bis dahin noch sinnvoll ist

Ende Dezember endet der offizielle Security-Support für PHP 8.2. Die Anwendung läuft danach weiter, aber neue Lücken werden nicht mehr offiziell geschlossen. Was der Sprung auf eine neuere Version in gewachsenen Anwendungen kostet, mit und ohne Framework, wo er hängen bleibt und was man tut, wenn es bis Silvester nicht reicht.

29. Oktober 2026 10 min · 1.967 Wörter

Am 31. Dezember 2026 endet der offizielle Security-Support für PHP 8.2. Abgeschaltet wird dabei nichts. Die Anwendung läuft am 1. Januar genauso wie am Tag davor. Nur werden neue Sicherheitslücken in 8.2 ab dann vom PHP-Projekt nicht mehr behoben.

Überraschend ist der Termin eigentlich nicht, denn er steht seit dem Release Ende 2022 fest. Jede PHP-Version wird vier Jahre gepflegt: zwei Jahre aktiver Support mit Bugfixes, dann zwei Jahre nur noch Sicherheitsupdates. Bei 8.2 ist die zweite Hälfte jetzt vorbei. Trotzdem fallen solche Termine in der Praxis meistens erst auf, wenn sie da sind. Solange die Anwendung läuft, gibt es kein Ticket dafür, und niemand fragt nach.

Die Termine laut offizieller PHP-Supporttabelle:

Version Sicherheitsupdates bis
8.1 31.12.2025 (abgelaufen)
8.2 31.12.2026
8.3 31.12.2027
8.4 31.12.2028
8.5 31.12.2029

Warum das nicht abstrakt ist

Wie schnell so ein Datum relevant wird, hat der Oktober gezeigt. Am 21. Oktober 2026 hat das PHP-Projekt die Versionen 8.5.12, 8.4.27, 8.3.36 und 8.2.35 veröffentlicht. Sie schließen zwei kritische Schwachstellen, CVE-2026-109618 und CVE-2026-109619, über die ein Angreifer ohne Anmeldung Code auf dem Server ausführen kann. Laut den Sicherheitsmeldungen sind PHP-Installationen betroffen, die Webanfragen bedienen, ob über PHP-FPM, mod_php oder andere SAPIs. Besonders gefährdet sind die, die aus dem Internet erreichbar sind und ungeprüfte Eingaben verarbeiten. Besonders relevant ist das für Installationen mit Apache und mod_php, weil die Verarbeitung dort bereits vor dem eigentlichen Anwendungscode stattfindet. Eine gute Zusammenfassung mit allen betroffenen Versionen hat das kanadische Cyber Centre veröffentlicht.

8.2 hat dafür noch ein reguläres Release bekommen. Wären die Lücken im Januar aufgetaucht, stünde 8.2 heute da, wo 8.1 schon jetzt steht: ohne offizielles Update, angewiesen auf das, was andere nachliefern.

Backports überbrücken, mehr nicht

Distributionen, Hoster oder LTS-Anbieter können Sicherheitsfixes in ältere Versionen zurückportieren. Das ist aber kein reguläres PHP-Release, und es ist nicht garantiert. Ob und wie schnell beim nächsten Mal wieder ein Backport kommt, verspricht einem niemand.

Dazu kommt ein Haken, der in der Hektik leicht untergeht: Bei einem Backport bleibt die PHP-Versionsnummer meistens gleich. php -v zeigt vor und nach dem Update dasselbe an. Ob der Fix drin ist, sieht man an der Paket-Revision. Auf Debian- und Ubuntu-Systemen zum Beispiel so:

dpkg -l 'php8.1*' | awk '/^ii/ {print $2, $3}'

Das ist keine universelle Prüfung. Auf anderen Distributionen, in Containern oder bei selbst kompiliertem PHP sieht der Weg anders aus, und welche Revision den Fix enthält, sagt einem nur der jeweilige Anbieter.

Wer nach Silvester auf 8.2 bleibt, sollte sich deshalb vom Hoster oder Distributor schriftlich geben lassen, bis wann er Sicherheitsfixes zurückportiert. Steht da kein Datum, ist das auch eine Antwort.

Drei Baustellen, die man auseinanderhalten sollte

Wenn von "PHP-Upgrade" die Rede ist, stecken meistens drei verschiedene Dinge dahinter: die Sprachmigration selbst, das Framework-Upgrade und die Pakete und Infrastruktur drumherum. Für die Planung hilft es, die getrennt anzuschauen, weil der Aufwand selten dort sitzt, wo man ihn vermutet.

Die Sprache. Von 8.2 auf 8.3 oder 8.4 ändert sich an der Sprache weniger, als man erwarten würde. Deprecations brechen nichts, sie füllen erst mal nur das Log. Die bekannteste in 8.4 betrifft implizit nullable Parameter, also string $x = null statt ?string $x = null. Wie ich die abarbeite, habe ich im Beitrag zu PHP 8.4 beschrieben. Wer allerdings noch von 7.4 kommt, hat ganz andere Themen. Der Wechsel auf 8.0 hat zum Beispiel die Vergleiche zwischen Strings und Zahlen geändert, und das bricht nicht laut, sondern liefert still andere Ergebnisse. Das ist eine eigene Migration, keine Versionsumstellung.

Extensions und Infrastruktur. Mit 8.4 sind IMAP, OCI8, PDO_OCI und pspell aus dem PHP-Kern geflogen und nur noch über PECL zu haben. Wer Mails per imap_open() abholt oder gegen Oracle spricht, merkt das spätestens beim ersten Deploy. Dazu kommen Erweiterungen, die pro PHP-Version gebaut werden müssen: Code-Encoder, Caching-Module, Bildbibliotheken. Die müssen für die Zielversion verfügbar sein, bevor man umstellt, nicht danach.

Frameworks und Abhängigkeiten. In gewachsenen Framework-Anwendungen liegt hier fast immer der eigentliche Brocken. Symfony 8 setzt PHP 8.4 voraus. Laravel 13 fordert im Framework selbst PHP 8.3, neuere 13.x-Versionen lassen aber auch Symfony-8-Komponenten zu, und die brauchen 8.4. Auf einem Server mit 8.3 löst Composer dann eigentlich Symfony 7.4 auf. Wird das Lockfile aber auf einem Rechner mit 8.4 erzeugt und auf 8.3 deployt, passt es nicht mehr zusammen. Je nach Patchstand kann also praktisch ebenfalls 8.4 nötig werden.

Umgekehrt ist eine alte Framework-Version, die selbst keine Updates mehr bekommt, mit einer neuen PHP-Version oft gar nicht offiziell getestet. Wenn eine Anwendung auf 8.2 festhängt, dann selten wegen PHP selbst, sondern weil das Framework darunter alt ist. Dann ist die PHP-Umstellung eigentlich ein Framework-Upgrade, und das plant man anders. Wie das schrittweise geht, steht im Laravel-Upgrade-Beitrag.

Und ohne Framework?

Fehlt das Framework, entfällt zwar dieser große zentrale Blocker. Einfacher wird die Migration dadurch aber nicht automatisch. Der Aufwand verschiebt sich nur, und zwar in den eigenen Code.

Wenn Composer im Spiel ist, gilt dasselbe wie mit Framework: Alle Bibliotheken müssen gegen die Zielversion aufgelöst werden. In frameworklosen Anwendungen sind das typischerweise Mailer, PDF-Generatoren, HTTP-Clients, XML- und SOAP-Bibliotheken, Hersteller-SDKs und alte PSR-Pakete. Gibt es gar keine Composer-Abhängigkeiten, rücken der PHP-Code selbst, die Extensions und die Infrastruktur ganz in den Mittelpunkt.

Im Code sind es meistens dieselben Stellen, an denen der Wechsel sichtbar wird: selbst geschriebene Autoloader, Bootstrap-Dateien, die überall eingebunden werden, globale Zustände, direkte Datenbankzugriffe ohne Abstraktion, eine eigene Fehlerbehandlung und dynamische Properties. Und dann die einzelnen Einstiegspunkte. Mit Framework läuft fast alles durch einen Front-Controller. Ohne Framework liegt unter public/ oft ein ganzer Satz einzelner PHP-Dateien, dazu Admin-Skripte, Webhooks, Cronjobs, Exporte, Reports und Deploy-Skripte. Jede davon ist ein eigener Einstieg, und jede muss geprüft werden.

Ohne Framework gibt es weniger zentrale Magie. Es gibt aber auch weniger zentrale Stellen, an denen man die Kompatibilität auf einen Schlag prüfen kann.

Wie ich die Umstellung angehe

Zuerst schaue ich mir an, welche PHP-Versionen tatsächlich laufen, und zwar überall, nicht nur beim Webserver. Die CLI-Version, mit der Cronjobs und Queue-Worker laufen, ist erstaunlich oft eine andere als die von PHP-FPM. Auf einem typischen Debian- oder Ubuntu-System sieht das so aus:

php -v
php-fpm8.2 -v

Die Namen der Binaries hängen von Distribution und Installationsweg ab. Entscheidend ist, dass jede SAPI geprüft wird, die im Einsatz ist: PHP-FPM oder das Apache-Modul für die Webanfragen, die CLI für alles andere. Zum "alles andere" gehören Cronjobs, Queue- und Supervisor-Worker (bei Laravel auch Horizon), Export- und Reportprozesse, Webhooks, Deploy-Skripte, direkte Shell-Aufrufe von PHP und bei frameworklosen Anwendungen die einzelnen Einstiegsskripte. Und es reicht nicht, nur die Version zu prüfen. CLI und FPM haben auf den meisten Systemen getrennte Konfigurationen, also kann eine Extension im einen geladen sein und im anderen fehlen. php -m zeigt das für die CLI, für FPM hilft ein kurzer Blick in phpinfo() auf einer nicht öffentlichen Seite. Die gehört danach sofort wieder entfernt, denn sie verrät Pfade, Versionen und Konfiguration.

Dann lasse ich Composer gegen die Zielversion auflösen, ohne dass auf dem Server schon irgendwas umgestellt ist. Dafür setze ich die Plattform testweise hoch:

 "config": {
     "platform": {
-        "php": "8.2.0"
+        "php": "8.4.0"
     }
 }

Ein composer update --dry-run zeigt danach, welche Paketversionen Composer gemäß den vorhandenen Constraints auflösen würde, und meldet die Pakete, die sich mit der Zielversion nicht vertragen. Diese Liste ist für mich die erste Aufwandsschätzung, nicht die Zahl der Deprecations. Sie zeigt aber nicht automatisch jede notwendige Codeänderung und ersetzt keine Tests. Dieselbe Einstellung hilft auch gegen das Lockfile-Problem von oben: Steht dort die PHP-Version, die in Produktion läuft, löst Composer gegen diese Version auf und nicht gegen die des Entwicklerrechners. Das setzt allerdings voraus, dass der Wert nach dem Test wieder auf den tatsächlichen Produktionsstand zurückgesetzt und bei jedem Server-Update mitgepflegt wird. Extensions deckt die Einstellung nur ab, wenn man sie dort ebenfalls einträgt.

Parallel stelle ich PHPStan auf die Zielversion um:

parameters:
    level: 5
    phpVersion: 80400

Damit prüft PHPStan gegen die Sprachregeln von 8.4, auch wenn lokal noch 8.2 läuft. Es findet natürlich nur, was sich statisch erkennen lässt. Laufzeitverhalten bleibt Sache der Tests. Wie man PHPStan in eine Codebase einzieht, die das noch nie gesehen hat, steht im PHPStan-Beitrag.

Und dann lohnt sich ein Blick ins Deprecation-Log, das 8.2 schon seit Jahren schreibt. In vielen Produktionsumgebungen ist E_DEPRECATED abgeschaltet, also hat es nie jemand gelesen. Der Klassiker aus 8.2 sind dynamische Properties:

// vorher: seit 8.2 deprecated, soll mit PHP 9 zum Fehler werden
class Rechnung
{
    public function __construct(array $daten)
    {
        foreach ($daten as $feld => $wert) {
            $this->$feld = $wert;
        }
    }
}
// nachher: Properties deklariert, Felder explizit zugewiesen
class Rechnung
{
    public string $nummer;
    public int $betragInCent;

    public function __construct(array $daten)
    {
        $this->nummer = (string) $daten['nummer'];
        $this->betragInCent = (int) $daten['betrag_in_cent'];
    }
}

Die Casts im Beispiel sorgen nur dafür, dass der Typ stimmt. Eine Validierung sind sie nicht: Aus "abc" wird mit (int) stillschweigend 0. Woher die Daten kommen, sollte vorher geprüft werden, entweder beim Einlesen oder in einer eigenen Factory.

Für 8.4 ist dieser Umbau noch kein Muss. Aber wer ohnehin gerade an der PHP-Version arbeitet, räumt das am besten gleich mit auf, statt es beim nächsten Sprung wieder vor sich zu haben. #[\AllowDynamicProperties] erlaubt dynamische Properties für die jeweilige Klasse und lässt die Deprecation verschwinden. Das Design- und Typisierungsproblem dahinter löst es nicht.

Der Rest ist Handwerk: auf Staging umstellen, Fehlerlog an, die kritischen Abläufe durchklicken. Dazu gehören auch die, die keiner von Hand aufruft, also Exporte, Reports und geplante Jobs. Erst dann Produktion in einem ruhigen Zeitfenster. Solange die alte Version noch installiert ist, ist der Rollback dieselbe Einstellung rückwärts.

Wenn es bis Silvester nicht reicht

Die Zeit bis Silvester ist für eine Anwendung mit altem Framework darunter knapp. Panik hilft da aber nicht, ein ehrlicher Zwischenplan schon.

Erst mal auf 8.2.35 patchen, falls das noch nicht passiert ist. Das ist das Mindeste, und es dauert Minuten. Dann den Backport-Zeitraum schriftlich klären, wie oben beschrieben. Wer noch mod_php einsetzt, sollte sich den Umstieg auf PHP-FPM anschauen. Eine WAF verringert das Risiko, beseitigt es aber nicht. Und vor allem: Die Arbeit, die die Umstellung blockiert, bekommt einen Verantwortlichen und ein Datum. Ohne Datum wird aus "nach Silvester" schnell "irgendwann".

Stolperfallen

Worker vergessen. Queue-Worker, Horizon, alles was unter Supervisor dauerhaft läuft, behält die alte PHP-Binary, bis es neu gestartet wird. Das gilt für den Versionswechsel genauso wie für jeden Sicherheitspatch. FPM ist neu gestartet, der Worker läuft seit Wochen mit der verwundbaren Version weiter.

Nur den Webserver prüfen. Wenn die CLI noch auf 8.2 steht, laufen Cronjobs und Deploy-Skripte weiter auf der alten Version, und niemand merkt es, weil die Webseite ja richtig antwortet.

Backport annehmen statt prüfen. Bei EOL-Versionen sagt die Versionsnummer nichts darüber, ob ein Fix drin ist. Nur die Paket-Revision zählt, und welche das ist, weiß der Anbieter.

Die Zielversion pauschal wählen. 8.4 bekommt bis Ende 2028 Sicherheitsupdates und ist deshalb das länger tragende Ziel, sofern die Abhängigkeiten mitspielen. Wenn Framework-Version, Extensions oder Drittanbieterpakete 8.4 noch nicht unterstützen, ist 8.3 ein sinnvoller Zwischenschritt, mit Support bis Ende 2027. Dann aber mit einem festen Termin, an dem man das wieder prüft. 8.5 bringt noch ein Jahr mehr, ist aber nur dann das richtige Ziel, wenn Frameworks, Extensions, Distributionspakete und Drittanbieter es schon vollständig mittragen.


Ob die Umstellung ein Nachmittag ist oder ein Projekt, hängt selten an PHP selbst. Meistens hängt es am Framework, an den Abhängigkeiten oder am gewachsenen Anwendungscode, der nie gegen eine neuere PHP-Version geprüft wurde. Wenn Sie wissen wollen, wo Ihre Anwendung da steht und was bis Jahresende realistisch ist, schildern Sie mir kurz Ihre Ausgangslage. Ich melde mich mit einer ersten Einschätzung. Termin buchen.