Vorab
PHP 7.4 hat am 28. November 2022 das End of Life erreicht. Die PHP2Shell-Fixes gibt es dafür nur als Backport-Branch im Quellcode, ein 7.4-Release mit diesen Fixes existiert nicht. Der Ausweg ist der Wechsel auf einen unterstützten Branch. Ziel ist 8.4 (behoben in 8.4.27). Siehe welche Version Sie wählen sollten und PHP 7.4.
Die Arbeit zwischen 7.4 und 8.4 lässt sich nicht überspringen, Sie können sie aber in einem Durchgang erledigen. Diese Seite listet auf, was sich in jeder Version geändert hat. Jeder Punkt stammt aus den Migrationsleitfäden im PHP-Handbuch, verlinkt unter den Quellen.
Die Reihenfolge, die funktioniert
- Sichern Sie Dateien und Datenbank. Testen Sie die Wiederherstellung.
- Kopieren Sie die Website auf eine Staging-Umgebung mit PHP 8.4 und denselben Extensions.
- Aktualisieren Sie Framework, CMS, Plugins und Composer-Pakete auf Versionen, die 8.4 offiziell unterstützen.
composer outdatedundcomposer why-not php 8.4zeigen, was Sie blockiert. - Scannen Sie Ihren eigenen Code mit PHPCompatibility (Befehle weiter unten).
- Beheben Sie die Funde, zuerst die Punkte aus 8.0, denn die brechen Code.
- Betreiben Sie Staging mit
error_reporting = E_ALLundlog_errors = On. Gehen Sie jeden Ablauf durch und lesen Sie danach das Log. - Beheben Sie die Deprecations aus 8.1 bis 8.4. Sie brechen heute keinen Code, füllen aber die Logs, und die betroffenen Features können in einer späteren Version wegfallen.
- Stellen Sie die Produktion um, starten Sie jeden PHP-Worker neu und prüfen Sie die Version.
PHP 8.0: die Änderungen, die Code brechen
8.0 ist ein Major Release. Hier liegt der Großteil der echten Probleme.
- Vergleich von Strings und Zahlen.
0 == "foo"war true und ist jetzt false. Dasselbe gilt für0 == ""und42 == "42foo". Suchen Sie nach losen==-Vergleichen mit Benutzereingaben oder Datenbankwerten und nehmen Sie===. - Aus Warnings wurden Exceptions. Arithmetik mit einem nicht numerischen String wirft
TypeError.count()auf einem nicht zählbaren Wert wirftTypeError. Eine undefinierte Konstante wirft einenError(7.4 las sie als String). Eine falsche Anzahl Argumente bei einer eingebauten Funktion wirftArgumentCountError. - Aus Notices wurden Warnings. Das Lesen einer undefinierten Variable, eines undefinierten Array-Keys oder einer undefinierten Property erzeugt jetzt eine Warning. Alte Themes und Plugins werden das Log damit füllen.
- Entfernte Funktionen.
each(),create_function(),__autoload(),money_format(),get_magic_quotes_gpc()undfgetss()gibt es nicht mehr. Nutzen Sieforeach, anonyme Funktionen undspl_autoload_register(). - Entfernte Syntax.
$str{0}(nutzen Sie$str[0]), die Casts(real)und(unset)sowie Methoden mit dem Namen der Klasse als Konstruktor (nutzen Sie__construct()). - Vorrang bei der Verkettung. Bei
"Sum: " . $a + $bwird jetzt zuerst addiert. Setzen Sie Klammern, wo Sie die alte Reihenfolge gemeint haben. - Named Arguments.
call_user_func_array()behandelt String-Keys jetzt als Parameternamen, statt sie zu ignorieren. Prüfen Sie Aufrufe, die assoziative Arrays übergeben. - Resources sind Objekte.
curl_init()liefert einCurlHandle, auch die GD- und OpenSSL-Funktionen liefern Objekte. Code, deris_resource($ch)prüft, schlägt jetzt fehl. Prüfen Sie stattdessen auffalse. @unterdrückt keine Fatal Errors mehr. Error-Handler, dieerror_reporting() == 0prüfen, solltenerror_reporting() & $errnoprüfen.- Standardwerte.
error_reportingsteht standardmäßig aufE_ALL. Der Standard-Fehlermodus von PDO sind Exceptions. - Reservierte Namen.
matchist ein Schlüsselwort, undmixeddarf kein Klassenname sein.#[leitet jetzt ein Attribut ein statt eines Kommentars. - Extensions und Module.
mbstring.func_overloadist weg, XML-RPC ist nach PECL gewandert. Der Name des Apache-Moduls hat sich vonphp7_modulezuphp_modulegeändert, alteLoadModule- undIfModule-Zeilen greifen deshalb nicht mehr. - Deprecated in 8.0. Ein Pflichtparameter nach einem optionalen,
libxml_disable_entity_loader()und Sortier-Callbacks, die einen Boolean statt eines Integers zurückgeben. Nutzen Sie<=>inusort().
PHP 8.1: meist Deprecations, zwei geänderte Verhaltensweisen
nullan eingebaute Funktionen. Wernullan einen nicht nullable Parameter übergibt, etwastr_contains($x, null)odertrim($x), bekommt eine Deprecation ins Log. Bei altem WordPress-Code ist das die größte Quelle für Log-Rauschen. Casten Sie mit(string) $xoder nutzen Sie$x ?? ''.- mysqli wirft Exceptions. Der Standard-Fehlermodus wechselte von stumm zu Exceptions. Kapseln Sie Aufrufe, die sich auf stilles Scheitern verlassen haben, oder stellen Sie den alten Modus mit
mysqli_report(MYSQLI_REPORT_OFF);wieder her. htmlspecialchars()nutzt standardmäßigENT_QUOTES | ENT_SUBSTITUTE, aus'wird also'.- Rückgabetypen interner Methoden. Klassen, die
ArrayAccess,Iterator,CountableoderJsonSerializableimplementieren, brauchen einen kompatiblen Rückgabetyp oder das Attribut#[\ReturnTypeWillChange], sonst gibt es eine Deprecation. Serializableallein ist deprecated. Ergänzen Sie__serialize()und__unserialize().- Weitere Deprecations.
strftime(),FILTER_SANITIZE_STRING, das Anhängen anfalse, als wäre es ein Array, und implizite Umwandlungen von Float zu Int mit Genauigkeitsverlust, etwa$a[15.5]. - PDO liefert mit den Treibern für MySQL und SQLite Integer und Floats jetzt als native Typen.
PHP 8.2: Dynamic Properties
- Dynamic Properties sind deprecated. Wer
$obj->newField = 1auf einer Klasse setzt, dienewFieldnie deklariert hat, bekommt eine Deprecation. Deklarieren Sie die Property. Als Notlösung dient#[\AllowDynamicProperties].stdClassist ausgenommen. - String-Interpolation mit
"${var}"ist deprecated. Schreiben Sie"{$var}"oder"$var". utf8_encode()undutf8_decode()sind deprecated. Nutzen Siemb_convert_encoding().- Relative Callables wie
"self::method"sind deprecated. Nutzen Sie[self::class, 'method']. - Funktionen für Groß- und Kleinschreibung wie
strtolower()unducfirst()ignorieren jetzt die Locale. Für lokalisierten Text nehmen Sie diemb_*-Funktionen. str_split('')liefert ein leeres Array statt[""].
PHP 8.3: wenig Bruch
range()ist strenger. Eine Schrittweite von0, eine negative Schrittweite bei aufsteigendem Bereich sowie INF oder NAN werfenValueError.number_format()rundet bei negativem$decimalsjetzt vor dem Dezimalkomma.- Ein negativer erster Array-Index ändert den nächsten impliziten Key: Nach
$a[-5]ergibt$a[]den Key-4statt0. - Datumsfehler nutzen jetzt die Klassen
DateExceptionundDateErrorstatt Warnings. - Deprecated:
get_class()ohne Argumente,assert_options()und die INI-Einstellungenassert.*sowieReflectionProperty::setValue()mit nur einem Argument.
PHP 8.4: die Deprecation für nullable Parameter
- Implizit nullable Parameter.
function f(Foo $a = null)ist deprecated. Schreiben Sie?Foo $a = null. Alte Bibliotheken lösen das bei jedem Laden aus, aktualisieren Sie also erst die Pakete, bevor Sie Ihren eigenen Code bewerten. E_STRICTist weg, undtrigger_error()mitE_USER_ERRORist deprecated. Werfen Sie stattdessen eine Exception.- CSV-Escape. Sich auf das Standardargument
escapevonfgetcsv(),fputcsv()undstr_getcsv()zu verlassen, ist deprecated. Übergeben Sie es explizit. - Nach PECL verschoben: IMAP, OCI8, PDO_OCI und PSpell. Wenn Sie
imap_*-Funktionen nutzen, installieren Sie das PECL-Paket oder wechseln Sie die Bibliothek. - Session-Einstellungen. Das Ändern von
session.sid_length,session.sid_bits_per_character,session.use_only_cookies,session.use_trans_sidund verwandten Einstellungen ist deprecated, ebenso die KonstanteSID. round()wirftValueErrorbei einem ungültigen Modus, und das alte Vorrunden ist entfallen. Einige Ergebnisse ändern sich dadurch.- Der bcrypt-Cost steht standardmäßig auf 12 statt 10. Logins dauern länger. Bestehende Hashes lassen sich weiter prüfen.
- Die Standardwerte für den OPcache-JIT haben sich geändert. Wenn Sie JIT nur über
opcache.jit_buffer_sizeaktiviert haben, setzen Sie zusätzlichopcache.jit. - Neue Namen können kollidieren. Jede in 8.4 neue Funktion oder Klasse bricht Code, der denselben Namen definiert. Kapseln Sie eigene Helfer in
function_exists(). - Höhere Mindestversionen für Bibliotheken. Das Handbuch nennt libcurl 7.61.0, libxml2 2.9.4 und OpenSSL 1.1.1. Sehr alte Server erfüllen das unter Umständen nicht.
Befehle zum Scannen und Beheben
Führen Sie diese Befehle auf einem Arbeitsrechner oder in der CI aus. Auf dem Webserver haben sie nichts zu suchen.
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcompatibility/php-compatibility:"^10.0.0@dev"
vendor/bin/phpcs -i
# während des Umzugs: Code muss auf beiden Versionen laufen
vendor/bin/phpcs -ps --standard=PHPCompatibility --runtime-set testVersion 7.4-8.4 \
--ignore='*/vendor/*,*/node_modules/*' --report-full=compat-report.txt .
# nach der Umstellung: nur noch 8.4 als Ziel
vendor/bin/phpcs -ps --standard=PHPCompatibility --runtime-set testVersion 8.4 \
--ignore='*/vendor/*,*/node_modules/*' .
Die Versionsangabe oben ist die, die das README von PHPCompatibility am 22. Oktober 2026 genannt hat. Die aktuelle finden Sie im README. PHPCompatibility 10 braucht PHP_CodeSniffer 4.0.2 oder neuer.
Rector kann einen Teil der Fixes automatisch anwenden. Sehen Sie sich den Diff an, bevor Sie ihn committen.
composer require rector/rector --dev
vendor/bin/rector # bietet an, rector.php anzulegen
vendor/bin/rector process --dry-run # Vorschau
vendor/bin/rector process # anwenden
<?php
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withPhpSets(); // liest die Ziel-PHP-Version aus composer.json
Keines der beiden Tools ersetzt Tests auf Staging. Sie lesen Code und führen ihn nie aus. Lassen Sie Plugin- und Theme-Code, der nicht Ihnen gehört, in Ruhe und aktualisieren Sie ihn stattdessen. Für WordPress siehe PHP-Version ändern.
Zuletzt geprüft am 22. Oktober 2026.