Inventario del padre
search.php, componentes de header y footer, un hero sin uso y los require que los cargaban, más las rutas de assets que apuntaban al directorio del padre.
Una actualización del tema padre ya había borrado una vez el header y el footer a medida. Los migré a un child theme y encontré la causa real de los errores 500: PHP guardado en la base de datos y ejecutado con eval().
El header, el footer y la plantilla de búsqueda eran componentes a medida que vivían dentro del tema padre. Cualquier actualización del tema podía sobrescribirlos, y ya había pasado una vez: desaparecieron en producción y hubo que volver a subirlos a mano.
El objetivo era simple de enunciar: que todo el código a medida sobreviva a las actualizaciones y esté en archivos versionables.
Lo que parecía obvio y no era. Descartar bien ahorra días de trabajo en la dirección equivocada.
Ya se habían retirado los require del padre y el error seguía.
Con los plugins desactivados en staging el error persistía.
Tras resetear OPcache el comportamiento era idéntico.
search.php, componentes de header y footer, un hero sin uso y los require que los cargaban, más las rutas de assets que apuntaban al directorio del padre.
Cambié las URLs de JS, imágenes y badges de get_template_directory_uri a get_stylesheet_directory_uri para evitar 404 al mover los archivos.
Dos snippets de un plugin de «insertar código» vivían en la base de datos y se ejecutaban con eval() antes que el tema. Hacían require de las clases desde la ruta antigua: por eso se declaraban dos veces.
Esos snippets no solo cargaban las clases: también renderizaban el header y el footer. Al desactivarlos, desaparecían. Los reescribí sin el require y, en una segunda fase, moví el render al functions.php del child y borré los snippets.
Retirar los require del padre antes de activar el hijo, nunca después.
Minificación segura (solo comentarios y espacios) manteniendo el mismo nombre de archivo, porque el componente lo inyecta inline con file_get_contents.
Fragmentos simplificados y sin datos del cliente, para que se vea el enfoque técnico real.
wp post list --post_type=wpcode --fields=ID,post_title,post_status
wp post meta get <ID> _wpcode_active
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_type='wpcode' AND post_content LIKE '%require_once%'" // functions.php del child theme
foreach ( array( 'header', 'footer' ) as $component ) {
$file = get_stylesheet_directory() . "/components/{$component}/{$component}.php";
if ( file_exists( $file ) ) {
require_once $file;
}
}
add_action( 'wp_body_open', array( 'Site_Header_Component', 'render' ) );
add_action( 'wp_footer', array( 'Site_Footer_Component', 'render' ) );Primero estabilizar (snippets sin require), después migrar el render al tema. Reduce el riesgo de dejar la web sin header en producción.
Sin reescribir reglas ni fusionar selectores: el ahorro viene de comentarios y espacios, y el riesgo visual es cero.
| Indicador | Antes | Después | Contexto |
|---|---|---|---|
| Plantillas a medida en el padre | 4 | 0 | a salvo de actualizaciones |
| Snippets PHP desde la BD | 2 | 0 | render en el child theme |
| CSS del header | 39,5 KB | 26,7 KB | −32 % |
| CSS del footer | 12 KB | 6,6 KB | −45 % |
Cuando WordPress se comporta como si cargara código que no existe, reviso la base de datos antes que los archivos.
Porque cualquier actualización lo sobrescribe. Lo que sea a medida debe vivir en un child theme o en un plugin.
No debería. Cambia dónde vive el código y lo seguro que es actualizar.
No por sí mismos, pero su código no está en el repositorio ni aparece en un grep. En sitios con varios desarrolladores conviene que la lógica importante viva en archivos.
Si ya tienes un WordPress en producción, reviso el contexto y te digo con honestidad si lo que necesitas es desarrollo, SEO técnico, rendimiento o automatización.
Empezar un proyecto