Refactorización de código legacy en PHP: de la A a la Z
Trabajar con código legacy (heredado) es una parte inevitable de la vida de cualquier desarrollador PHP. Tarde o temprano te enfrentarás a un proyecto escrito en PHP 5.6 (o incluso 4), donde no hay autocarga, se utilizan variables globales y la lógica de negocio está mezclada con HTML. La refactorización de dicho código no es solo un "peinado", sino un proceso estratégico que puede salvar al proyecto de la deuda técnica y hacerlo mantenible.
En este artículo, analizaremos cómo abordar la refactorización de código PHP legacy de forma sistemática, sin romper la funcionalidad existente, y convertir un "pantano" en código moderno y comprobable.
1. Diagnóstico y evaluación: ¿por dónde empezar?
Antes de cambiar el código, debes entender con qué estás lidiando. Comienza con una auditoría del proyecto.
Paso 1. Identifica los "puntos débiles"
- Dependencias: ¿Se utilizan bibliotecas obsoletas? Por ejemplo, funciones
mysql_*en lugar de PDO o MySQLi. - Arquitectura: ¿Existe alguna estructura (MVC)? ¿O todo el código está en un solo archivo
index.php? - Pruebas: ¿Existen pruebas automáticas? Si no, esa es tu primera prioridad.
Paso 2. Herramientas para el análisis
Utiliza analizadores estáticos para identificar rápidamente las áreas problemáticas:
- PHPStan / Psalm: Encontrarán errores de tipo e inconsistencias en las firmas.
- Rector: Actualizará automáticamente la sintaxis a una nueva versión de PHP.
- PhpMetrics: Mostrará la complejidad ciclomática y el acoplamiento del código.
Ejemplo de ejecución de Rector para actualizar de PHP 7.4 a 8.0:
vendor/bin/rector process src/ --set php802. Estrategia "Cirugía sin dolor": plan paso a paso
La refactorización de código legacy no es una "explosión". Es una serie de pasos pequeños y seguros. Utiliza la técnica "Strangler Fig" (higuera estranguladora): reemplaza gradualmente los módulos antiguos por nuevos, sin reescribir todo de una vez.
Paso 1. Cubre el código con pruebas
Sin pruebas, la refactorización es jugar a la ruleta rusa. Comienza con pruebas de caracterización (characterization tests):
class LegacyCalculatorTest extends TestCase { public function testOldCalculation(): void { // Ejecutamos el código antiguo y registramos el resultado $result = legacy_calculation(10, 5); $this->assertEquals(15, $result); }}Esto creará una "red de seguridad" antes de los cambios.
Paso 2. Aísla la lógica "sucia"
Si el código trabaja con variables globales ($GLOBALS), extráelas como parámetros de la función:
// Antes (dependencia global)function processOrder() { global $db; $result = $db->query("SELECT * FROM orders");}
// Después (inyección de dependencia)function processOrder(PDO $db) { $result = $db->query("SELECT * FROM orders");}Paso 3. Reemplaza funciones obsoletas
Por ejemplo, reemplazar mysql_* por PDO:
// Código antiguo$link = mysql_connect('localhost', 'user', 'pass');mysql_select_db('test', $link);$result = mysql_query("SELECT * FROM users", $link);
// Código nuevo (PDO)$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');$stmt = $pdo->query("SELECT * FROM users");$result = $stmt->fetchAll(PDO::FETCH_ASSOC);3. Problemas típicos del PHP legacy y sus soluciones
Problema 1: "Código espagueti" (mezcla de lógica y presentación)
Solución: Separa el código en capas. Extrae la lógica de negocio en clases independientes y el HTML en plantillas (por ejemplo, con Twig).
// Mal: lógica y HTML en el mismo archivoif ($user->isAdmin()) { echo "<h1>¡Hola, admin!</h1>";} else { echo "<h1>¡Hola, usuario!</h1>";}
// Bien: controlador + plantilla//