Refactoring von Legacy-Code in PHP: Von A bis Z
\n\nDie Arbeit mit veraltetem (Legacy) PHP-Code ist für viele Entwickler eine unvermeidliche Realität. Vor 5-10 Jahren geschriebene Projekte sind oft "Spaghetti-Code" mit globalen Variablen, Vermischung von Geschäftslogik und Darstellung, fehlender Typisierung und veralteten Funktionen (mysql_*, ereg usw.). In diesem Artikel analysieren wir einen schrittweisen Ansatz zum Refactoring von Legacy-PHP-Code, der es ermöglicht, das Projekt zu verbessern, ohne seinen Betrieb zu unterbrechen.
\n\nWarum ist das Refactoring von Legacy-Code schwierig?
\n\nDas Hauptproblem von Legacy-Code ist das Fehlen von Tests. Sie können nicht sicher sein, dass Änderungen die vorhandene Funktionalität nicht beeinträchtigen. Zusätzliche Herausforderungen:
\n\n- \n
- Fehlende Dokumentation — die Arbeitslogik ist oft unklar \n
- Vermischung von Ebenen — SQL-Abfragen innerhalb von HTML-Vorlagen \n
- Globaler Zustand — Variablen, die von jeder Stelle im Code aus zugänglich sind \n
- Veraltete Muster — Verwendung von register_globals, mysql_*-Funktionen \n
- Starre Abhängigkeiten — direkte Objekterstellung mit
newinnerhalb von Klassen \n
Vorbereitung auf das Refactoring: Erstellen eines "Sicherheitsnetzes"
\n\nBevor Sie den Code ändern, müssen Sie sicherstellen, dass Sie Fehler abfangen können. Hier sind die obligatorischen Schritte:
\n\n1. Hinzufügen von Charakterisierungstests
\n\nCharakterisierungstests (characterization tests) erfassen das aktuelle Verhalten des Codes. Sie werden geschrieben, nachdem der Code geschrieben wurde, und ermöglichen es sicherzustellen, dass das Refactoring das Verhalten nicht geändert hat.
\n\n<?php\n// Beispiel eines Charakterisierungstests für eine Legacy-Funktion\nclass LegacyCalculatorTest extends PHPUnit\\Framework\\TestCase\n{\n public function testCalculateTotal()\n {\n // Aktuelles Verhalten erfassen\n $result = legacy_calculate_total(100, 0.2);\n $this->assertEquals(120, $result);\n }\n}\n\n\n2. Erstellen einer "Testdecke"
\n\nBeginnen Sie mit Integrationstests, die wichtige Benutzerszenarien überprüfen (Registrierung, Bestellabschluss usw.). Dies gibt ein grundlegendes Vertrauen beim Refactoring.
\n\nSchrittweise Strategie zum Refactoring von Legacy-PHP-Code
\n\nWir verwenden den Ansatz "Strangler Fig Pattern" (Würgefeigen-Muster) — ersetzen Sie alte Komponenten schrittweise durch neue, ohne den Betrieb des Systems zu unterbrechen.
\n\nSchritt 1: Isolieren und kapseln Sie den globalen Zustand
\n\nGlobale Variablen und Funktionen sind der Hauptfeind des Refactorings. Beginnen Sie mit deren Kapselung:
\n\n<?php\n// VORHER: globale Funktion und Variable\n$db = mysql_connect('localhost', 'user', 'pass');\nfunction get_user($id) {\n global $db;\n $result = mysql_query("SELECT * FROM users WHERE id = $id", $db);\n return mysql_fetch_assoc($result);\n}\n\n// NACHHER: Klasse mit expliziter Abhängigkeit\nclass UserRepository {\n private PDO $pdo;\n \n public function __construct(PDO $pdo) {\n $this->pdo = $pdo;\n }\n \n public function findById(int $id): ?array {\n $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');\n $stmt->execute([$id]);\n $user = $stmt->fetch();\n return $user ?: null;\n }\n}\n\n\nSchritt 2: Trennen Sie Logik von der Darstellung
\n\nTypischer Legacy-Code enthält SQL- und PHP-Logik direkt in HTML. Verwenden Sie das "Template Method"-Muster zur Trennung:
\n\n<?php\n// VORHER: Spaghetti-Code in der Vorlage\n?>\n<ul>\n<?php\n$result = mysql_query("SELECT * FROM products");\nwhile ($row = mysql_fetch_assoc($result)) {\n echo "<li>" . $row['name'] . " - " . $row['price'] . " Rubel.</li>";\n}\n?>\n</ul>\n\n// NACHHER: Auslagerung der Logik in einen Service\nclass ProductService {\n public function __construct(private ProductRepository $repo) {}\n \n public function getProductList(): array {\n return $this->repo->findAll();\n }\n}\n\n// In der Vorl