Cómo modernicé Bitadir.com: de PHP 4/5 a PHP 8.4 sin reescribirlo desde cero (y por qué merecía la pena)

Bitadir.com no es “una web más”. Es un directorio de blogs en español que lleva 29 años online (desde 1996) y que, con el tiempo, ha terminado por funcionar también como una pequeña cápsula del tiempo: referencias, enlaces y rastros de una blogosfera que, en muchos casos, ya no existe. Aunque también aprovechamos para hacer una pequeña limpieza de webs que no funcionaban y spam de todo tipo. Ese valor histórico es precisamente lo que convierte la decisión de modernizarlo en algo más serio que un simple “upgrade de versión”.

El problema era el habitual en cualquier proyecto veterano que ha sobrevivido demasiados ciclos tecnológicos: Bitadir funcionaba sobre una combinación de PHP 4/5, plantillas antiguas, una capa de datos heredada, y un frontend que se había quedado clavado en el HTML de tablas. El sitio seguía operativo, sí, pero el coste invisible era alto: mantenimiento difícil, riesgo creciente protegido con capas de WAF y cada vez menos margen para adaptarlo a un entorno moderno sin que saltaran cosas por los aires.

La pregunta, por tanto, no era si merecía la pena tocarlo o simplemente cerrarlo para siempre. La pregunta real era cómo modernizarlo sin caer en el “lo reescribimos todo” que, en la práctica, suele convertirse en meses de trabajo, presupuestos disparados y un producto final que rara vez respeta todos los matices del original. La realidad es que tampoco tenía sentido porque no es un proyecto que genere ingresos, más bien solo pequeños gastos de hosting, pero de momento hay un tema de nostalgia y síndrome de Diógenes digital como con otras webs que conservo como Recursos Gratis (1995).


Lo que me encontré: deuda técnica, compatibilidad y seguridad “de otra era”

El diagnóstico inicial fue tan claro como incómodo:

  • Código PHP con patrones obsoletos: uso de var, constructores con nombre de clase (estilo PHP 4), referencias & que en PHP moderno dejan de comportarse como se esperaba, y funciones directamente eliminadas o deprecadas (each(), create_function(), get_magic_quotes_gpc()).
  • Acceso a la base de datos heredado, con puntos donde era necesario reforzar el escape de parámetros y reducir la superficie de ataque.
  • Diseño anticuado: layout con tablas, sin diseño responsive real, tipografías rígidas y una experiencia móvil muy por debajo de lo que hoy se considera aceptable.
  • El elefante en la habitación: contraseñas en MD5 sin salt, sin protección contra fuerza bruta, sin cabeceras de seguridad HTTP y formularios sin CSRF.
  • De hecho, solo funcionaba en un servidor con PHP 5.4, realmente anticuado.

En un proyecto moderno, esto no pasa la revisión. En un proyecto con 29 años de historia, esto pasa… hasta que deja de pasar.


Un poco de contexto: qFramework, qDevel y la arquitectura original

Para entender por qué Bitadir “aguantó tanto”, hay que hablar del cimiento: qFramework.

qFramework fue un framework MVC para PHP, creado por Francesc Pla Prats y Carles Balaguer Lozano en qDevel (a inicios de 2000 o así). Ferca Networks (la parte de webs la gestiona hoy Color Vivo) contó con los servicios de qDevel para ese desarrollo. Y aquí hay un matiz importante: esa base no era un “código improvisado”, sino una arquitectura con ideas que, bien reinterpretadas, siguen teniendo sentido hoy:

  • MVC con Front Controller
  • Sistema de filtros (cadena de responsabilidad)
  • Capa DAO para datos
  • Integración con Smarty para templates
  • Validaciones, usuarios y permisos
  • Licencia GPL v2
  • Versión documentada: 0,9b

Además, se notaban influencias de la cultura PHP de aquella época, incluyendo plataformas de blogging como pLog (más tarde LifeType), muy presentes en la comunidad hispanohablante entre 2003 y 2010.

¿Conclusión? No se trataba de “conservar lo viejo” solo por nostalgia. Se trataba de aprovechar lo rescatable (la separación de capas y el flujo MVC) y refactorizar con criterio todo lo que estaba desfasado o representaba un riesgo.


La estrategia: modernizar por capas, con seguridad primero y sin dramas

La hoja de ruta fue deliberadamente práctica:

  1. Compatibilidad completa con PHP 8.4: primero el framework y luego la aplicación.
  2. Seguridad como prioridad: hashing moderno, limitación de tasas, cabeceras HTTP, cookies y HTTPS.
  3. Rediseño visual completo, pero sin meter frameworks pesados: CSS moderno, componentes, responsive.
  4. Arreglo de errores históricos que afectaban la navegación y la administración.
  5. Rendimiento con un sistema de caché híbrido y limpieza automática.

Elegí PHP 8.4 porque sitúa al proyecto en una rama moderna con un horizonte de soporte claro: según el calendario oficial de PHP, 8.4 mantiene soporte activo hasta el 31 de diciembre de 2026 y soporte de seguridad hasta el 31 de diciembre de 2028.


1) Refactorización a PHP 8.4: lo “invisible” que lo cambia todo

El trabajo más delicado fue actualizar el framework y la base de la aplicación archivo por archivo. En términos prácticos, significó:

  • Reescribir patrones de clases antiguos (visibilidad, constructores, retornos y referencias).
  • Eliminar dependencias de funciones que ya no existen o que no se recomiendan.
  • Hacer que el core del proyecto volviera a ser predecible en un entorno de ejecución moderno.

En la capa de datos se reforzó la compatibilidad adoptando una abstracción actualizada basada en ADOdb para PHP 8.4, y se mejoró el escape de parámetros donde correspondía, dejando el camino preparado para un siguiente salto más contundente a PDO / prepared statements.

Versión antigua de Bitadir, igual desde 1997
Versión antigua de Bitadir, igual desde 1997

2) Seguridad: de “esto aguanta” a “esto se puede operar”

Aquí hubo dos cambios que, por sí solos, justifican el proyecto.

Contraseñas: de MD5 sin salt a bcrypt (sin resetear cuentas)

Se implementó una clase qPassword que usa bcrypt con coste 12, y lo hace con migración transparente:

  • Si el hash ya es bcrypt, la verificación es estándar.
  • Si el hash es MD5, se valida y, en ese mismo login, se rehash a bcrypt y se actualiza en la base de datos.

Resultado: se mejora la seguridad sin comprometer el acceso de los usuarios existentes. Que posiblemente ni siquiera entraban al panel de gestión. Una tarea pendiente es forzar el reinicio de contraseñas si vuelven a intentar acceder.

Fuerza bruta: rate limiting real

Se añadió qRateLimiter Para endpoints críticos (login, admin login, recuperación de contraseña y registro), con un esquema por defecto de 5 intentos en una ventana de 15 minutos y bloqueo de 30 minutos al exceder el límite.

A esto se sumaron cabeceras HTTP de seguridad, HTTPS forzado y cookies de sesión con flags HttpOnly y Secure, además de una CSP razonable.


3) Rediseño completo: del HTML con tablas a una UI moderna y responsive

Aquí no hubo “maquillaje”. Se pasó a un CSS nuevo (modern.css) con:

  • Variables CSS (custom properties) para mantener colores, sombras y radios con coherencia.
  • Tipografía moderna y jerarquía visual más clara.
  • Layout con Grid/Flexbox y enfoque mobile-first.
  • Componentes reutilizables: cards, botones con estados, mensajes de error y de éxito y badges de estado para el admin.
  • Sustitución de iconos antiguos por soluciones CSS cuando tenía sentido.

También se mejoró la navegación: menú responsive con toggle, indicador de sección activa, breadcrumbs y URLs más limpias con rewrite rules por temas de SEO, algo que hasta ahora se había pasado por alto.

Versión nueva de Bitadir en 2026 adaptada a PHP 8.4
Versión nueva de Bitadir en 2026 adaptada a PHP 8.4

4) Rendimiento: caché híbrida y cron con limpieza automática

Se implementó una caché híbrida:

  • Redis si está disponible (memoria y velocidad),
  • caché en archivos como fallback (siempre disponible),
  • TTL típico de 24 horas para categorías, noticias, blogs recientes y conteos.

Además, el cron de RSS limpia las cachés al actualizar los feeds y purga las entradas expiradas.


Los bugs “de verdad”: lo que se notaba en el día a día

Parte del éxito de modernizar un legacy es arreglar lo que se había normalizado como “cosas que pasan”:

  • Pérdida de parámetros en la paginación por falta de QSA en las reglas de rewrite.
  • Redirecciones con doble barra (//login) por construcción defectuosa de URLs.
  • Login público que rechazaba a los administradores por un filtro de rol demasiado rígido.
  • Páginas del admin que mostraban datos incorrectos debido a errores históricos de copiar y pegar.
  • Menús duplicados en administración debido a includes repetidos.
  • Listados que no mostraban datos debido a includes faltantes en las clases DAO.
  • Sin olvidar problemas realmente graves, como inyecciones SQL, errores potenciales de código, vulnerabilidades en PHPmailer muy antiguo, etc. Que, por suerte, el WAF funcionaba bastante bien.

Son detalles que no se ven en una captura bonita, pero que determinan si un sistema se mantiene con calma o con dolor.


Métricas y resultados: lo que cambió después

En métricas internas del propio proyecto, el salto quedó reflejado así (valores aproximados):

  • Lighthouse Performance: ~40 → ~85
  • Lighthouse Accessibility: ~50 → ~90
  • Tiempo de carga: ~3 s → ~1 s
  • Responsive: No → Sí
  • Seguridad de contraseñas: MD5 → bcrypt (cost 12)
  • Fuerza bruta: Ninguna → rate limiting + bloqueo temporal

Tiempo, tokens y coste: el ejemplo más claro de por qué conviene adaptarse

Una de las cosas que más me interesan de esta modernización es lo que representa como lección: usar la tecnología para ser más eficiente no es una amenaza; es supervivencia operativa.

El trabajo real se midió en torno a:

  • ~8 horas en total (en sesiones),
  • 62 archivos modificados,
  • ~4.800 líneas actualizadas,
  • 780 líneas de CSS nuevo,
  • ~600.000 tokens consumidos en sesiones con Claude Opus 4.5.

A partir de ahí, la comparación con “el mercado” es bastante intuitiva (e igual poco realista e injusta):

  • Reescritura completa típica: 10.000 € – 20.000 € y 2–4 meses (estimación del propio documento del proyecto).
  • Migración manual tradicional: 5.000 € – 10.000 € y 1–2 meses.
  • Modernización asistida por IA: coste de tokens + validación humana + ejecución técnica, pero en una escala completamente distinta.

Para hacerse una idea de la magnitud del coste de tokens, Anthropic indica que el precio de Claude Opus 4.5 “empieza” en 5 $ por millón de tokens de entrada y 25 $ por millón de tokens de salida.
Con un consumo de este tamaño, el coste directo de modelo se vuelve una partida asumible frente a semanas de desarrollo, y lo importante pasa a ser otra cosa: saber qué tocar, validar bien, y no confundir velocidad con falta de rigor.

Ese, para mí, es el mensaje de fondo: en 2026, resistirse a estas herramientas por miedo es como rechazar el control de versiones porque “antes se trabajaba con FTP”. No se trata de sustituir el criterio técnico; se trata de multiplicar la productividad con cabeza.


Preguntas frecuentes

¿Qué es lo más difícil al migrar una aplicación de PHP 4/5 a PHP 8.4 sin reescritura completa?
Identificar incompatibilidades ocultas (constructores antiguos, referencias &, funciones eliminadas), refactorizar sin romper flujos y validar regresiones en rutas y panel de administración.

¿Cómo se puede migrar de contraseñas MD5 a bcrypt sin obligar a un reseteo masivo?
Con migración transparente en login: se valida el hash legacy y, si es correcto, se recalcula con bcrypt y se actualiza en la base de datos en ese momento.

¿Qué medidas mínimas debería tener un login moderno en una web legacy modernizada?
Hashing robusto (bcrypt/argon2), rate limiting con bloqueos, HTTPS forzado, cookies HttpOnly/Secure, y cabeceras básicas (anti-clickjacking, nosniff, referrer policy y CSP si aplica).

¿De verdad compensa usar IA para modernizar un sistema legacy?
Sí, si se usa como acelerador y no como “piloto automático”, reduce tiempos de refactorización y permite abordar más superficie de código, pero exige validación, pruebas y criterio técnico en cada cambio.


Fuente: Documento interno del proyecto “Bitadir.com – Caso de Éxito: Modernización de una Aplicación Web Legacy” (febrero de 2026)