Zum Hauptinhalt springen
RefoundRefound
Alle Artikel
migrationphplegacy systemsarchitecture

Wie Sie ohne Ausfallzeiten von PHP 5.6 migrieren

Niclas Kusenbach

PHP 5.6 hat das End-of-Life im Dezember 2018 erreicht. Das war vor über sieben Jahren. Wenn Ihr Produktionssystem noch darauf läuft, ist jede ungepatchte CVE seitdem eine offene Tür. Jedes Deployment ist ein Glücksspiel. Und mit jedem Monat, in dem Sie warten, wird die Migration schwieriger — denn der Abstand zwischen PHP 5.6 und PHP 8.3 ist nicht inkrementell. Er ist architektonischer Natur.

Aber das wissen Sie bereits. Die Frage ist nicht, ob Sie migrieren sollen. Die Frage ist, wie Sie es tun, ohne das System offline zu nehmen.

Warum "Einfach PHP updaten" nicht funktioniert

Der Abstand zwischen PHP 5.6 und modernem PHP (8.x) ist kein einfaches Versions-Update. Es handelt sich um eine Sprachentwicklung, die die Abwärtskompatibilität grundlegend bricht:

  • Entfernte Funktionen. mysql_* Funktionen wurden in PHP 7.0 entfernt. Wenn Ihre Codebase diese verwendet — und die meisten PHP-Anwendungen von vor 2015 tun das —, wird jeder Datenbankaufruf beim Upgrade fehlschlagen.
  • Strenge Typisierung (Strict Typing). PHP 7+ erzwingt eine strengere Typisierung. Code, der sich auf implizite Typumwandlungen (Type Coercion) verlassen hat, liefert nun andere Ergebnisse oder wirft Fehler.
  • Fehlerbehandlung. PHP 7 hat fatale Fehler in Error-Exceptions umgewandelt. Es kann sein, dass Ihr Code zur Fehlerbehandlung nun gar nichts mehr abfängt.
  • Namespace und Autoloading-Änderungen. PSR-4 Autoloading hat die manuellen require_once-Ketten ersetzt, auf die ältere Anwendungen angewiesen sind.

Das Ausführen von apt-get install php8.3 bei einer PHP 5.6 Codebase wird zu einer Wand von fatalen Fehlern führen. Das ist keine Migration — das ist ein Crash.

Der inkrementelle Ansatz: Strangler Fig Migration

Der einzige Ansatz, der bei Produktionssystemen zuverlässig funktioniert, ist die inkrementelle Migration. Sie schreiben die Anwendung nicht komplett neu. Sie ersetzen sie, Stück für Stück, während sie live bleibt.

Schritt 1: Duale Laufzeitumgebung einrichten

Betreiben Sie PHP 5.6 und PHP 8.3 Seite an Seite auf demselben Server oder in parallelen Containern. Verwenden Sie Ihren Webserver (nginx oder Apache), um Anfragen basierend auf dem URL-Pfad an die eine oder andere Version weiterzuleiten.

# Neue Module an PHP 8.3 leiten, alles andere an PHP 5.6
location /api/v2/ {
    fastcgi_pass php83-fpm:9000;
    include fastcgi_params;
}

location / {
    fastcgi_pass php56-fpm:9000;
    include fastcgi_params;
}

Das ist das Fundament. Beide Laufzeiten greifen auf dieselbe Datenbank, denselben Session-Store und dasselbe Dateisystem zu. Der Benutzer weiß nicht, welche PHP-Version seine Antwort generiert hat.

Schritt 2: Die erste "Naht" (Seam) finden

Eine "Naht" ist ein Teil der Funktionalität, den Sie aus der alten Codebase extrahieren und in der neuen neu implementieren können, ohne etwas anderes zu ändern. Gute erste Nähte:

  • Ein Nur-Lese-API-Endpunkt (Read-only). Keine Schreibvorgänge bedeuten kein Risiko einer Datenbeschädigung während des Übergangs.
  • Ein Reporting-Modul. Meist in sich geschlossen, hohes Leseaufkommen, geringes Schreibaufkommen.
  • Authentifizierung. Wenn Sie planen, auf ein Framework wie Laravel umzusteigen, beginnen Sie mit der Authentifizierung — sie berührt alles, und eine frühzeitige Modernisierung macht jede nachfolgende Migration einfacher.

Schritt 3: Die Naht im modernen PHP umschreiben

Bauen Sie den Ersatz mit einem modernen Framework (Laravel, Symfony oder einfach reines PHP 8 mit Composer-Abhängigkeiten). Schreiben Sie Tests. Richten Sie CI ein. Dies ist Ihre Chance, es richtig zu machen.

// Alt: PHP 5.6, kein Framework, rohes SQL
$result = mysql_query("SELECT * FROM orders WHERE customer_id = " . $_GET['id']);

// Neu: PHP 8.3, Laravel, Eloquent ORM
$orders = Order::where('customer_id', $request->input('id'))->get();

Schritt 4: Traffic schrittweise umleiten

Beginnen Sie mit 0 % des Traffics, der an die neue Implementierung geht. Verwenden Sie Feature Flags oder prozentuales Routing, um den Traffic langsam zu verschieben: 1 %, 5 %, 25 %, 50 %, 100 %.

Überwachen Sie bei jedem Schritt Fehlerraten, Antwortzeiten und Datenkonsistenz. Wenn etwas bricht, machen Sie einen Rollback, indem Sie wieder 100 % auf den alten Pfad leiten.

Schritt 5: Den alten Code löschen

Sobald die neue Implementierung 100 % des Traffics für einen vereinbarten Zeitraum ohne Probleme verarbeitet hat (wir verwenden typischerweise zwei Wochen), löschen Sie den alten Code. Kommentieren Sie ihn nicht aus. Verschieben Sie ihn nicht in ein legacy/-Verzeichnis. Löschen Sie ihn.

Umgang mit der Datenbank

Datenbankmigrationen sind der schwierigste Teil jeder PHP-Migration. Alter und neuer Code müssen während der Übergangszeit Daten teilen.

Option 1: Geteilte Datenbank. Beide PHP-Versionen lesen und schreiben in dieselbe Datenbank. Der neue Code nutzt das bestehende Schema. Das ist am einfachsten, bedeutet aber, dass Ihr neuer Code die Probleme des alten Schemas übernimmt.

Option 2: Schema-Evolution. Fügen Sie neue Spalten oder Tabellen neben den alten hinzu. Der neue Code schreibt während des Übergangs sowohl in alte als auch in neue Spalten. Nach Abschluss der Migration löschen Sie die alten Spalten.

-- Neue Spalte neben der alten hinzufügen
ALTER TABLE users ADD COLUMN email_verified_at TIMESTAMP NULL;

-- Neuer Code schreibt während des Übergangs in beide
UPDATE users SET email_verified = 1, email_verified_at = NOW() WHERE id = ?;

-- Nach der Migration: alte Spalte löschen
ALTER TABLE users DROP COLUMN email_verified;

Option 3: Datenbank-Proxy. Verwenden Sie ein Tool wie ProxySQL oder PgBouncer, um die Datenbankschicht zu abstrahieren. Beide PHP-Versionen verbinden sich über den Proxy, und Sie können Connection Pooling und Failover zentral verwalten.

Wie das in der Praxis aussieht

Wir haben das Auftragsverwaltungssystem eines Logistikunternehmens über 14 Wochen von PHP 5.6 auf PHP 8.3 migriert. Das System verarbeitete 3.000 Bestellungen pro Tag und durfte keine Ausfallzeiten haben.

Der Ablauf:

  1. Woche 1–2: Duale Laufzeit eingerichtet. PHP 8.3-Container neben PHP 5.6 bereitgestellt.
  2. Woche 3–5: Tracking-API migriert (leselastig, klar definierte Grenze).
  3. Woche 6–8: Workflow zur Auftragserstellung migriert (schreiblastig, erforderte Schema-Evolution).
  4. Woche 9–11: Reporting-Modul und Admin-Oberfläche migriert.
  5. Woche 12–14: Authentifizierung und Session-Management migriert. PHP 5.6-Container außer Betrieb genommen.

Null Minuten Ausfallzeit. Das System war live und verarbeitete an jedem Tag dieser 14 Wochen Bestellungen.

Häufige Fehler, die es zu vermeiden gilt

1. Zu versuchen, alles auf einmal zu migrieren. Das ist ein Big-Bang-Rewrite in Verkleidung. Es wird länger dauern, mehr kosten und das Risiko einer fehlgeschlagenen Umstellung bergen.

2. Tests überspringen. Wenn Sie migrieren, ohne Tests für den neuen Code zu schreiben, verlagern Sie nur technische Schulden auf eine neue Laufzeitumgebung. Schreiben Sie parallel zu jedem migrierten Modul Tests.

3. Den alten Code "für alle Fälle" behalten. Toter Code ist keine Versicherung. Er ist ein Wartungsaufwand. Löschen Sie ihn, wenn sich der neue Code bewährt hat.

4. Die Session-Schicht ignorieren. PHP 5.6 und PHP 8.3 serialisieren Sessions unterschiedlich. Wenn sich beide Laufzeiten Sessions teilen (was während der Migration der Fall sein sollte), benötigen Sie einen Session-Store, den beide lesen können — Redis oder datenbankgestützte Sessions eignen sich gut.


Wenn Ihr Unternehmen auf PHP 5.6 oder 7.x läuft und Sie einen Migrationsplan benötigen, der die Uptime Ihrer Produktion nicht gefährdet, ist ein System-Audit unser Startpunkt — wir bilden die Codebase ab, identifizieren die Nähte und entwerfen eine Migrationssequenz, die Ihr System durchgehend live hält.