¿Qué hace 8.4?
Cuándo usarme
Usa este skill cuando un proyecto PHP heredado (típicamente un CMS personalizado escrito para PHP 5.x) deba:
- ejecutarse en PHP 8.3 / 8.4 sin una reescritura completa, y/o
- ser desplegado en hosting compartido (cPanel/Plesk) mediante FTP, sin Docker.
Síntomas típicos: mysql_*, ereg/eregi, etiquetas cortas <?, constructores estilo PHP4,
claves de array sin comillas ($row[Id]), $$var['key'], sizeof(null), dump MyISAM,
plantillas TemplatePower, credenciales en texto plano en config/defines*.php.
Lo que incluye este skill
| Recurso | Propósito |
|---|---|
scripts/compat.php | Shim: mysql_*→mysqli, ereg*/split→preg, userErrorHandler |
scripts/fix_barewords.php | Corrección tokenizada de claves bareword en arrays |
scripts/fix_variable_variables.php | Corrige la rotura de sintaxis de variable variable $$var['key'] en PHP 8 |
scripts/check_signatures.php | Verificador estático de firmas de métodos (LSP) |
scripts/lint-all.sh | Barrido php -l |
scripts/smoke-test.sh | Prueba de humo HTTP sobre una lista de URLs |
templates/defines.php | Cargador de .env + constantes de base de datos (funciona con y sin Docker) |
templates/env.example | Plantilla .env (DB_* + FTP_*) |
templates/htaccess | .htaccess moderno para Apache/PHP-FPM |
templates/user.ini | Sobreescrituras de PHP-FPM para hosting compartido |
templates/timthumb.php | Generador seguro de miniaturas GD (reemplaza TimThumb vulnerable) |
deploy/deploy.sh | Despliegue por FTP o rsync; excluye archivos de desarrollo; escribe .env remoto |
Reglas de oro
- Seguridad primero:
git init+ commit de referencia antes de tocar nada. Haz una copia de seguridad de la base de datos. - Desbloquear antes de refactorizar: el shim hace que toda la base de código funcione de inmediato.
- Una capa central de base de datos: intercambia/limita
dbClassprimero; el shim cubre las llamadasmysql_*sin procesar. - Seguridad temprana: elimina herramientas peligrosas y TimThumb tan pronto como el sitio se renderice.
- Verificar continuamente:
php -ldespués de cada lote, luego prueba de humo en cada URL.
Procedimiento
0. Línea base
git init -q && git add -A && git commit -qm "baseline: legacy PHP"
find . -name '*.php' -not -path './node_modules/*' | wc -l
Guarda una lista de URLs públicas reales (desde la base de datos/mapa del sitio) para scripts/smoke-test.sh.
1. Instalar el shim de compatibilidad
- Copia
scripts/compat.phpa, por ejemplo,includes/compat.php. - Cárgalo al principio del bootstrap, antes de cualquier código heredado:
include_once(__DIR__.'/../includes/compat.php'); - Proporciona
mysql_*,ereg/eregi/ereg_replace/eregi_replace/split,mysqli_report(MYSQLI_REPORT_OFF)(los errores SQL devuelvenfalseen lugar de lanzar una excepción), yuserErrorHandler()(suprime los antiguos E_NOTICE de PHP 5 como "undefined index/variable"). - Registra el manejador una vez:
if (function_exists('userErrorHandler')) set_error_handler('userErrorHandler');
2. Correcciones mecánicas (scripts)
php scripts/fix_barewords.php # prueba en seco
php scripts/fix_barewords.php --apply
php scripts/fix_variable_variables.php # prueba en seco
php scripts/fix_variable_variables.php --apply
scripts/lint-all.sh
Luego corrige lo que aún reporte el lint:
- Etiquetas cortas:
<?→<?php(busca con grep<\(?!php|=|xml\)). - Constructores PHP4:
function NombreClase()→function __construct(). - Acceso a cadenas con llaves:
$s{0}→$s[0]. breaksueltos fuera de bucles/switches (fatal en PHP 8).- Variables variables en
${...}: entrecomilla las claves bareword / usa${$var['k']}.
3. Firmas, estática y seguridad nula
php scripts/check_signatures.php
Para cada MISMATCH, agrega parámetros por defecto al método hijo (ej. $tabla = null).
Luego corrige en tiempo de ejecución según aparezcan errores fatales:
| Error | Corrección |
|---|---|
ArgumentCountError: too few arguments | asigna valores por defecto a parámetros finales |
$this en método estático | usa self::/static:: o pasa el contexto de la aplicación |
method_exists(null, ...) | is_object($x) && method_exists($x, ...) |
Cannot access offset of type string on string | reordena is_array($x) antes de $x['k'] |
Undefined constant "X" | entrecomilla claves bareword / constantes |
sizeof/count/array_keys(null) | is_array($x) ? ... : ..., (array)$x |
foreach(null) | foreach ((array)$x as ...) |
Accessing static property X as non static | $this->prop → self::$prop |
4. Ejecutar contra la base de datos real e iterar
Apunta la configuración a la base de datos, luego solicita cada URL. Corrige un error fatal por prueba hasta que
scripts/smoke-test.sh reporte cero fallos y el registro no tenga advertencias.
5. Seguridad (hacerlo temprano)
- Reemplaza TimThumb con
templates/timthumb.php. - Elimina
scanner.php,chmod777.php,info.php, scripts de depuración sueltos. - Configura
.htaccesspara denegar.env,.git,config/defines.php, archivos con punto. - Rota las contraseñas de base de datos comprometidas; mueve secretos a
.env. - Recomienda tokens CSRF, CSP/HSTS, limitación de tasa, sentencias preparadas.
6. Configuración + despliegue
- Usa
templates/defines.php(cargador de.env) ytemplates/env.example. - Usa
templates/htaccessytemplates/user.ini. - Despliega con
deploy/deploy.sh.
Sistema .env
config/defines.php carga .env desde la raíz del proyecto, con fallback a getenv()
y finalmente valores predeterminados seguros. Esto mantiene Docker funcionando (mismas claves DB_*) mientras hace que el despliegue por FTP sea una edición de un solo archivo.
.env (nunca se sube al repositorio):
DB_HOST=localhost
DB_NAME=usuario_cpanel_termospa
DB_USER=usuario_cpanel_termospa
DB_PASS=super-secreto
DB_SUBDIRECTORIO=
Despliegue (sin Docker)
Lista de verificación para hosting compartido (cPanel/Plesk)
- PHP: selecciona 8.3 (o 8.1+); habilita
mysqli,gd,mbstring,pdo_mysql. - Base de datos: crea base de datos + usuario, importa el dump. Para MySQL 8:
Espera advertencias deSET SESSION sql_mode='NO_ENGINE_SUBSTITUTION';utf8_spanish_ci→utf8mb3_spanish_ci(o ejecuta la migración a utf8mb4). - Archivos: sube a
public_html/(excluyedocker/,tools/,router.php,_DOCS/,.git/). - Permisos: directorios
755, archivos644;recursos/cache/,.cache/,upload/escribibles (775/777). - Configuración: crea el
.envremoto (deploy.sh lo hace) y verifica las variablesDB_*. - Deniega el acceso a
.enven.htaccess.
deploy.sh
./deploy.sh ftp # lftp reverse mirror
./deploy.sh ftp --dry-run
./deploy.sh rsync # rsync sobre SSH
Lee .env (FTP_HOST/USER/PASS/PATH/TLS, o SSH_*), prepara solo los archivos de tiempo de ejecución,
escribe un .env solo remoto que contiene únicamente DB_* (para que las credenciales FTP nunca estén en el servidor),
sube, e imprime los pasos de permisos y base de datos.
Lista de verificación de verificación
-
scripts/lint-all.sh→ todo limpio -
scripts/smoke-test.sh urls.txt→ 0 fallos - las páginas de inicio / producto / proyecto / noticias / contacto se renderizan
-
/sitemap.xmly/rssdevuelven XML válido -
/imagenes/thumbnail/...devuelve una imagen (GD + reescritura) -
/administracion/login/carga; el inicio de sesión y un gestor de contenido funcionan - el formulario de contacto envía
- la subida de imágenes del administrador escribe en
upload/ - no hay
PHP Warning/Fatalen el registro de errores
Problemas que siempre aparecen
mysqli_querylanza una excepción en PHP 8.1+ por defecto; llama amysqli_report(MYSQLI_REPORT_OFF)para queor die()/if($q=...)heredado siga funcionando.php_flag/php_valueen.htaccesscausa HTTP 500 con PHP-FPM — usa.user.ini.- La sintaxis de variable uniforme de PHP 8 rompe silenciosamente
$$var['key'](escribe en la variable incorrecta) — ejecutafix_variable_variables.php. - Las rutas relativas en
include()se resuelven contra el directorio del script/CWD; prefiere__DIR__. - Eliminado en PHP 8:
mysql_*,ereg*,split,create_function,each, magic quotes. - OPcache almacena en caché archivos antiguos: reinicia FPM/Apache después de editar.
- UTF-8:
SET NAMES utf8mb4; prefiere InnoDB +utf8mb4_unicode_cipara trabajo nuevo.
Proyectos hermanos
Estos proyectos CMS comparten la misma arquitectura. Ejecuta el mismo procedimiento, ajustando solo
la ruta del bootstrap y el destino de la reescritura de miniaturas. Copia scripts/ dentro de tools/
del proyecto destino, ejecuta desde la raíz del proyecto.