Alle Artikel
29. September 2026
10 mins

PHP 8.2 endet am 31. Dezember: was jetzt zu tun ist

Von Tim Rutte, Cloud & Software ArchitectThemaLegacy & Modernisierung

Eine Sanduhr mit weißem Sand, deren oberer Kolben fast leer ist, daneben liegt ein blauer Schraubenschlüssel bereit.

Am 31. Dezember 2026 endet die Sicherheitsunterstützung für PHP 8.2.

Danach bleibt jede neue Lücke offen.

Am 1. Januar läuft Ihre Anwendung trotzdem weiter, genauso schnell und genauso stabil wie am Tag davor. Genau deshalb wird dieser Termin so oft verschoben. Es passiert nichts, das jemand sieht. Es kommt nur nichts mehr nach.

Dieser Artikel klärt, ob der Termin Sie überhaupt trifft, auf welche Version Sie gehen sollten und was der Sprung von 8.2 kostet. Am Ende steht ein Plan für die Wochen, die bis Dezember bleiben. Was ein End of Life grundsätzlich bedeutet, steht im Glossar. Hier geht es um diesen einen Termin.

Trifft der Termin Sie überhaupt?

Die Frage klingt banal. Sie ist es nicht. Der 31. Dezember gilt für das PHP von php.net. Ob er für Ihr PHP gilt, hängt davon ab, woher es kommt.

  • Offizielle Docker-Images und selbst gebaute Binaries folgen php.net. Ein Image wie php:8.2-fpm bekommt nach dem Termin keine neuen PHP-Versionen mehr.
  • Debian 12 liefert PHP 8.2 als Systempaket aus. Die Distribution ist seit Juni 2026 in der Langzeitunterstützung, das Debian-LTS-Team patcht ihre Pakete bis zum 30. Juni 2028. Welche Pakete davon ausgenommen sind, zeigt check-support-status aus dem Paket debian-security-support. Hier verschiebt sich der Termin also um anderthalb Jahre.
  • Paketquellen wie die von Ondřej Surý oder Remi Collet folgen php.net und nehmen eine Version nach ihrem Ende aus der Pflege.
  • Beim Managed Hosting entscheidet der Anbieter. Manche stellen still um, manche verlangen einen Aufpreis für alte Versionen, manche kündigen sie ab.

Wie weit das Gedachte von der Wirklichkeit abweichen kann, habe ich im September in einem Projekt gesehen. Die lokale Entwicklungsumgebung lief auf einem Basis-Image vom Dezember 2024. Die CI testete jeden Tag gegen eines vom Dezember 2025. Das neuere Image war nie in die Registry gespiegelt worden, aus der die Entwickler zogen. Niemand hatte das entschieden. Es war einfach so gekommen.

Deshalb prüfe ich zuerst auf jeder Umgebung, nicht im Repository:

php -v
php -m
dpkg -l 'php8.2*' 2>/dev/null | grep ^ii

Und zwar auf jeder: Produktion, Staging, CI, Worker, der Server mit den Cronjobs. Der mit den Cronjobs ist oft der, auf dem noch etwas Älteres läuft, weil ihn beim letzten Upgrade niemand auf der Liste hatte.

Das Problem kommt über Composer, nicht über PHP

Angenommen, Ihre Distribution patcht PHP 8.2 bis 2028. Ihre Abhängigkeiten warten trotzdem nicht so lange.

Symfony 8.0 verlangt PHP 8.4. Laravel 13 verlangt offiziell PHP 8.3, erlaubt ab Version 13.3 aber Symfony-8-Komponenten und landet damit in der Praxis oft bei 8.4. Wer das Lockfile auf einem Rechner mit PHP 8.4 erzeugt und auf 8.3 ausrollt, bekommt beim Start der Anwendung diese Meldung aus der Plattformprüfung von Composer:

Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= 8.4.0".

Das ist der eigentliche Mechanismus hinter einem End of Life. Nicht der Interpreter wird unsicher. Die Sicherheitsupdates Ihrer Bibliotheken erscheinen in Versionen, die Sie nicht mehr installieren können.

Welche Pakete Sie festhalten, sagt Composer selbst:

composer why-not php 8.4

Und eine Einstellung setze ich in jedem Upgrade als Erstes: Die PHP-Version, gegen die Composer auflöst, steht im Projekt und nicht auf dem Rechner, der gerade das Update macht.

composer config platform.php 8.2

Während der Umstellung steht dort 8.2. Am Tag der Umschaltung wird daraus die Zielversion. So kann niemand versehentlich ein Lockfile erzeugen, das nur auf seinem eigenen Laptop funktioniert.

8.3, 8.4 oder 8.5?

VersionErschienenSicherheitsupdates bisMeine Einschätzung
PHP 8.3November 202331.12.2027kauft ein Jahr, dann dieselbe Frage
PHP 8.4November 202431.12.2028meine Voreinstellung
PHP 8.5November 202531.12.2029wenn Composer zustimmt
PHP 8.619.11.202631.12.2030nicht für einen Umstieg im Dezember

PHP 8.3 kauft Ihnen zwölf Monate. Im Dezember 2027 stehen Sie vor derselben Frage, mit demselben Aufwand. Das lohnt sich nur, wenn 8.4 an einem konkreten Blocker scheitert, etwa an einer Erweiterung, die es dort nicht mehr gibt. Dazu gleich mehr.

PHP 8.4 ist meine Voreinstellung. Zwei Jahre Ruhe, ein eingeschwungenes Ökosystem, und Symfony 8 und aktuelle Laravel-Versionen setzen es ohnehin voraus.

Warum dann nicht gleich PHP 8.5? Der Einwand ist berechtigt. Ein Jahr mehr für fast denselben Aufwand. Ich entscheide das an einer Stelle: composer why-not php 8.5. Ist die Liste leer, nehme ich 8.5. Stehen dort Pakete, die Sie nicht selbst in der Hand haben, nehme ich 8.4 und plane 8.5 als Routinewartung für 2027.

PHP 8.6 erscheint am 19. November. Eine x.0-Version fünf Wochen vor dem Stichtag in Produktion zu bringen, ist kein Plan. Es ist eine Wette.

Was zwischen 8.2 und 8.4 bricht

Die gute Nachricht zuerst: Der schwere Teil liegt hinter Ihnen. Wer auf 8.2 läuft, hat den Bruch von PHP 8.0 schon hinter sich, also die TypeErrors, den geänderten Vergleich zwischen Strings und Zahlen und die entfernten Altkonstrukte.

Wer noch davor steht, findet den ganzen Weg in PHP 7.4 auf PHP 8.4.

Zwischen 8.2 und 8.4 kommt deutlich weniger. Fünf Stellen treffen alte Codebasen trotzdem:

  • Implizit nullable Parameter. Eine Signatur wie function speichern(string $name = null) ist seit 8.4 deprecated und muss ?string $name = null heißen. Mechanisch, aber in vielen Dateien. Rector erledigt das vollständig.
  • Ausgelagerte Erweiterungen. IMAP, OCI8, PDO_OCI und pspell gehören seit 8.4 nicht mehr zum PHP-Kern, sie liegen jetzt in PECL. Ein Postfachabruf über imap_open() oder eine Oracle-Anbindung ist damit kein Hinweis im Log, sondern ein Blocker. Das ist der häufigste Grund, warum 8.4 nicht sofort geht.
  • Die Kosten von bcrypt. password_hash() mit PASSWORD_DEFAULT rechnet seit 8.4 mit Kostenfaktor 12 statt 10. Bestehende Hashes bleiben gültig. Jeder Login, der über password_needs_rehash() neu hasht, braucht danach aber rund viermal so viel Rechenzeit. Auf einem knapp bemessenen Server mit einer Lastspitze um acht Uhr morgens merkt man das.
  • Kleinere Brüche aus 8.3. range() prüft seine Argumente strenger und wirft bei ungültigen Werten Fehler, wo es früher still ein Ergebnis lieferte. get_class() ohne Argument ist deprecated. Selten, aber in altem Code nicht nie.
  • Stumm geschaltete Meldungen aus 8.2. Dynamische Properties sind seit 8.2 deprecated. Viele Teams haben die Meldungen damals mit #[AllowDynamicProperties] oder einem angepassten error_reporting zum Schweigen gebracht. Das trägt auch unter 8.4. Aber genau dort wird PHP 9 zum Fehler, und wer das Upgrade macht, sollte wissen, wie viele Stellen es sind.

Den mechanischen Teil erledigen zwei Werkzeuge. PHPStan bekommt die Zielversion, damit es gegen 8.4 analysiert und nicht gegen die Version, mit der es gerade läuft:

parameters:
    phpVersion: 80400
    level: 5

Rector schreibt die Signaturen und die übrigen mechanischen Änderungen um:

<?php

use Rector\Config\RectorConfig;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withPhpSets(php84: true);

Einen Lauf über das ganze Projekt committe ich nie am Stück. Eine Regel, ein Commit, ein Review. Ein Diff über 800 Dateien ist nicht mutig, sondern unlesbar.

Welche Regeln ich abschalte und welcher Rest von Hand bleibt, steht in Rector im Altprojekt.

Wie PHPStan in eine Codebasis kommt, die bisher keine statische Analyse kannte, steht in PHPStan Stufe für Stufe in Altcode einführen.

Wo die Zeit wirklich hingeht

Der PHP-Sprung selbst sprengt selten den Zeitplan. Das tun die Abhängigkeiten, die er mitzieht.

Im selben Projekt vom September brach nach dem Wechsel auf Doctrine DBAL 4 der Start der Entwicklungsumgebung ab, mit der Meldung „Unknown database type enum“. DBAL 4 kennt keinen ENUM-Typ mehr, und die Datenbank hatte solche Spalten aus Migrationen in rohem SQL. Jede Schema-Introspektion brach ab.

Die Lösung war eine Zeile:

$connection->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');

Die eigentliche Arbeit war die Frage, wo diese Zeile hingehört. Nicht in die Verbindungskonfiguration der Anwendung. Dort hätte sie jede Datenbankverbindung von lazy auf eager umgestellt, weil die Plattform erst beim Verbindungsaufbau feststeht. Sie steht jetzt in den beiden Einstiegspunkten der Kommandozeile, und nur dort.

Eine Woche später kam die nächste Runde: doctrine/migrations 3.9, PHPStan 2.2, Codeception 5.3. Beim Nachziehen fiel eine Zeile auf, die jahrelang falsch gewesen war:

$daten = json_decode($json, true, JSON_THROW_ON_ERROR);

Der dritte Parameter von json_decode() ist die Verschachtelungstiefe, nicht die Flags. Die Konstante landete als Tiefe von gut vier Millionen im Aufruf. Eine Exception gab es nie, fehlerhaftes JSON kam still als null zurück. Richtig ist:

$daten = json_decode($json, true, 512, JSON_THROW_ON_ERROR);

Kein Upgrade hat diesen Fehler verursacht. Das Upgrade hat ihn gefunden.

Das ist die Sorte Befund, die in jedem Upgrade auftaucht und in keiner Schätzung steht. Ich plane dafür Puffer ein. Nicht weil ich erwarte, dass etwas schiefgeht, sondern weil alter Code Annahmen enthält, die erst auffallen, wenn man ihn anfasst.

Was der Sprung von 8.2 kostet

Weniger, als die meisten erwarten. Wenn die Voraussetzungen stimmen.

Eine Anwendung auf 8.2 mit Composer, einer CI und einem Framework in einer unterstützten Version ist ein Vorhaben von einigen Tagen bis etwa zwei Wochen. Rector erledigt den Großteil, PHPStan zeigt den Rest, und die Tests auf den kritischen Pfaden sagen, ob es stimmt.

Teurer wird es an vier Stellen:

  • Das Framework hängt hinterher. Symfony 6.4 läuft auf PHP 8.4, aber wer zu Symfony 8 will, muss vorher durch die Deprecations von 7.4. Bei Laravel bekommen die Versionen 10 und 11 keine Sicherheitsupdates mehr, und der Weg zu 13 führt über jede Hauptversion dazwischen. Dann ist das PHP-Upgrade der kleinere Teil. Für beide Fälle gibt es eigene Seiten: Symfony-Upgrade und Laravel-Upgrade.
  • Eine Erweiterung ist ausgelagert. Eine PECL-Erweiterung einmal zu bauen, dauert eine Stunde. Sie über Jahre in jedes Image mitzunehmen, ist eine Entscheidung. Meist ist es billiger, den Postfachabruf auf eine Bibliothek in reinem PHP umzustellen.
  • Es gibt keine Tests auf den kritischen Pfaden. Dann ist jedes Upgrade ein Blindflug, auch ein kleines. Zuerst kommen Charakterisierungstests für Checkout, Login, Abrechnung und Schnittstellen, und die kosten mehr als das Upgrade selbst.
  • PHP lebt auf einem Server, den jemand von Hand eingerichtet hat. Dann ist das Upgrade eine Neuinstallation mit ungewissem Ausgang. Ich baue die Umgebung in diesem Fall als Image neu, und das Upgrade wird zum Nebeneffekt.

Wie man Tests in Code bekommt, der nie welche hatte, steht in Legacy-Code testen, wenn es keine Tests gibt.

Und die Alternative? Bezahlten Langzeitsupport für PHP 8.2 bieten mehrere Anbieter an. Das kann eine Brücke über ein paar Monate sein. Es löst aber nicht das Problem aus dem Abschnitt über Composer: Ihre Bibliotheken ziehen trotzdem weiter.

Wie ich ein PHP-Upgrade abwickle, von der Inventur bis zur Umschaltung, steht auf einer eigenen Seite.

Ein Plan für die Wochen bis Dezember

Der 31. Dezember ist nicht Ihr Termin. Ihr Termin liegt früher.

Viele Unternehmen frieren ab Mitte Dezember Änderungen an Produktionssystemen ein. Wer bis dahin nicht umgestellt hat, stellt entweder im Änderungsstopp um oder läuft im Januar ohne Sicherheitsupdates. Von heute an bleiben damit rund elf Wochen.

  1. Bis Mitte Oktober: Inventur. Jede Umgebung prüfen wie oben, composer why-not php 8.4 ausführen, die Erweiterungen auflisten und die Zielversion festlegen.
  2. Bis Ende Oktober: die CI gegen beide Versionen. Die Pipeline läuft parallel gegen 8.2 und 8.4. Solange 8.2 grün bleibt, ist jede Änderung auslieferbar.
  3. Im November: Umbau in kleinen Schritten. Rector regelweise, PHPStan auf der Zielversion, Abhängigkeiten nachziehen. Jeder Schritt geht einzeln in den Hauptzweig, kein Branch lebt länger als ein paar Tage.
  4. Ende November: Staging auf 8.4. Zwei Wochen echter Betrieb auf Staging, einschließlich der Cronjobs und Worker, nicht nur der Weboberfläche.
  5. Anfang Dezember: Umschalten mit Rückweg. Die neue Umgebung steht parallel, der Verkehr wird umgeschaltet, die alte bleibt stehen, bis die neue sich bewiesen hat. Das ist ein Blue/Green-Deployment, und der Rückweg gehört zur Lieferung.

Für den zweiten Schritt reicht in GitHub Actions eine Matrix:

strategy:
  matrix:
    php: ['8.2', '8.4']
steps:
  - uses: shivammathur/setup-php@v2
    with:
      php-version: ${{ matrix.php }}

Damit Sie im Dezember 2028 nicht wieder elf Wochen vor dem Termin davon erfahren: Ein EOL-Kalender für den ganzen Stack.

Häufige Fragen

Läuft meine Anwendung am 1. Januar noch? Ja. Nichts schaltet sich ab. Es erscheinen nur keine Sicherheitsupdates mehr, und jede Lücke, die danach bekannt wird, bleibt in Ihrer Installation offen. Für Audits, Cyberversicherungen und Kunden mit Sicherheitsanforderungen ist eine Version nach dem End of Life außerdem ein Befund im Patch-Management.

Kann ich 8.3 überspringen? Ja. Von 8.2 direkt auf 8.4 ist der normale Weg. Was 8.3 geändert hat, zeigt die Analyse gegen 8.4 ohnehin mit an.

Reicht es, wenn der Hoster umstellt? Nur wenn Ihr Code vorher gegen die neue Version geprüft wurde. Sonst findet die Umstellung Ihre Fehler in Produktion.

Was ich diese Woche tun würde

Wenn Sie heute auf PHP 8.2 sind, würde ich diese Woche zwei Befehle ausführen: php -v auf jeder Umgebung und composer why-not php 8.4 im Projekt.

Sind beide Ergebnisse unauffällig, ist das Upgrade ein kleines Vorhaben. Dann beginnen Sie im Oktober, nicht im Dezember. Steht in der zweiten Liste ein Framework oder eine Erweiterung, ist es ein Projekt, und dann zählt jede Woche.

PHP 8.2 wird am 1. Januar nicht langsamer. Es wird nur nie wieder sicherer.