Ein PHP-Upgrade beweisen, bevor Sie umschalten, mit bereitem Rollback
Eine App sicher auf einen neuen PHP-Branch bringen: parallel installieren, den Code linten und scannen, nachverfolgen, was der Traffic wirklich lädt, dann ein Socket umschalten.

Die meisten PHP-Upgrades scheitern auf dieselbe Weise: Die neue Version wird über die alte installiert, die Startseite lädt, alle entspannen sich, und der erste Administrator, der am nächsten Morgen eine selten genutzte Maske öffnet, bekommt eine weiße Seite. Hier ist die Methode, mit der wir diese Probleme auf der Werkbank finden, vor dem Cutover, mit einem Rückweg, der Sekunden dauert.
Warum Upgrades immer wieder Dinge kaputtmachen
Jeder PHP-Branch entfernt Funktionen, zieht die Typbehandlung an und macht aus alten Warnungen fatale Fehler. Eine Anwendung, die über Jahre gewachsen ist, trägt Codepfade mit sich, die lange niemand mehr ausgeführt hat: einen Export, ein altes Zahlungsmodul, einen Admin-Bericht. Ein Smoke-Test der öffentlichen Seiten wird sie nie erreichen.
Kommerzielle Anwendungen fügen eine zweite Ebene hinzu. Durch einen Encoder geschützte Dateien laufen nur, wenn der Loader für den neuen PHP-Branch das Format unterstützt, mit dem sie kodiert wurden, und das ist eine Eigenschaft jeder einzelnen Datei, nicht der Marketingseite der Anwendung.
Schritt eins: parallel installieren, nie darüber
Installieren Sie das Ziel-PHP neben dem aktuellen. Auf RHEL und seinen Nachbauten machen Remis Software Collections das einfach: Jeder Branch lebt in seinem eigenen Präfix, mit eigenem FPM-Dienst und eigenem Socket. Legen Sie einen Pool für die Site auf der neuen Version an, identisch mit dem alten Pool bis auf das Binary und den Socket-Pfad.
Für Besucher hat sich noch nichts geändert. Beide Versionen laufen, und der Webserver zeigt noch auf das alte Socket.
Schritt zwei: jede Datei mit dem neuen Binary linten
Der günstigste Test ist, das neue PHP zu bitten, jede Datei zu parsen:
NEW_PHP=/path/to/new/php
find /var/www/app -name '*.php' -print0 \
| xargs -0 -n1 -P8 "$NEW_PHP" -l 2>&1 \
| grep -v '^No syntax errors'
Das fängt Syntax ab, die der neue Branch nicht mehr akzeptiert. Es fängt keine entfernten Funktionen oder geändertes Verhalten ab, weil die erst zur Laufzeit scheitern. Dafür führen Sie einen statischen Kompatibilitäts-Scan aus, etwa PHP_CodeSniffer mit dem Standard PHPCompatibility, eingestellt auf Ihren Ziel-Branch:
phpcs -p --standard=PHPCompatibility --runtime-set testVersion <target> \
--extensions=php /var/www/app
Rechnen Sie mit Rauschen aus eingebetteten Bibliotheken und totem Code. Die Liste ist eine Landkarte, kein Urteil.
Schritt drei: herausfinden, welche Dateien der Traffic tatsächlich lädt
Eine Anwendung kann Zehntausende PHP-Dateien enthalten, von denen ein Bruchteil je ausgeführt wird. Sie möchten den Kompatibilitätsaufwand auf diesen Bruchteil verwenden, und Sie möchten sicher sein, dass er die Pfade für angemeldete Benutzer und Administratoren einschließt.
OPcache weiß es bereits. Fragen Sie ihn nach einem normalen Geschäftstag auf der aktuellen Version, mit Leuten, die sich anmelden, und Administratoren, die ihre Arbeit tun, welche Skripte er kompiliert hat:
<?php
// protect this file: admin network only, and delete it afterwards
$s = opcache_get_status(true);
foreach (array_keys($s['scripts']) as $f) { echo $f, "\n"; }
Speichern Sie die Liste, vergleichen Sie sie mit den Lint- und Scan-Ergebnissen, und die Probleme, die tatsächlich auftreten werden, steigen nach oben. Dateien, die im Scan auftauchen, aber nie in der Nachverfolgung, sind Kandidaten fürs Entfernen, nicht fürs Reparieren.
Schritt vier: die Header der kodierten Dateien lesen
Prüfen Sie bei kodierter kommerzieller Software jede Datei, statt einem Kompatibilitätsbanner zu vertrauen. Kodierte Dateien tragen einen Klartext-Header, der Encoder und Format nennt:
grep -rl --include='*.php' 'ionCube Loader' /var/www/app \
| while read -r f; do head -c 400 "$f" | strings | head -3; done | sort | uniq -c
Gruppieren Sie die Dateien nach Encoder-Format und bestätigen Sie dann, dass der Loader für Ihren Ziel-PHP-Branch dieses Format ausführt. Ein Modul, das vor Jahren für einen alten Branch kodiert wurde, ist genau die Datei, die sich auf dem neuen weigern wird zu laden, und es ist besser, das aus einem Header zu erfahren als von einem Kunden.
Schritt fünf: auf dem neuen Pool proben
Richten Sie einen Test-Hostnamen, oder Ihre eigene IP über eine Header-Regel, auf das neue Socket, während die Produktion auf dem alten bleibt. Gehen Sie die Pfade aus der OPcache-Nachverfolgung durch, mit besonderer Aufmerksamkeit für die, die nur angemeldete Benutzer und Administratoren erreichen, und beobachten Sie dabei das Error-Log des neuen Pools:
tail -F /var/log/php-fpm/app-new-error.log | grep -E 'Fatal|Uncaught|Deprecated'
Deprecation-Hinweise sind heute keine Fehler, aber sie sind die Liste für das nächste Upgrade. Schreiben Sie sie auf.
Schritt sechs: eine Zeile umschalten, den alten Pool laufen lassen
Der Cutover ist eine Änderung im Webserver: das FastCGI-Socket.
# fastcgi_pass unix:/run/php-fpm/app-old.sock;
fastcgi_pass unix:/run/php-fpm/app-new.sock;
nginx -t && systemctl reload nginx
Das Rollback ist dieselbe Änderung rückwärts, in Sekunden angewendet, ohne Paketänderungen und ohne Neustart von irgendetwas außer der Webserver-Konfiguration. Stimmen Sie den Zeitpunkt mit dem Site-Betreiber ab, beobachten Sie in der ersten Stunde Fehlerraten und Antwortzeiten, und lassen Sie den alten Pool über eine Burn-in-Phase laufen, die lang genug ist, um wöchentliche und monatliche Jobs abzudecken. Erst dann legen Sie ihn still.
Auf einem Stack mit mehreren Knoten tun Sie das einen Web-Knoten nach dem anderen hinter dem Load Balancer, sodass selbst eine böse Überraschung für ein paar Minuten nur einen Bruchteil des Traffics erreicht.
Schritt sieben: Cron und CLI im Gleichschritt halten
Der Webserver ist nicht das Einzige, was PHP ausführt. Geplante Jobs, Queue-Worker und Kommandozeilenwerkzeuge rufen oft einen fest eingetragenen Binary-Pfad auf. Finden Sie sie:
grep -rE 'php[0-9]*( |$)|/bin/php' /etc/cron* /var/spool/cron /etc/systemd/system 2>/dev/null
Stellen Sie sie im selben Änderungsfenster auf das neue Binary um, oder bewusst danach, aber nie aus Versehen.
Ihre Checkliste
- Neues PHP neben dem alten installiert, mit eigenem Pool und Socket.
- Jede Datei mit dem neuen Binary gelintet, und ein Kompatibilitäts-Scan für den Ziel-Branch.
- Eine OPcache-Nachverfolgung dessen, was echter Traffic lädt, einschließlich der Pfade für angemeldete Benutzer und Administratoren.
- Kodierte Dateien nach Format gruppiert und mit dem neuen Loader abgeglichen.
- Eine Probe auf dem neuen Pool mit offenem Error-Log.
- Ein Cutover aus einer Zeile, der alte Pool bis zum Ende des Burn-in behalten, und Cron bewusst umgestellt.
Wenn Sie ein Upgrade vor sich herschieben, planen wir es gern mit Ihnen.
cta: Mit einem Ingenieur sprechen
Related
- /what-we-do/data-web/php-upgrades-security-patching/
- /what-we-do/data-web/performance-audits/
- /what-we-do/data-web/high-availability-web-tiers/
Veröffentlicht am 15. September 2026.
Alle Notizen aus der Praxis