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 funcionando también como una pequeña cápsula del tiempo: referencias, enlaces y rastros de una blogosfera que, en muchos casos, ya no existen. 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 que se disparan 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 base de datos heredado, con puntos donde era necesario reforzar el escape de parámetros y reducir superficie de ataque.
  • Diseño anticuado: layout con tablas, sin 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 un PHP 5.4 realmente anticuado.

En un proyecto moderno, esto no pasa una 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 desde qDevel (inicios de año 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 era peligroso.


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 después la aplicación.
  2. Seguridad como prioridad: hashing moderno, rate limiting, cabeceras HTTP, cookies y HTTPS.
  3. Rediseño visual completo, pero sin meter frameworks pesados: CSS moderno, componentes, responsive.
  4. Arreglo de bugs históricos que afectaban a navegación y administración.
  5. Rendimiento con un sistema de caché híbrido y limpieza automática.

Elegí PHP 8.4 porque coloca al proyecto en una rama moderna con 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 no se recomiendan.
  • Hacer que el core del proyecto volviera a ser predecible en un runtime 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, verificación estándar.
  • Si el hash es MD5, se valida y, en ese mismo login, se rehash a bcrypt y se actualiza en base de datos.

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

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 ventana de 15 minutos y bloqueo de 30 minutos al exceder.

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/é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 olvidado hasta ahora.

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, últimos blogs y conteos.

Además, el cron de RSS limpia cachés al actualizar feeds y purga 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 paginación por falta de QSA en reglas de rewrite.
  • Redirecciones con doble barra (//login) por construcción defectuosa de URLs.
  • Login público que rechazaba administradores por un filtro de rol demasiado rígido.
  • Páginas del admin que mostraban datos incorrectos por errores históricos de copiar/pegar.
  • Menús duplicados en administración por includes repetidos.
  • Listados que no mostraban datos por includes faltantes en clases DAO.
  • Sin olvidar problemas realmente graves, como SQL injections, potenciales errores de código, vulnerabilidades como PHPmailer muy antiguo, etc. Que por suerte el WAF paraba de forma bastante eficiente.

Son detalles que no salen 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 interesa de esta modernización es lo que representa como lección: usar 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 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 criterio técnico; se trata de multiplicar 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 pasar 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 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 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)