Breaking Changes verstehen, Risiken kontrollieren
Wer heute noch produktiv mit PHP 7.4 arbeitet, betreibt eine Version ohne Sicherheitsupdates auf einem Stand, den kaum ein modernes Composer-Paket noch unterstützt. Die Migration von PHP 7 auf PHP 8 ist technisch überschaubar, wenn man Typsystem-Änderungen, entfernte Funktionen und String-Vergleiche kennt, und mit PHPStan sowie Rector systematisch statt improvisiert vorgeht.
Inhaltsverzeichnis
- 1. Warum die Migration jetzt dringend ist
- 2. Breaking Changes im Typsystem
- 3. Named Arguments, Constructor Promotion und Match
- 4. String-Vergleiche und neue String-Funktionen
- 5. Entfernte und veraltete Features
- 6. Der JIT-Compiler realistisch bewertet
- 7. Praxis-Checkliste vor der Migration
- 8. Teststrategie während der Migration
- 9. Schrittweise Migration: 7.4 bis 8.4
- 10. Zusammenfassung
- 11. FAQ
1. Warum die Migration von PHP 7 auf PHP 8.x dringend ist
PHP 7.4 hat sein offizielles End of Life im November 2022 erreicht. Seitdem gibt es keine Sicherheitspatches mehr, auch nicht für kritische Lücken in der Zend Engine oder in Core-Extensions wie OpenSSL-Bindings oder der Session-Verwaltung. Wer PHP 7 auf PHP 8 migrieren muss, tut das nicht aus Neugier auf neue Sprachfeatures, sondern weil jede weitere Woche auf einer unsupporteten Version das Risiko einer nicht behebbaren Sicherheitslücke erhöht. Compliance-Anforderungen wie PCI-DSS setzen für Zahlungsverkehr ohnehin unterstützte Laufzeitversionen voraus, und Cyber-Versicherungen prüfen zunehmend, ob produktive Systeme End-of-Life-Software einsetzen.
Das Ökosystem hat sich ebenfalls verschoben. Aktuelle Major-Releases von Symfony, Laravel und praktisch allen relevanten Composer-Paketen verlangen mindestens PHP 8.1, viele bereits 8.2 oder 8.3. Wer auf PHP 7.4 verharrt, friert damit automatisch auch alle Abhängigkeiten auf veralteten Ständen ein und verliert Zugriff auf Bugfixes, die nur noch in neueren Paketversionen erscheinen. Diese Kopplung zwischen Sprachversion und Abhängigkeitsversion ist der Grund, warum eine PHP-Migration selten isoliert bleibt, sondern fast immer ein größeres Dependency-Update nach sich zieht.
Der dritte Treiber ist Performance. Die Optimierungen an der Zend Engine seit PHP 8.0, kombiniert mit dem JIT-Compiler und verbesserten Speicherstrukturen für Arrays und Objekte, senken CPU-Last und Speicherverbrauch spürbar, ganz ohne Codeänderung. In Benchmarks unter realistischer Last zeigen sich bei reinen Interpreter-Optimierungen zwischen PHP 7.4 und PHP 8.3 typischerweise zweistellige Prozentwerte an eingesparter Rechenzeit. Für Teams, die Server-Ressourcen nach Auslastung abrechnen oder skalieren, ist das ein direkter Kostenfaktor, der die PHP-Migration wirtschaftlich rechtfertigt, unabhängig von der Sicherheitsfrage.
2. Breaking Changes im Typsystem: TypeError statt Warning
Der wichtigste konzeptionelle Bruch bei der Migration von PHP 7 auf PHP 8 betrifft den Umgang mit Typfehlern. In PHP 7 haben viele interne Funktionen bei falschem Argumenttyp lediglich eine E_WARNING ausgegeben und danach mit null oder false weitergemacht. Das Skript lief weiter, oft mit einem stillen Folgefehler an einer ganz anderen Stelle. PHP 8 wirft in denselben Situationen ein TypeError, das den Ausführungspfad sofort unterbricht, sofern es nicht abgefangen wird. Das ist strenger, aber ehrlicher: Ein Fehler wird dort sichtbar, wo er entsteht, statt sich unbemerkt durch den Aufrufstack zu propagieren.
Zusätzlich wurde die Exception-Hierarchie in PHP 7 bereits umgebaut, sodass Fehler wie Division durch Null oder Aufrufe auf nicht existierende Methoden als Error-Objekte geworfen werden, die das gemeinsame Throwable-Interface implementieren. PHP 8 baut darauf konsequent auf: Argumentanzahl-Fehler, Zugriffe auf undefinierte Konstanten und falsche Typen bei internen Funktionen erzeugen jetzt durchgängig TypeError oder ArgumentCountError. Wer bestehenden Code auf PHP 8 hebt, muss also nicht nur neue Fehlermeldungen erwarten, sondern in kritischen Codepfaden gezielt try/catch-Blöcke für TypeError ergänzen, wo vorher ein stiller Fallback ausreichte.
Praktisch zeigt sich das am häufigsten bei String-Funktionen, die mit null aufgerufen werden. Ein Aufruf wie strlen($value), bei dem $value aus einer Datenbankabfrage null zurückgibt, produzierte in PHP 7.4 nur eine Deprecation-Warnung. Seit PHP 8.1 ist die Übergabe von null an nicht-nullable interne Parameter deprecated, und in vielen Fällen wird daraus in strikten Kontexten ein harter Fehler. Das folgende Beispiel zeigt den Unterschied im Verhalten konkret.
<?php
declare(strict_types=1);
// PHP 7.4 behavior (conceptual): calling with a wrong type on an
// internal function only triggered a warning and returned null/false.
// The script continued running with a silently broken value.
function legacyBehaviorExample(?string $raw): int
{
// In PHP 7.4 this would emit E_WARNING and return 0 on failure,
// masking the real bug further down the call stack.
return (int) $raw;
}
// PHP 8.x behavior: passing an incompatible type into a strictly
// typed function now throws a TypeError instead of warning silently.
function strictBehaviorExample(int $count): string
{
return str_repeat('*', $count);
}
try {
// Passing a non-numeric string throws TypeError under strict_types
echo strictBehaviorExample('not-a-number');
} catch (TypeError $e) {
// The error surfaces immediately at the call site, not later
error_log('TypeError caught during migration check: ' . $e->getMessage());
}
// Array access on a non-array value: PHP 7.4 warned and returned null,
// PHP 8 still returns null for simple offset access, but function calls
// on that value now throw instead of silently returning false/null.
$config = null;
$timeout = $config['timeout'] ?? 30; // still safe with null coalescing
3. Named Arguments, Constructor Promotion und die Match-Expression als neue Möglichkeiten
Neben den Breaking Changes bringt PHP 8 auch Sprachfeatures, die Code während der PHP-Migration gleichzeitig lesbarer machen. Named Arguments erlauben, Funktionsparameter beim Aufruf über ihren Namen statt über ihre Position zu adressieren. Das ist besonders bei Funktionen mit vielen optionalen Parametern wertvoll, weil man nur die tatsächlich benötigten Werte angibt, ohne vorherige Parameter mit ihren Default-Werten wiederholen zu müssen. In PHP 7 musste man dafür entweder ein Options-Array übergeben oder alle Zwischenparameter explizit mit ihrem Standardwert ausschreiben.
Constructor Property Promotion reduziert den typischen Boilerplate-Code von Value-Objects und Datenklassen drastisch. Statt Property-Deklaration, Konstruktor-Parameter und Zuweisung im Konstruktorkörper separat zu schreiben, deklariert man die Property direkt in der Parameterliste des Konstruktors. Das spart in typischen Domain-Objekten mit fünf bis zehn Properties zwischen 30 und 50 Zeilen Code, ohne dass sich das Verhalten ändert. Die Match-Expression schließlich ersetzt viele switch-Konstrukte durch eine Ausdrucksform mit striktem Typvergleich (=== statt ==) und ohne Fallthrough-Gefahr, was eine ganze Klasse klassischer switch-Bugs eliminiert.
<?php
declare(strict_types=1);
// PHP 7.4 style: verbose constructor, positional arguments, switch statement
final class LegacyOrderPhp7
{
private string $status;
private int $priority;
private bool $express;
public function __construct(string $status, int $priority, bool $express)
{
$this->status = $status;
$this->priority = $priority;
$this->express = $express;
}
public function shippingLabel(): string
{
switch ($this->status) {
case 'pending':
$label = 'Wartet auf Zahlung';
break;
case 'paid':
$label = 'Bereit zum Versand';
break;
case 'shipped':
$label = 'Unterwegs';
break;
default:
$label = 'Unbekannt';
}
return $label;
}
}
$legacy = new LegacyOrderPhp7(status: 'paid', priority: 1, express: true);
// PHP 8.x style: constructor property promotion, named arguments, match
final class ModernOrderPhp8
{
public function __construct(
private readonly string $status,
private readonly int $priority = 1,
private readonly bool $express = false,
) {
}
public function shippingLabel(): string
{
// match is an expression, uses strict comparison, no fallthrough
return match ($this->status) {
'pending' => 'Wartet auf Zahlung',
'paid' => 'Bereit zum Versand',
'shipped' => 'Unterwegs',
default => 'Unbekannt',
};
}
}
// Named arguments: only override what differs from the default
$modern = new ModernOrderPhp8(status: 'paid', express: true);
4. String-zu-Zahl-Vergleiche und neue String-Funktionen
Eine der subtilsten, aber folgenreichsten Änderungen bei der Migration von PHP 7 auf PHP 8 betrifft den lockeren Vergleich (==) zwischen Zahlen und nicht-numerischen Strings. In PHP 7 wurde bei 0 == "foo" der String zuerst in eine Zahl konvertiert, was 0 ergab, wodurch der Vergleich zu true auswertete. Das führte in der Praxis regelmäßig zu Sicherheitslücken, etwa wenn Passwort-Hashes im Format "0e123..." (sogenannte Magic Hashes) fälschlich als numerisch gleich 0 interpretiert wurden. Seit PHP 8.0 wird bei einem Vergleich zwischen Zahl und String die Zahl in einen String umgewandelt und dann als String verglichen, wenn der String nicht numerisch ist. Damit ergibt 0 == "foo" in PHP 8 korrekt false.
Diese Änderung betrifft überraschend viele Codepfade, die sich auf implizites Type-Juggling verlassen haben, etwa Vergleiche von Datenbank-IDs, die als String aus einer API kommen, gegen Integer-Konstanten. Wer bei der PHP-Migration systematisch nach ==-Vergleichen mit gemischten Typen sucht und diese auf === oder explizite Type-Casts umstellt, eliminiert eine ganze Fehlerklasse dauerhaft, unabhängig vom PHP-Verhalten.
Parallel dazu bringt PHP 8 mit str_contains(), str_starts_with() und str_ends_with() drei lange vermisste String-Funktionen nativ mit. Sie ersetzen das fehleranfällige Muster strpos($haystack, $needle) !== false, bei dem ein vergessener strikter Vergleich (!== statt !=) dazu führt, dass ein Treffer an Position 0 fälschlich als "nicht gefunden" behandelt wird, weil 0 == false in PHP 7 true ergab. Die neuen Funktionen geben einen echten Boolean zurück und machen diese Fehlerquelle strukturell unmöglich.
<?php
declare(strict_types=1);
// Numeric string comparison: PHP 7 vs PHP 8 behavior
var_dump(0 == 'foo'); // PHP 7.4: true (string cast to 0) | PHP 8.x: false
var_dump('1' == '01'); // both versions: true (both numeric strings)
var_dump('10' == '1e1'); // both versions: true (both numeric strings)
var_dump(100 == '1e2'); // both versions: true (numeric string, compared as number)
// The classic PHP 7 security trap: "magic hash" style comparisons
$storedHash = '0e123456789';
$userInput = '0';
// PHP 7.4: (0 == "0e123456789") could evaluate to true due to scientific
// notation parsing, a known source of authentication bypass bugs.
// PHP 8.x: non-numeric strings are never silently cast to 0.
// WRONG (PHP 7 era pattern): fragile strpos check
function containsNeedleLegacy(string $haystack, string $needle): bool
{
// Bug-prone: a match at position 0 requires !== false, easy to get wrong
return strpos($haystack, $needle) !== false;
}
// RIGHT (PHP 8): explicit, readable, no off-by-zero trap possible
function containsNeedleModern(string $haystack, string $needle): bool
{
return str_contains($haystack, $needle);
}
$path = '/var/www/html/index.php';
var_dump(str_starts_with($path, '/var/www')); // true
var_dump(str_ends_with($path, '.php')); // true
var_dump(str_contains($path, 'html')); // true
5. Entfernte und veraltete Features: create_function(), each() und mehr
PHP 8.0 räumt mit einer Reihe von Funktionen auf, die schon in PHP 7 als deprecated markiert waren, aber noch aufgerufen werden konnten. create_function(), das dynamisch Funktionscode aus einem String via eval() erzeugte, wurde vollständig entfernt. Es war ohnehin ein Sicherheitsrisiko, weil es String-Konkatenation mit Code-Ausführung vermischte, und wird seit Jahren durch Closures und Arrow Functions ersetzt. Ebenso entfernt wurde each(), das den internen Array-Zeiger manuell weiterbewegte und paarweise Key-Value-Tupel zurückgab, ein Muster, das durch foreach vollständig abgedeckt ist und in modernem Code keine Berechtigung mehr hat.
Der geschweifte-Klammer-Zugriff auf String-Offsets ($string{0} statt $string[0]) wurde ebenfalls entfernt, nachdem er seit PHP 7.4 deprecated war. Wer diese Syntax noch im Code hat, meist aus sehr alten Codebasen übernommen, muss sie vor dem Sprung auf PHP 8 zwingend auf eckige Klammern umstellen, da der Parser sonst einen Fatal Error wirft, keine Warnung. Ein weiterer, oft übersehener Breaking Change betrifft die Sortier-Stabilität: Seit PHP 8.0 sind alle Sortierfunktionen wie sort(), usort() und asort() stabil, das heißt, Elemente mit gleichem Vergleichswert behalten ihre relative Reihenfolge. In PHP 7 war die Reihenfolge bei gleichen Werten implementierungsabhängig und konnte sich zwischen PHP-Patch-Versionen unterscheiden.
Für die PHP-Migration bedeutet das: Ein automatisierter Suchlauf nach create_function(, each( und dem geschweiften Array-Zugriffsmuster im gesamten Repository ist ein Pflichtschritt vor jedem Upgrade-Versuch, da diese drei Muster nicht mit einer Warnung, sondern mit einem Fatal Error abbrechen, sobald der Interpreter auf PHP 8 läuft.
<?php
declare(strict_types=1);
// REMOVED in PHP 8.0: create_function() no longer exists at all
// $callback = create_function('$a, $b', 'return $a + $b;');
// Fatal error: Uncaught Error: Call to undefined function create_function()
// Modern replacement: arrow function or closure
$callback = fn(int $a, int $b): int => $a + $b;
echo $callback(2, 3); // 5
// REMOVED in PHP 8.0: each() no longer exists
// while (list($key, $value) = each($array)) { ... }
// Fatal error: Uncaught Error: Call to undefined function each()
// Modern replacement: foreach handles the same task safely
$array = ['id' => 42, 'name' => 'Widget', 'active' => true];
foreach ($array as $key => $value) {
// process each key/value pair without manual pointer management
echo sprintf("%s => %s\n", $key, var_export($value, true));
}
// REMOVED in PHP 8.0: curly brace string offset access
// $first = $someString{0};
// Fatal error: Uncaught Error: Cannot use '{}' for indexing
// Modern replacement: square bracket offset access
$someString = 'PHP 8 migration';
$first = $someString[0]; // 'P'
6. Der JIT-Compiler: Performance-Unterschiede realistisch bewertet
Der seit PHP 8.0 verfügbare JIT-Compiler (Just-In-Time) übersetzt häufig durchlaufene Opcode-Pfade zur Laufzeit in nativen Maschinencode und umgeht damit den klassischen Interpreter-Overhead. In Marketing-Material wird der JIT oft pauschal als Performance-Wunder dargestellt, in der Praxis hängt der Nutzen aber vollständig vom Workload ab. Bei CPU-gebundenen Aufgaben wie mathematischen Berechnungen, Bildverarbeitung, Kompressionsalgorithmen oder dem Parsen sehr großer Datenstrukturen zeigt der JIT messbare Laufzeitgewinne, in Benchmarks teilweise im Bereich von 20 bis 40 Prozent gegenüber reinem Interpreter-Betrieb.
Bei einer typischen, datenbanklastigen Webanwendung, wie sie die meisten Shop- und CMS-Systeme darstellen, ist der Effekt dagegen gering bis kaum messbar. Der Grund liegt darin, dass die Ausführungszeit einer HTTP-Anfrage überwiegend aus I/O-Wartezeit besteht: Datenbankabfragen, externe API-Aufrufe, Dateisystemzugriffe und Netzwerklatenz dominieren die Gesamtzeit, während der eigentliche PHP-Code, den der JIT beschleunigen könnte, nur einen kleinen Bruchteil der Anfrage ausmacht. Ein JIT-kompilierter Loop, der zwei Mikrosekunden statt fünf Mikrosekunden braucht, verschwindet in der Wahrnehmung neben einer 50-Millisekunden-Datenbankabfrage vollständig.
Für die PHP-Migration bedeutet das: Der JIT ist kein Argument, das man isoliert für den Umstieg anführen sollte, wenn die Anwendung überwiegend I/O-gebunden ist. Sinnvoll ist es, den JIT im Modus tracing mit opcache.jit=1255 zu aktivieren und testen, aber die Erwartungshaltung anhand des tatsächlichen Workload-Profils zu kalibrieren, nicht anhand pauschaler Benchmark-Zahlen aus Rechenintensiven Microbenchmarks, die mit dem eigenen Anwendungsfall wenig zu tun haben.
7. Praxis-Checkliste vor der Migration: Composer, PHPStan und Rector
Bevor ein Team mit der eigentlichen PHP-Migration beginnt, lohnt sich ein systematischer Vorlauf, statt einfach die PHP-Version im Server auszutauschen und zu beobachten, was bricht. Der erste Schritt ist die Prüfung aller Composer-Abhängigkeiten auf ihre PHP-8-Kompatibilität. Mit composer outdated --direct und einem Blick in die require-Sektion jedes Pakets lässt sich klären, welche Bibliotheken bereits PHP-8-kompatible Major-Versionen anbieten und welche ein größeres Update benötigen, bevor der Sprung überhaupt möglich ist.
Der zweite Schritt ist statische Analyse mit PHPStan. Ein Level-5- oder Level-6-Durchlauf gegen die bestehende Codebasis deckt Typinkonsistenzen, potenzielle TypeError-Quellen und tote Codepfade auf, lange bevor ein Nutzer in Produktion auf einen Fehler trifft. PHPStan-Regelsets für PHP-Versionskompatibilität, etwa über phpstan/phpstan-deprecation-rules, markieren zusätzlich jede Verwendung einer deprecated oder entfernten Funktion explizit im Report.
Der dritte Schritt ist automatisiertes Refactoring mit Rector. Rector bringt fertige Regelsets für jeden PHP-Versionssprung mit, etwa Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_81, die Codemuster wie fehlende Property-Typen, veraltete Array-Funktionsaufrufe oder Nullable-Parameter automatisch auf die moderne Schreibweise umstellen. Der --dry-run-Modus zeigt vor jeder Änderung einen vollständigen Diff, sodass das Team die vorgeschlagenen Transformationen review-en kann, bevor sie angewendet werden.
#!/usr/bin/env bash
# Migration pipeline: dependency check, static analysis, automated refactoring
set -euo pipefail
# 1. Check which Composer dependencies are not yet PHP 8 compatible
composer outdated --direct --format=json > outdated-report.json
# 2. Install PHPStan with deprecation rules for the migration audit
composer require --dev phpstan/phpstan phpstan/phpstan-deprecation-rules
# Run static analysis to surface TypeError risks and deprecated calls
vendor/bin/phpstan analyse src --level=6 --error-format=table
# 3. Install Rector and run the PHP 8.1 upgrade set in dry-run mode first
composer require --dev rector/rector
# Dry-run shows a full diff of proposed changes without touching files
vendor/bin/rector process src --dry-run --config=rector.php
# Once the diff has been reviewed, apply the changes for real
vendor/bin/rector process src --config=rector.php
# Re-run PHPStan after Rector to confirm no new type errors were introduced
vendor/bin/phpstan analyse src --level=6
8. Teststrategie während der Migration: Testsuite, Feature-Flags und Canary-Deployments
Eine belastbare Testsuite ist die Voraussetzung dafür, dass eine PHP-Migration nicht zur Vertrauensfrage wird. Wer keine oder nur eine dünne Unit-Test-Abdeckung hat, sollte vor dem eigentlichen Versionswechsel gezielt Tests für die kritischsten Geschäftslogik-Pfade nachziehen, insbesondere für Bereiche, die von den in diesem Artikel beschriebenen Breaking Changes betroffen sind: lockere Typvergleiche, String-Funktionen und Sortierlogik. Diese Tests laufen idealerweise sowohl gegen die alte als auch gegen die neue PHP-Version in der CI-Pipeline parallel, sodass Verhaltensunterschiede sofort sichtbar werden, statt erst in Produktion aufzufallen.
Feature-Flags helfen, die Migration in kontrollierbare Einheiten zu zerlegen. Statt die gesamte Anwendung an einem Stichtag auf PHP 8 umzustellen, lässt sich etwa der Applikationsserver-Pool aufteilen: Ein Teil der Instanzen läuft testweise bereits auf PHP 8.x, während der Großteil des Traffics noch auf PHP 7.4 verbleibt. Bei kritischen Fehlern kann der Traffic-Anteil auf der neuen Version sofort auf null reduziert werden, ohne einen vollständigen Rollback der Codebasis durchführen zu müssen.
Dieses Canary-Deployment-Muster, kombiniert mit engmaschigem Error-Monitoring über Tools wie Sentry oder New Relic, macht sichtbar, ob die neue PHP-Version zusätzliche Exceptions, insbesondere TypeError und ArgumentCountError, in Produktion erzeugt, die in Staging nicht aufgefallen sind. Erst wenn die Fehlerrate auf der Canary-Gruppe über einen definierten Zeitraum stabil bleibt, wird der Traffic-Anteil schrittweise erhöht, bis die gesamte Flotte auf der neuen Version läuft.
9. Schrittweise Migration: von 7.4 über 8.0, 8.1, 8.2, 8.3 bis 8.4
Der direkte Sprung von PHP 7.4 auf PHP 8.4 in einem einzigen Schritt ist theoretisch möglich, in der Praxis aber deutlich riskanter als eine schrittweise PHP-Migration über die Zwischenversionen. Jede Minor-Version zwischen 8.0 und 8.4 bringt eigene Breaking Changes und Deprecations mit, die sich bei einem Direktsprung alle gleichzeitig manifestieren und in der Fehlersuche kaum noch voneinander zu trennen sind. Ein Fehler, der durch die Sortier-Stabilität in 8.0 entsteht, lässt sich in einem Big-Bang-Upgrade auf 8.4 nur schwer von einem Fehler unterscheiden, der durch geänderte null-Parameter-Handling in 8.1 verursacht wird.
Der empfohlene Weg führt über 8.0 als ersten Zwischenschritt, weil hier die größten strukturellen Änderungen liegen: Union Types, das neue Typsystem-Verhalten, die numerischen String-Vergleiche und die entfernten Funktionen. Steht dieser Schritt stabil in Produktion, folgt 8.1 mit Enums, Readonly Properties und der Deprecation von impliziten Nullable-Parametern. Danach 8.2 mit Readonly-Classes und weiteren Deprecations, 8.3 mit typisierten Klassenkonstanten, und schließlich 8.4 mit Property Hooks und der asymmetrischen Sichtbarkeit. Jeder dieser Schritte lässt sich einzeln in Staging validieren, mit einer überschaubaren Diff-Größe an Breaking Changes.
Für Teams mit begrenzter Kapazität ist ein pragmatischer Kompromiss, mindestens zwei Zwischenstopps einzulegen, etwa 7.4 auf 8.1 und danach 8.1 auf 8.4, statt vier oder fünf einzelne Schritte zu fahren. Wichtig ist in jedem Fall, dass PHPStan und Rector bei jedem Zwischenschritt erneut laufen, weil neue Deprecations in der jeweils nächsten Version erst durch die aktualisierten Regelsets sichtbar werden.
Die folgende Tabelle zeigt, wie sich zentrale Sprachfeatures über die relevanten Versionen entwickelt haben und ab welchem Zwischenschritt sie jeweils zur Verfügung stehen. Sie hilft dabei, den eigenen Migrationsplan an konkreten Feature-Meilensteinen statt an abstrakten Versionsnummern auszurichten.
| Feature | PHP 7.4 | PHP 8.0 | PHP 8.4 |
|---|---|---|---|
| Named Arguments | nicht verfügbar | verfügbar | verfügbar |
| Constructor Property Promotion | nicht verfügbar | verfügbar | verfügbar |
| Match-Expression | nicht verfügbar | verfügbar | verfügbar |
| Enums | nicht verfügbar | nicht verfügbar | verfügbar (seit 8.1) |
| Readonly Properties | nicht verfügbar | nicht verfügbar | verfügbar (seit 8.1) |
| JIT-Compiler | nicht verfügbar | verfügbar | verfügbar, optimiert |
| Nullsafe-Operator | nicht verfügbar | verfügbar | verfügbar |
| str_contains() | nicht verfügbar | verfügbar | verfügbar |
10. Zusammenfassung
Die Migration von PHP 7 auf PHP 8.x ist kein rein technisches Detail-Update, sondern eine Kombination aus Sicherheitsnotwendigkeit, Ökosystem-Zwang und einer überschaubaren, aber ernstzunehmenden Liste von Breaking Changes. Wer PHP 7 auf PHP 8 migrieren will, sollte das Typsystem-Verhalten, die numerischen String-Vergleiche, entfernte Funktionen wie create_function() und each() sowie die Sortier-Stabilität als konkrete, prüfbare Punkte auf einer Checkliste behandeln, nicht als abstrakte Randnotiz.
Mit PHPStan zur statischen Analyse, Rector für automatisiertes Refactoring, einer belastbaren Testsuite und einer schrittweisen Version-für-Version-Strategie über 8.0, 8.1, 8.2 und 8.3 lässt sich das Risiko einer PHP-Migration auf ein kontrollierbares Maß reduzieren. Der Aufwand steht in keinem Verhältnis zu den Kosten, die ein produktiver Betrieb auf einer unsupporteten, sicherheitslückenbehafteten PHP-7-Instanz langfristig verursacht.
PHP 7 auf PHP 8 migrieren - Das Wichtigste auf einen Blick
Typsystem
Interne Funktionen werfen jetzt TypeError statt nur zu warnen. Try/Catch-Blöcke in kritischen Codepfaden ergänzen.
String-Vergleiche
0 == "foo" ist seit PHP 8 false. Alle gemischten ==-Vergleiche auf === prüfen.
Entfernte Funktionen
create_function(), each() und geschweifter Array-Zugriff existieren nicht mehr. Vorab per Suche prüfen.
Werkzeuge
PHPStan für statische Analyse, Rector für automatisiertes Refactoring, schrittweise über 8.0 bis 8.4 migrieren.
11. FAQ: PHP 7 auf PHP 8 migrieren
1Wie lange ist PHP 7.4 schon ohne Sicherheitsupdates?
2Wichtigster Unterschied im Typsystem?
3Warum 0 == "foo" jetzt false statt true?
4Direkt von 7.4 auf 8.4 springen?
5Welche Funktionen wurden entfernt?
6Hilft der JIT immer bei Performance?
7Wie hilft PHPStan konkret?
8Was macht Rector bei der Migration?
9Was ist ein sinnvolles Canary-Deployment?
10Müssen Composer-Abhängigkeiten vorab aktualisiert werden?
Mironsoft
PHP-Entwicklung, Legacy-Modernisierung und Magento-Agentur
Noch auf PHP 7 unterwegs und unsicher, wo die Risiken liegen?
Wir analysieren eure Codebasis mit PHPStan und Rector, identifizieren Breaking Changes vor dem Umstieg und begleiten die Migration schrittweise bis zur produktiven PHP-8.4-Umgebung, inklusive Magento-spezifischer Anpassungen.
Kompatibilitäts-Audit
PHPStan-Analyse und Composer-Dependency-Check vor dem Versionswechsel
Automatisiertes Refactoring
Rector-Regelsets pro Versionssprung, mit Review-Diff vor jeder Anwendung
Begleitetes Rollout
Canary-Deployments, Monitoring und schrittweiser Umstieg ohne Produktionsausfall