Upgrade von PHP 8.1 auf 8.4: was zu beheben ist und in welcher Reihenfolge

Für PHP 8.1 gibt es kein offizielles PHP2Shell-Release. Diese Anleitung zeigt die Änderungen aus 8.2, 8.3 und 8.4, die echte Websites brechen, mit Scan-Befehlen.

Wo Sie stehen

PHP 8.1 hat am 31. Dezember 2025 das End of Life erreicht. Für PHP2Shell (CVE-2026-109618 und CVE-2026-109619) hat das PHP-Projekt für 8.1 nur einen Backport-Branch im Quellcode veröffentlicht. Ein 8.1-Release mit den Fixes gibt es nicht. Die behobenen Releases sind 8.2.35, 8.3.36, 8.4.27 und 8.5.12. Der unterstützte Fix ist der Wechsel nach oben. Siehe PHP 8.1 und, falls Sie noch nicht wechseln können, was Sie in der Zwischenzeit tun können.

Ziel ist 8.4 (behoben in 8.4.27). Unter Welche Version Sie wählen sollten steht, warum nicht 8.2.

Wenn Sie von 7.4 oder 8.0 kommen, nutzen Sie die Anleitung von 7.4 auf 8.4. Sie deckt auch die Änderungen aus 8.0 und 8.1 ab.

Reihenfolge der Arbeit

  1. Sichern Sie Dateien und Datenbank und testen Sie die Wiederherstellung.
  2. Bauen Sie eine Staging-Kopie auf 8.4 mit denselben Extensions und php.ini-Werten.
  3. Aktualisieren Sie CMS, Framework, Plugins und Composer-Pakete auf Versionen, die 8.4 offiziell unterstützen. composer why-not php 8.4 listet die Blocker auf.
  4. Führen Sie den Kompatibilitäts-Scan weiter unten aus.
  5. Beheben Sie zuerst die Funde aus der Liste für 8.2. Dynamic Properties und utf8_encode() sind die üblichen Kandidaten.
  6. Betreiben Sie Staging mit error_reporting = E_ALL und log_errors = On. Testen Sie Logins, Formulare, Uploads, Checkout und Cronjobs. Lesen Sie das Log.
  7. Stellen Sie die Produktion um, starten Sie jeden PHP-Worker neu und prüfen Sie die laufende Version.

Der direkte Sprung von 8.1 auf 8.4 ist in Ordnung. Behandeln Sie die drei Versionsabschnitte unten als eine einzige Checkliste.

PHP 8.2

  • Dynamic Properties sind deprecated. Wer einer Property einen Wert zuweist, die die Klasse nie deklariert hat, bekommt eine Deprecation. Deklarieren Sie die Property. Als Notlösung setzen Sie #[\AllowDynamicProperties] an die Klasse. stdClass und Klassen mit __get() und __set() sind nicht betroffen. Am häufigsten kommt das aus alten Plugins.
  • Interpolation mit "${var}" und "${expr}" ist deprecated. Nutzen Sie "{$var}" oder "$var".
  • utf8_encode() und utf8_decode() sind deprecated. Nutzen Sie mb_convert_encoding($s, 'UTF-8', 'ISO-8859-1') oder iconv().
  • Relative Callables wie "self::method" und ["parent", "method"] sind deprecated. Nutzen Sie [self::class, 'method'] oder self::method(...).
  • mbstring-Encodings. Base64, QPrint, Uuencode und HTML-ENTITIES als mbstring-Encodings sind deprecated. Nutzen Sie base64_encode(), quoted_printable_encode(), convert_uuencode() und htmlentities().
  • Funktionen für Groß- und Kleinschreibung ignorieren die Locale. strtolower(), strtoupper(), ucfirst(), ucwords() und ähnliche arbeiten jetzt wie in der Locale "C". Für Text mit Akzenten oder nicht lateinischer Schrift nehmen Sie die mb_*-Varianten.
  • Kleinere Änderungen. str_split('') liefert [] statt [""]. var_export() setzt einen führenden Backslash vor Klassennamen. glob() liefert ein leeres Array statt false, wenn open_basedir jeden Pfad sperrt.

PHP 8.3

8.3 bricht wenig. Deshalb taugt es als Ausweichziel.

  • range() ist strenger. Eine Schrittweite von 0, eine negative Schrittweite bei aufsteigendem Bereich sowie INF oder NAN werfen ValueError. Einige Aufrufe mit gemischten Typen wie range(5, 'z') erzeugen jetzt eine Warning.
  • number_format() rundet bei negativem $decimals jetzt vor dem Dezimalkomma, statt den Wert zu ignorieren.
  • Array-Keys. Nachdem einem leeren Array ein negativer Index zugewiesen wurde, nutzt das nächste $a[] diesen Index plus eins statt 0.
  • Datumsfehler nutzen die neuen Klassen DateError und DateException statt Warnings.
  • Erkennung von Stack Overflows. Sehr tiefe Rekursion kann jetzt einen Error werfen, bevor der Prozess abstürzt.
  • Deprecated: get_class() und get_parent_class() ohne Argumente, assert_options() und die INI-Einstellungen assert.*, ReflectionProperty::setValue() mit nur einem Argument, ldap_connect() mit getrenntem Host und Port sowie der Modus MT_RAND_PHP.
  • Das Inkrementieren nicht numerischer Strings wie $s = 'a'; $s++ ist soft deprecated. Neuer Code sollte str_increment() aufrufen.
  • Entfernte INI-Einstellung: opcache.consistency_checks.

PHP 8.4

  • Implizit nullable Parameter. function f(Foo $a = null) ist deprecated. Schreiben Sie ?Foo $a = null. Veraltete Bibliotheken lösen das bei jedem Request aus, aktualisieren Sie also erst die Pakete, bevor Sie Ihren eigenen Code bewerten.
  • E_STRICT ist als Error-Level entfernt, und trigger_error() mit E_USER_ERROR ist deprecated. Werfen Sie stattdessen eine Exception.
  • CSV-Funktionen. Sich auf das Standardargument escape von fgetcsv(), fputcsv() und str_getcsv() zu verlassen, ist deprecated. Übergeben Sie es explizit. str_getcsv() wirft außerdem, wenn Trennzeichen oder Begrenzer nicht genau ein Byte lang sind.
  • Nach PECL verschoben: IMAP, OCI8, PDO_OCI und PSpell. Installieren Sie sie aus PECL oder wechseln Sie auf eine andere Bibliothek.
  • Session-INI-Änderungen sind deprecated für session.sid_length, session.sid_bits_per_character, session.use_only_cookies, session.use_trans_sid, session.trans_sid_tags, session.trans_sid_hosts und session.referer_check. Entfernen Sie sie aus php.ini und .user.ini, sofern Sie sie nicht brauchen.
  • mysqli: mysqli_ping(), mysqli_kill() und mysqli_refresh() sind deprecated.
  • round() wirft ValueError bei einem ungültigen Modus. Das alte Vorrunden ist entfallen, einige Randergebnisse ändern sich dadurch.
  • exit() verhält sich wie eine Funktion. Es beachtet jetzt strict_types und wirft TypeError bei einem ungültigen Typ.
  • Die JIT-Standardwerte haben sich geändert. Wenn Sie JIT nur durch Setzen von opcache.jit_buffer_size eingeschaltet haben, müssen Sie jetzt zusätzlich opcache.jit setzen.
  • Der bcrypt-Cost steht standardmäßig auf 12 statt 10, das Hashen von Passwörtern dauert also länger. Bestehende Hashes lassen sich weiter prüfen.
  • Namenskollisionen. Jede in 8.4 neu hinzugekommene Funktion oder Klasse bricht Code, der sie bereits definiert. Sichern Sie Polyfills mit function_exists() ab.
  • Höhere Mindestversionen für Bibliotheken: libcurl 7.61.0, libxml2 2.9.4, OpenSSL 1.1.1. Für mod_php ist Apache 2.4 das Minimum.

Befehle zum Scannen und Beheben

Führen Sie diese Befehle auf einem Arbeitsrechner oder in der CI aus.

composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcompatibility/php-compatibility:"^10.0.0@dev"
vendor/bin/phpcs -i

vendor/bin/phpcs -ps --standard=PHPCompatibility --runtime-set testVersion 8.1-8.4 \
  --ignore='*/vendor/*,*/node_modules/*' --report-full=compat-report.txt .

testVersion 8.1-8.4 meldet Probleme, die irgendeine Version im Bereich betreffen. So bleibt der Code sicher, solange 8.1 und 8.4 nebeneinander laufen. Stellen Sie nach der Umstellung auf 8.4 um. Die Versionsangabe ^10.0.0@dev ist die, die das README von PHPCompatibility am 22. Oktober 2026 zeigt. Sie braucht PHP_CodeSniffer 4.0.2 oder neuer.

Rector kann einige Fixes automatisch anwenden, etwa explizite nullable Typen. Prüfen Sie jeden Diff.

composer require rector/rector --dev
vendor/bin/rector process --dry-run
vendor/bin/rector process

Mit return RectorConfig::configure()->withPaths([__DIR__ . '/src'])->withPhpSets(); in rector.php liest Rector die Zielversion aus composer.json.

Lassen Sie Plugin- und Theme-Code, der nicht Ihnen gehört, beim Scannen und Umschreiben außen vor. Aktualisieren Sie ihn stattdessen. Die Schritte für WordPress stehen unter PHP-Version ändern.

Zuletzt geprüft am 22. Oktober 2026.