WP Manifestindependent plugin directory
manifest / security / dctwphero

DCT WP Hero

Modular WordPress plugin: IP-based login protection, WP-CLI migration and CDN URL rewriting.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/doblejou/dctwphero/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Plugin para WordPress 7.0+ y PHP 8.4+. Sin dependencias externas ni build. Sus funcionalidades se organizan en módulos independientes; el primero es la protección de login por IP. Incluye un módulo de migración exclusivamente WP-CLI, activo por defecto. Incluye un módulo CDN genérico, desactivado por defecto, sin integración con proveedores.

Instalación

Copiar dctwphero.php, includes/, modules/, templates/, uninstall.php, readme.txt y review-notes.txt a wp-content/plugins/dctwphero/ y activar el plugin. No admite activación en red multisite en esta versión: la activación se rechaza en esas instalaciones.

En Ajustes → DCT WP Hero se configuran:

La pantalla utiliza pestañas nativas de WordPress, sin JavaScript ni CSS propios. La primera, Módulos, contiene únicamente la activación de funcionalidades. Los módulos con ajustes tienen su pestaña dedicada: Protección de login muestra sus ajustes y bloqueos, incluso cuando está desactivado. La versión aparece en todas las pestañas. Guardar ajustes, paginar o desbloquear conserva la pestaña de login.

  • Módulos: activa o desactiva cada funcionalidad mediante su casilla y «Guardar módulos». La protección de login está activa inicialmente, también al actualizar instalaciones anteriores.

  • Fallos para bloquear (X): inicialmente 3; rango 1–10000.

  • Ventana móvil (Y): inicialmente 1 minuto; rango 1–10080.

  • Duración del bloqueo: inicialmente 15 minutos; rango 1–10080.

Estos valores iniciales no sobrescriben los ajustes ya guardados.

Los cambios se aplican a nuevos fallos; los bloqueos activos mantienen su fecha de expiración. La pantalla incluye IP bloqueadas, fechas y desbloqueo individual. Solo se aceptan enteros o cadenas de dígitos dentro del rango; booleanos y decimales se rechazan. Al leer ajustes persistidos incompletos se añaden los valores iniciales; si contienen tipos o rangos inválidos se usan los valores iniciales completos, también tras una importación.

Desactivar la protección de login deja de bloquear accesos y de registrar fallos. No borra los ajustes ni las filas existentes: al reactivarla se aplican los bloqueos que todavía no hayan expirado. La limpieza de datos caducados y las migraciones siguen funcionando aunque el módulo esté desactivado. Los ajustes y el desbloqueo manual siguen disponibles. Los módulos se guardan separadamente de sus ajustes, mediante la Settings API y con permiso manage_options y nonce. La lectura y la validación comparten el contrato de estados 0/1 (enteros o cadenas). Los valores persistidos inválidos recuperan el valor inicial del módulo: login y migración activos, CDN desactivado, sin alterar otros estados válidos.

Para añadir un módulo, crear modules/<id>/class-module.php con un método register() que conecte sus hooks, cargarla desde dctwphero.php y declararla en DCTWPHero::modules() con etiqueta, descripción, estado inicial y callback de registro, además de callbacks opcionales admin_register para conectar su administración (incluso desactivado) y render para su pantalla de ajustes. El listado de administración y las pestañas se generan a partir de ese registro. Cada módulo agrupa su lógica, administración, almacenamiento y plantillas en su propia carpeta. modules/login/class-module.php contiene la protección de login; su almacenamiento está en modules/login/class-store.php, su administración en modules/login/class-admin.php y su plantilla en modules/login/templates/settings.php. La administración general permanece en includes/class-admin.php y sus plantillas en templates/admin.php y templates/modules.php. Las plantillas se limitan a la presentación, sin consultas ni validaciones de negocio. Reservar includes/ para infraestructura general y código realmente reutilizado entre módulos; no introducir dependencias directas entre módulos ni extraer utilidades compartidas para necesidades hipotéticas. Las pruebas específicas se organizan en tests/<id>/; tests/modules.php comprueba el registro general.

Estructura de carpetas

includes/             Infraestructura general y código compartido
modules/
  login/              Protección de login, administración y almacenamiento
    templates/        Presentación del módulo de login
  migration/          Migración WP-CLI y administración informativa
    templates/        Ejemplos de uso de migración
  cdn/                Coordinación, reescritura y administración del CDN
    templates/        Ajustes y aviso CORS
templates/            Pantalla general y activación de módulos
tests/
  login/              Pruebas específicas de login
  migration/          Pruebas específicas de migración
  cdn/                Motor, ajustes, diagnóstico e integración HTTP desechable
release/              ZIP instalables versionados

CDN

Configurar Ajustes → DCT WP Hero → CDN y activar después en Módulos. Está desactivado por defecto, también al actualizar. Necesita un CDN ya configurado para servir las mismas rutas del origen; no configura DNS, sube archivos ni purga cachés. Desactivar conserva ajustes y deja de reescribir.

  • Un dominio ASCII sin protocolo, puerto ni ruta, como cdn.ejemplo.com (IDN en punycode). Vacío no reescribe. Conserva el protocolo de URL absolutas.
  • Extensiones con punto, una por línea. Iniciales: .avif, .css, .gif, .jpeg, .jpg, .js, .json, .mp3, .mp4, .pdf, .png, .svg, .webp, .woff y .woff2. Lista vacía no reescribe.
  • Exclusiones literales por cadenas, una por línea y sensibles a mayúsculas.
  • URL relativas a la raíz activadas inicialmente; se pueden desmarcar.

Fuentes y CORS: el CDN debe enviar Access-Control-Allow-Origin permitiendo el dominio de la web; de lo contrario el navegador puede bloquear las fuentes. Configurar esas cabeceras en CDN/origen según el proveedor. El módulo muestra un aviso, pero no modifica ni certifica las cabeceras CORS.

Captura HTML público GET y transforma URL locales absolutas, sin protocolo, relativas a raíz y con barras escapadas, incluidas las de srcset y CSS inline. Utiliza home_url() y site_url() como autoridades exactas, nunca HTTP_HOST. Conserva rutas, parámetros y fragmentos, omitiendo dominios externos y CDN existente. No reescribe rutas como imagenes/foto.jpg sin barra inicial, administración, login, AJAX, CLI, cron, XML-RPC, previews, robots, trackbacks, feeds, descargas, respuestas comprimidas ni tipos distintos de HTML. En REST solo transforma content.rendered y excerpt.rendered de tipos de contenido, no context=edit, raw ni otros campos JSON. El búfer retiene la salida hasta el final y no está destinado a streaming.

Los ajustes están en dctwphero_cdn_settings mediante Options API; la reescritura no altera las URL guardadas en la base de datos ni archivos. No procesa referencias internas de CSS/JS externos. Invalidar la caché de página tras cambiar ajustes o estado: el HTML servido sin ejecutar WordPress no puede ser reescrito por el módulo. No ejecutar simultáneamente con CDN Enabler; la pestaña avisa si está activo, pero no lo desactiva ni importa sus ajustes automáticamente.

«Comprobar CDN» usa los ajustes guardados y hace una petición HEAD explícita a wp-includes/images/blank.gif mediante la API HTTP segura de WordPress, sin redirecciones y rechazando destinos inseguros. Solo HTTP 200 se considera éxito. Un fallo no borra ajustes; un éxito no garantiza todos los recursos ni CORS. Puede fallar si el CDN no distribuye wp-includes o no admite HEAD. Guardar ajustes no realiza peticiones externas. Comprobación restringida a POST con nonce y manage_options, disponible incluso con el módulo desactivado.

Detalles técnicos en modules/cdn/MEMORIA.md. Pruebas:

wp --path=/var/www/wpdev4.local --skip-plugins=dctwphero eval-file tests/cdn/rewriter.php
python3 -B tests/cdn/integration.py --wp-path /var/www/wpdev4.local

La integración crea un WordPress desechable con prefijo SQL aleatorio y servidor local, ejecuta también ajustes, diagnóstico HTTP simulado y regresión de login, y limpia sus recursos al terminar. Solo utiliza núcleo y credenciales del sitio indicado; no modifica sus opciones ni sus plugins. Requiere WP-CLI, PHP y un host .local que resuelva a 127.0.0.1 (--http-host, por defecto wpdev4.local). No prueba un proveedor CDN real ni CORS en navegador.

Migración mediante WP-CLI

Activar Migración CLI en la pestaña Módulos. Está activo por defecto, también al actualizar. Desactivarlo deja de registrar sus comandos. La pestaña Migración CLI muestra ejemplos de uso incluso desactivado, sin formularios ni controles de ejecución. No hay endpoints de migración REST/AJAX; la implementación está aislada en modules/migration/ y sus clases operativas no se cargan mediante HTTP.

wp dctwphero export --file=/ruta/privada/sitio.zip
wp dctwphero import --file=/ruta/privada/sitio.zip --mode=merge --dry-run
wp dctwphero import --file=/ruta/privada/sitio.zip --mode=merge
wp dctwphero import --file=/ruta/privada/sitio.zip --mode=mirror --yes

Importar sobrescribe datos y no crea copia previa ni garantiza rollback. La copia del destino es responsabilidad del operador. Solo importar paquetes de confianza: contienen código PHP y estructuras de base de datos. SHA-256 detecta corrupción, pero no autentica el origen. Los ZIP contienen datos sensibles sin cifrar: almacenarlos fuera de cualquier directorio público, con acceso restringido.

Contenido y requisitos

El formato ZIP propio versión 1 no es compatible con .wpress. Contiene manifest.json, archivos bajo content/ y un volcado NDJSON por tabla bajo database/: esquema CREATE TABLE, columnas y valores base64 para conservar datos binarios. No necesita los plugins de All-in-One ni Composer.

Incluye las tablas del prefijo del sitio y archivos de wp-content, incluidos plugins, temas, uploads y mu-plugins. No incluye el núcleo, wp-config.php ni configuración del servidor. El destino necesita WordPress y DCT WP Hero instalados. Los usuarios y contraseñas importados reemplazan los del destino.

Requiere PHP de 64 bits con ZipArchive y mysqli; tablas InnoDB sin vistas, triggers, particiones ni rutas de almacenamiento externas. Admite claves externas canónicas de SHOW CREATE TABLE entre tablas incluidas, también compuestas y cíclicas. Se conservan columnas y acciones ON DELETE/UPDATE, con nombres nuevos de constraint para evitar colisiones. Se reconstruyen sobre tablas temporales y SQL valida sus filas antes de publicar. Se rechazan relaciones que crucen el conjunto exportado o sustituido/eliminado hacia tablas conservadas, excluidas o de otro sitio/base. Dry-run valida estructura y alcance, no integridad referencial de las filas: esta se comprueba en la carga real antes de copiar archivos. Los identificadores de tabla y columna deben usar letras ASCII, números, _ o $. Plugins, temas, mu-plugins y uploads deben estar en sus rutas estándar dentro de wp-content. No admite multisite, enlaces simbólicos en el ámbito migrado, paquetes cifrados ni importar un WordPress posterior al instalado.

El ZIP de salida requiere una ruta absoluta y un directorio existente. Se rechazan las rutas públicas conocidas y archivos ya existentes; el operador debe verificar que el directorio no esté expuesto mediante otros alias del servidor. Se publica solo tras verificar íntegramente su contenido y con permisos 0600. Se conservan fecha y permisos de lectura/ejecución de los archivos, sin bits especiales ni escritura para grupo u otros. Los ZIP antiguos sin esos metadatos siguen admitidos y sus archivos usan 0644.

Modos, exclusiones y URLs

--mode es obligatorio, sin valor predeterminado:

  • merge: sustituye archivos coincidentes y tablas incluidas completas; conserva archivos y tablas adicionales. No fusiona filas de tablas.
  • mirror: hace lo anterior y elimina archivos y tablas extra del ámbito no excluido. No borra archivos fuera de wp-content, tablas de otros prefijos ni componentes excluidos. También elimina directorios sobrantes, de hijos a padres, únicamente cuando quedan vacíos. Conserva las raíces plugins, themes, uploads y mu-plugins, así como los padres de las exclusiones. El paquete no registra directorios vacíos del origen; no se reconstruyen salvo esas raíces.

Exportación admite listas separadas por comas:

wp dctwphero export --file=/ruta/privada/sitio.zip --exclude=uploads/privado,plugins/ejemplo --exclude-tables=wp_tabla_temporal

Las rutas son relativas a wp-content, sin glob, rutas absolutas ni ... Las tablas se indican con su nombre completo; no se pueden excluir options, users ni usermeta. Las exclusiones quedan en el manifiesto y se conservan al importar, también en modo mirror y al cambiar de prefijo.

Exclusiones predeterminadas: cache, upgrade, upgrade-temp-backup, ai1wm-backups, updraft, backups, debug.log, db.php, object-cache.php, advanced-cache.php y el directorio del propio DCT WP Hero. Las copias de otras herramientas deben excluirse explícitamente. El código instalado del migrador no se reemplaza; el plugin permanece activo, pero sus estados de módulos proceden de los datos importados.

Por defecto conserva home y siteurl del destino y adapta rutas, URLs de medios, prefijo y claves de permisos. Se pueden fijar URLs finales con --home-url=https://destino.example y --site-url=https://destino.example. Se rechazan conflictos con WP_HOME y WP_SITEURL. Los reemplazos contemplan variantes escapadas/codificadas y longitudes serializadas, sin instanciar objetos. Si origen comparte home y siteurl pero destino los separa, las coincidencias ambiguas usan home; las opciones finales se fijan individualmente. La serialización personalizada C: que necesite cambios se rechaza. No se sustituyen dominios de correo indiscriminadamente.

Validación y fallos

Antes de la fase destructiva, la importación sustituye el proceso inicial por uno aislado sin plugins normales, mu-plugins ni temas antiguos. Así se evita ejecutar al cerrar callbacks de Elementor u otros plugins cuyos archivos se hayan borrado o reemplazado. Se omiten los drop-ins de caché durante esa fase; los ajustes y comprobaciones posteriores arrancan procesos normales nuevos con el sitio restaurado. El trabajador aislado no se hereda a las verificaciones ni a la limpieza de cachés.

El reinicio transparente usa pcntl_exec de PHP CLI. Si no está disponible, se detiene antes de modificar el sitio y se muestra el comando equivalente con --skip-plugins --skip-themes --require=.../modules/migration/import-bootstrap.php. Ese arranque explícito comprueba también que DCT WP Hero y el módulo estén activos. Los filtros que omiten los drop-ins no dependen de funciones auxiliares de WordPress: también funcionan con WP_CACHE=true, antes de que esas funciones estén disponibles. Si wp-config.php o un drop-in carga código de plugins o temas del destino pese al aislamiento, la importación se rechaza antes de borrar archivos.

--dry-run comprueba integridad, rutas, destinos, espacio estimable, esquemas y transformación de filas. Extrae a temporales privados, pero no escribe archivos ni tablas del sitio. No garantiza permisos DDL ni espacio en un servidor SQL remoto, compatibilidad de todos los plugins o ausencia de cambios concurrentes. Las conversiones SQL que produzcan avisos se consideran errores: no se publican tablas con valores truncados silenciosamente. La finalización de ajustes se ejecuta en un proceso nuevo sin los plugins normales antiguos cargados.

Sin --yes se pide confirmación después de mostrar el resumen. Durante la restauración se mantiene .maintenance, se preparan tablas aisladas y se publican por RENAME TABLE. Se actualizan URLs, cachés y enlaces permanentes; se comprueban instalación y base de datos en procesos nuevos. No se modifica .htaccess. La comprobación SQL usa CHECK TABLE solo sobre las tablas del prefijo del sitio y requiere sus permisos; no revisa otras instalaciones de la misma base de datos. Al terminar, antes de retirar mantenimiento, se limpia la caché de objetos de WordPress. Si Elementor está activo, primero se invalida su CSS con la misma API que wp elementor flush-css; se regenerará al visitar las páginas. No se activa Elementor si está desactivado. Esto no purga automáticamente CDN ni todas las cachés de página de terceros. Un fallo de esta limpieza devuelve error y mantiene el sitio en mantenimiento. El usuario SQL debe poder inspeccionar los metadatos relevantes. MySQL/MariaDB pueden ocultar triggers sin el permiso correspondiente: no se puede certificar la ausencia de objetos que el servidor no permite ver; comprobar ese requisito al preparar la migración.

Ante fallo tras comenzar la fase destructiva se devuelve error y se conserva mantenimiento. Revisar o recuperar manualmente el sitio antes de retirar .maintenance. No hay copia automática. Una terminación forzada puede dejar temporales privados o tablas aleatorias dctm_; no eliminarlos indiscriminadamente, pues otra migración de otro sitio podría estar en curso. El bloqueo de operación solo serializa estas migraciones: no detiene cron, workers, WP-CLI ni otros escritores. Durante la operación se eleva SESSION wait_timeout a 24 horas, o se conserva su valor si ya era mayor, sin cambiar la configuración global. Al terminar se restaura el valor anterior solo sobre la conexión original. Se verifica la identidad de la conexión y el propietario de GET_LOCK antes de publicar el ZIP y en los puntos críticos de importación. Una desconexión o pérdida del bloqueo detiene la operación, sin readquirirlo automáticamente; si ya se iniciaron cambios, conserva mantenimiento. La limpieza identifica el recurso que falló (temporales, bloqueo o timeout). Si el ZIP ya estaba publicado y verificado cuando falla la limpieza, el error lo indica: conservar el paquete y revisar los recursos pendientes antes de repetir la operación.

El procesamiento es por filas/archivos, no por tablas completas en memoria; una fila muy grande y el inventario sí consumen memoria. La exportación mantiene una instantánea InnoDB, pero no es atómica con los archivos: evitar escrituras y cambios de esquema durante toda la operación. Se necesita disco para contenido preparado, ZIP y verificación; en importación también para las tablas de preparación. No hay límite comercial de tamaño; el manifiesto se limita a 64 MiB para validación segura.

Actualizaciones y migraciones

Para distribuir el plugin, crear un ZIP con la carpeta dctwphero/ que contenga dctwphero.php, includes/, modules/, templates/, uninstall.php, readme.txt y review-notes.txt. Excluir pruebas, reglas, releases anteriores y memorias técnicas MEMORIA.md. Subirlo desde Plugins → Añadir plugin → Subir plugin. Para actualizar, subir el nuevo ZIP y confirmar la sustitución del instalado, sin desinstalarlo.

El código se distribuye bajo GPL-2.0-or-later. readme.txt contiene la ficha pública en inglés para WordPress.org; mantener su Stable tag sincronizado con Version al preparar una release. Indicar en Tested up to únicamente una versión mayor/menor de WordPress probada.

Antes del envío, ejecutar Plugin Check sobre el paquete de distribución. Para comprobar el código fuente desde este proyecto sin desplegarlo en el sitio:

wp --path=/var/www/wpdev4.local plugin check "$PWD" --exclude-directories=tests,release,.ai-rules --exclude-files=AGENTS.md,APP.md,MEMORIA.md --format=json

Plugin Check no sustituye la revisión de seguridad ni las pruebas funcionales. El bootstrap CLI de migración se carga antes de definir ABSPATH: su guardia WP_CLI es intencionada. No añadir una exigencia de ABSPATH a ese archivo. Los avisos sobre operaciones nativas de archivos, SQL dinámico y excepciones CLI del migrador requieren revisión contextual; no deben ocultarse globalmente ni resolverse aplicando escape HTML al terminal o cambiando garantías de publicación. review-notes.txt, incluido en el ZIP, describe los contratos que justifican las excepciones PHPCS de línea concretas. Command::terminal() convierte controles en texto visible al imprimir diagnósticos CLI. No utilizar esas excepciones para código HTTP ni considerar la documentación una aprobación del equipo de revisión.

Hacer una copia de seguridad antes de actualizar y probar primero en un entorno de pruebas. Desinstalar elimina los datos; sustituir los archivos no los elimina.

La versión del plugin y la del esquema son independientes. La opción dctwphero_db_version guarda el último paso completado. En la activación y en plugins_loaded se ejecutan los pasos pendientes antes de usar el almacenamiento. La migración 1 crea o adapta la tabla existente mediante dbDelta() sin borrar filas. Las instalaciones anteriores sin esta opción parten de la versión 0.

Para futuros cambios, aumentar DCTWPHero_Store::DB_VERSION y añadir el siguiente caso en upgrade(). Mantener los pasos anteriores para admitir saltos de versión. Cada paso debe ser reejecutable y comprobar su resultado; usar SQL explícito para renombrados, eliminaciones o transformaciones que dbDelta() no resuelva. No modificar solo el esquema inicial para introducir una nueva migración.

Un bloqueo de MySQL/MariaDB serializa las migraciones concurrentes. La versión se guarda solo tras completar cada paso. Si falla, la petición se detiene con HTTP 503 y se registra el error; se reintentará en la siguiente petición. Esto afecta también al frontal mientras la migración esté pendiente. No hay rollback automático de DDL: corregir el problema y reintentar, o restaurar la copia completa. Se rechaza ejecutar código antiguo contra un esquema de versión superior.

Comportamiento

Al alcanzar X fallos en los últimos Y minutos se bloquean los siguientes accesos a wp-login.php con HTTP 403: visualización, autenticación, recuperación de contraseña y demás acciones de esa ruta. El fallo que alcanza X ya ha sido procesado; el 403 se aplica a partir de la siguiente petición.

El bloqueo no se prolonga con accesos posteriores. Al expirar o desbloquear manualmente se reinicia el historial. Un login correcto no borra fallos previos. REST, XML-RPC y formularios de login externos no están protegidos.

La única fuente de IP es REMOTE_ADDR, normalizada para IPv4 e IPv6. Con CDN o proxy, Apache debe configurar correctamente mod_remoteip y sus proxies de confianza. El plugin no interpreta cabeceras. Si no hay IP válida, deniega el acceso al login. Una IP compartida (NAT) comparte también el contador y bloqueo.

Se guarda una fila por IP con marcas temporales de fallos, último cambio y expiración. No se guardan credenciales. InnoDB y bloqueos de fila serializan los fallos simultáneos. La limpieza horaria depende de WP-Cron y elimina registros caducados; su retraso no prolonga bloqueos.

Desactivar conserva los datos. Desinstalar elimina la tabla, los ajustes y la tarea periódica. Un administrador bloqueado puede entrar desde otra IP o utilizar WP-CLI para desbloquear:

wp eval 'DCTWPHero_Store::unlock("192.0.2.10");'

No sustituye una protección de servidor/WAF frente a ataques distribuidos. Si la base de datos no está disponible, no se garantiza el registro de nuevos fallos.

Desarrollo y pruebas

Sintaxis: find . -name '*.php' -exec php -l {} \;.

Las pruebas PHP comparten las aserciones de tests/checks.php. Los recuentos indican comprobaciones ejecutadas, no un porcentaje de cobertura.

Las pruebas de integración requieren un WordPress local con el plugin activo. No deben ejecutarse en producción: modifican temporalmente sus ajustes y módulos y crean bloqueos de prueba y usuarios temporales. Restauran los ajustes y las filas afectadas, restauran los estados de módulos y eliminan los usuarios al terminar, incluso cuando una prueba falla. Una interrupción forzada del proceso puede impedir esa restauración.

python3 tests/login/integration.py --wp-path /var/www/wpdev4.local --url http://wpdev4.local

Incluye pruebas HTTP de login, administración, permisos, nonce y desbloqueo, activación y desactivación de módulos, pruebas concurrentes y tests/login/store.php para ventana móvil, expiración e IPv6. tests/login/settings.php comprueba tipos, límites y lectura segura de ajustes; tests/login/store-contract.php provoca errores SQL en una tabla independiente para verificar propagación de fallos, rollback y reintentos, incluido COMMIT. Ambos se ejecutan desde la suite desechable de migración descrita abajo.

Validación de estados y compatibilidad con instalaciones anteriores: wp --path=/var/www/wpdev4.local eval-file tests/modules.php. Modifica y restaura temporalmente la opción de módulos; ejecutar solo en pruebas y sin tráfico.

Migraciones: wp --path=/var/www/wpdev4.local eval-file tests/login/migrations.php. Solo en pruebas y sin tráfico concurrente: usa una tabla temporal independiente, modifica temporalmente la opción de versión y la restaura al terminar. Comprueba instalación, adaptación de instalaciones antiguas, reejecución y fallos. Los mensajes de error de migración de estos casos negativos son esperados.

Pruebas de migración en una copia desechable del núcleo, con tablas de prefijo aleatorio en la misma base de datos y configuración privada. El WordPress indicado solo proporciona núcleo y credenciales; no se restaura sobre él:

python3 -B tests/migration/integration.py --wp-path /var/www/wpdev4.local --http-host wpdev4.local

--http-host es opcional y debe ser un host .local que resuelva a 127.0.0.1. Habilita un servidor PHP temporal en un puerto libre para ejecutar la regresión HTTP de login/administración y comprobar que las clases CLI no se cargan por web. Siempre ejecuta valores serializados, pruebas de almacenamiento/migraciones, round-trip con dominios/rutas/prefijos diferentes, valores numéricos y binarios, merge/mirror, exclusiones, dry-run, cancelación, paquetes inseguros, fallo parcial y desactivación del módulo. Limpia sus tablas y directorios al finalizar. La matriz y las limitaciones de cobertura están en tests/migration/README.md. Para incluir la prueba de caché con Elementor real, añadir --elementor-path=/var/www/wpdev4.local/wp-content/plugins/elementor; se utiliza una copia desechable y no se modifica el plugin indicado. Las integraciones sin permisos SQL se señalan como SKIP; no se confunden con pruebas reales aprobadas. --keep-on-failure conserva el entorno privado para diagnosticar un fallo.