- Inicio
- Rutas de aprendizaje
- Ruta Experto
- La arquitectura de Éclair
La arquitectura de Éclair
- 1. La arquitectura de Éclair
- 2. Desarrollo local y pruebas
- 3. Personalizar las plantillas del tema
- 4. Nginx, VPS, proxy y CDN: desplegar Éclair
- 5. Rendimiento de Éclair: mantener 100 sobre 100
- 6. Migrar desde WordPress o Wix
- 7. Seguridad avanzada y mantenimiento
- 8. Industrializar Éclair en una agencia
Índice
- ¿Cómo atraviesa una petición la arquitectura de Éclair?
- El almacenamiento en archivos planos: la clase Store
- Los archivos de la carpeta data/
- Las clases principales de app/lib/
- ¿Por qué se vacía la caché con cada cambio?
- Cabeceras de seguridad y CSP
- Las traducciones
- Para recordar
- Para saber más
- Preguntas frecuentes
Índice
- ¿Cómo atraviesa una petición la arquitectura de Éclair?
- El almacenamiento en archivos planos: la clase Store
- Los archivos de la carpeta data/
- Las clases principales de app/lib/
- ¿Por qué se vacía la caché con cada cambio?
- Cabeceras de seguridad y CSP
- Las traducciones
- Para recordar
- Para saber más
- Preguntas frecuentes
La arquitectura de Éclair se resume en una regla: hacer trabajar a PHP solo cuando hace falta. Esta lección sigue una petición de principio a fin, del servidor web a la caché estática, y muestra cómo se guardan los datos sin base de datos y qué clase hace cada cosa. Después podrás depurar, personalizar o alojar Éclair sabiendo cómo funciona por dentro.
Lo que sabrás hacer
Seguir una petición
Del servidor web a index.php y, de ahí, a la caché o a la aplicación.
Leer la carpeta data/
Qué contiene cada archivo y cómo está protegido.
Encontrar la clase adecuada
Saber en pocos segundos dónde actuar dentro de app/lib/.
¿Cómo atraviesa una petición la arquitectura de Éclair?

Todo parte de una constatación: la mayoría de las visitas piden una página que no ha cambiado. Por eso la arquitectura de Éclair la devuelve tal cual, sin leer ni un solo contenido. Este es el recorrido de una petición.
- El servidor web. En Apache o LiteSpeed, el
.htaccessenvía aindex.phpcualquier dirección que no sea un archivo existente. Responde 404 paraapp/,data/,cache/, los archivos ocultos (salvo.well-known) y cualquier.phpdistinto deindex.php, y transmite la cabeceraAuthorizational conector MCP. - La vía rápida de index.php. Para un GET o un HEAD,
index.phpleecache/runtime.phpy busca encache/pages/la página guardada con elmd5de la ruta. Si la encuentra, devuelve sus cabeceras guardadas, su ETag yX-Cache: HIT, o un304 Not Modifiedsi el navegador ya tiene esa versión. - El arranque. Si no,
app/bootstrap.phpcomprueba la versión de PHP (8.1 como mínimo), cargaeclair-config.phpsi existe, defineECLAIR_DATAyECLAIR_DEBUG, registra la carga automática de las clasesEclair\…y llama aEclair\App::run(). - El enrutamiento.
App::run()instala la gestión de errores y entrega la petición al módulo adecuado (tabla de abajo). - El renderizado.
Site::handle()solo acepta GET y HEAD, impone una sola dirección por contenido (barra final, mayúsculas,index.php: redirección 301), sirve los archivos generados (robots.txt,sitemap.xml,llms.txt, feeds, versiones.md) y después busca el contenido.Cache::put()guarda cada página renderizada con el código 200.
| Ruta solicitada | Módulo que responde |
|---|---|
/__eclair/ping | comprobación de la reescritura, prueba de identidad (Loopback) |
| cualquier ruta, antes de la instalación | Installer::handle() |
| la dirección de la administración y sus subpáginas | Admin::handle() |
/_eclair/contact | Contact::handle(), el formulario de contacto |
/_eclair/edit/<id> | redirección al editor, si hay una sesión iniciada |
/_eclair/… y /.well-known/… | Mcp::handle(), mudo mientras el conector esté desactivado |
| todo lo demás | Site::handle() |
La búsqueda, la página 404, la vista previa de la apariencia, el modo Visual y la administración nunca se guardan en caché. Para ver la vía rápida en acción, consulta dos veces la misma página:
# 1.ª llamada: la aplicación genera la página y la guarda
curl -sI https://exemple.fr/contact | grep -i -E 'etag|x-cache'
# ETag: "0745734655e7d523"
# 2.ª llamada: index.php la sirve desde cache/pages/, sin cargar la aplicación
curl -sI https://exemple.fr/contact | grep -i -E 'etag|x-cache'
# ETag: "0745734655e7d523"
# X-Cache: HIT
# El archivo de caché lleva el md5 de la ruta solicitada
php -r 'echo md5("/contact"), PHP_EOL;' # 90609cfd31a32bdbd3d81843c57d356b
El almacenamiento en archivos planos: la clase Store
La arquitectura de Éclair prescinde de la base de datos: cada dato es un archivo data/<nom>.php que empieza por la línea <?php exit; ?>, seguida de JSON legible. Si se llama desde la web, no devuelve nada, incluso en un servidor que ignorara los .htaccess. Este es el principio de un archivo de menús real:
<?php exit; ?>
{
"main": [
{
"type": "content",
"id": "af59e5ba0a",
"label": "",
"url": ""
}
]
}
Cuatro mecanismos hacen que este almacenamiento sea seguro, incluso en un hosting compartido muy cargado:
- Escritura atómica:
Store::atomic()escribe un archivo temporal y después lo renombra, de modo que un lector nunca ve un archivo a medio escribir. Los archivos reciben los permisos 0600, y las carpetas dedata/ycache/, 0700. - Ningún código inyectable:
Store::write()codifica el JSON conJSON_HEX_TAGyJSON_HEX_AMP. Un<se convierte en\u003C: la secuencia<?phpno puede aparecer en los datos. - Bloqueo:
Store::locked()pone un bloqueo exclusivo (flock) sobredata/.lockdurante cada operación de lectura, modificación y escritura. - Nombres controlados:
Store::file()solo acepta minúsculas, números, guiones y barras. Es imposible salir dedata/.
Todas las clases que escriben siguen este patrón, por ejemplo Redirects::add():
use Eclair\Store;
$menus = Store::read('menus', []); // lectura, con un valor por defecto
Store::locked(function (): void { // leer, modificar y escribir sin condiciones de carrera
$list = Store::read('redirects', []);
// … modificar $list …
Store::write('redirects', $list); // archivo temporal, 0600, renombrado
});
Los archivos de la carpeta data/
La última columna indica lo que conserva la copia de seguridad ZIP de la página Seguridad: los secretos y los datos temporales quedan fuera.
| Archivo o carpeta | Contenido | Copia ZIP |
|---|---|---|
site.php | ajustes, combinados con Settings::DEFAULTS | sí |
users.php | cuentas, hashes Argon2id, verificación en dos pasos | sí |
keys.php | secreto del sitio y clave de IndexNow, creados en la instalación | sí |
index.php | resumen de cada contenido, para listar y enrutar rápido | sí |
content/<id>.php | una página, un artículo o un producto, con sus bloques | sí |
revisions/<id>/ | las 20 últimas versiones de ese contenido | sí |
search.php, media.php | texto de búsqueda, registro de la biblioteca de medios | sí |
menus.php, categories.php, redirects.php, custom-css.php | menús, categorías, redirecciones, CSS personalizado | sí |
messages/, inbox.php | mensajes del formulario, 500 como máximo | sí |
logs/ | registros de errores y de seguridad, limitados a 1 MB | sí |
vault.php | claves de API cifradas | no |
ai/ | conversaciones del asistente, propuestas, conexiones MCP | no |
sessions/, limits/ | sesiones de la administración, contadores contra la fuerza bruta | no |
Las actualizaciones nunca tocan data/, que la constante ECLAIR_DATA puede incluso situar fuera de la carpeta pública.
Las clases principales de app/lib/
Todas las clases viven en el espacio de nombres Eclair\, un archivo por clase, sin framework ni Composer:
| Clase | Función |
|---|---|
App | enrutamiento de las peticiones que no sirve la caché |
Req, Http | lectura de la petición, respuestas y redirecciones |
Store, Settings | almacenamiento en archivos planos, ajustes y valores por defecto |
Content, Blocks | contenidos, índice, revisiones; renderizado de los 18 bloques |
Site, Theme | enrutador público; plantillas y CSS en línea |
Cache | páginas estáticas y cache/runtime.php |
Seo, Analyzer | datos estructurados, sitemap, llms.txt; puntuaciones |
Security, Session, Auth | cabeceras, tokens CSRF, sesiones, inicio de sesión |
Media | biblioteca de medios, recodificación y WebP |
Admin y Admin… | pantallas y acciones de la administración |
Ai, AiChat, AiTools, Mcp | asistente IA, herramientas, conector MCP y OAuth |
Integrity, Log, Keys, Vault, Loopback | integridad, registros, secretos, caja fuerte, verificación de la dirección |
I18n | traducciones de la administración |
¿Por qué se vacía la caché con cada cambio?
Un título modificado aparece también en un menú, en una tarjeta de artículo o en las migas de pan. En lugar de adivinar esas dependencias, la arquitectura de Éclair lo vacía todo: Cache::purge() se ejecuta tras cada guardado de un contenido, de los ajustes, los menús, las categorías, los medios, un perfil o el CSS personalizado. Borra los archivos de cache/pages/ y reescribe cache/runtime.php; cada página se regenera en su primera visita.
<?php exit; ?>
{"on":true,"expires":0,"host":"exemple.fr","https":true}
index.php lee este pequeño archivo en cada petición:
on: caché activa (Caché estática de páginas marcada y modo de depuración apagado);expires: fecha del próximo artículo programado; a partir de ellaindex.phpignora la caché, que vacíaCache::checkExpiry();hostyhttps: host y protocolo oficiales, una vez verificada la dirección, para no saltarse nunca las redirecciones.
Cabeceras de seguridad y CSP
Security::headers() produce tres juegos de cabeceras: site, admin y text (sitemap, llms.txt, feeds). Dependen de los ajustes, nunca de la petición: nadie puede envenenar la caché con una cabecera falsificada. Estas son las de una página pública servida por HTTP, tomadas de Éclair 1.8:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'sha256-…'; img-src 'self' data: https:; media-src 'self' https:; frame-src https://www.youtube-nocookie.com https://player.vimeo.com; connect-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), browsing-topics=()
Cross-Origin-Opener-Policy: same-origin
Theme::hash() calcula la huella sha256 sobre el CSS insertado en la página: solo se autoriza esa etiqueta <style>, no se permite ningún script en línea y el único script público es assets/site/site.js. Con HTTPS se añaden upgrade-insecure-requests y, si la opción HSTS está marcada, Strict-Transport-Security. La administración es aún más estricta: ningún estilo en línea, frame-ancestors 'none', noindex y Cache-Control: no-store, private.
Las traducciones
- Administración: cada texto se escribe en francés en el código, con
t().I18n::t()busca su traducción enapp/lang/<langue>.json.gz, compilado portools/i18n.phpe indexado por la huella CRC32 del texto francés. Sin la extensión zlib, la administración se queda en francés. - Sitio público:
Theme::t('clé', $lang)lee el arrayTheme::STRINGS(«Índice», «Lo esencial», «Leer en»…) en seis idiomas, yTheme::words()da las palabras de las direcciones (página, autor, búsqueda, tienda) en el idioma del sitio.
Para recordar
- En la arquitectura de Éclair,
index.phpsirve una página ya generada desdecache/pages/sin cargar la aplicación:X-Cache: HITlo demuestra. - Lo demás pasa por
app/bootstrap.phpy después porApp::run(), que lo encamina al instalador, la administración, el contacto, el MCP o el sitio. - Los datos son archivos JSON detrás de
<?php exit; ?>, escritos de forma atómica, con permisos 0600 y con bloqueo. - La caché se vacía por completo con cada cambio y se reconstruye en la visita siguiente.
- Las cabeceras de seguridad solo dependen de los ajustes: la caché no puede envenenarse.
Para saber más
- Desarrollo local y pruebas: ejecutar Éclair en tu máquina y repetir sus 550 comprobaciones.
- Personalizar las plantillas del tema: actuar sobre el renderizado sin romper la CSP.
- Nginx, VPS, proxy y CDN: reproducir las reglas del
.htaccessfuera de Apache. - Seguridad avanzada y mantenimiento: integridad, copias de seguridad y actualizaciones.
- Documentación de PHP: flock() y rename(), en el corazón de las escrituras de
Store. - OWASP: hoja de referencia sobre la Content Security Policy, en inglés.
Preguntas frecuentes
¿Puede funcionar Éclair sin su caché estática?
Sí: desmarca Caché estática de páginas en Ajustes › Avanzado, o activa el modo de depuración. Cada visita genera entonces la página, lo que consume más tiempo de servidor.
¿Por qué guardar JSON en archivos .php?
Para que la línea <?php exit; ?> proteja cada archivo aunque el servidor ignore el .htaccess. Si se llama desde la web, no muestra nada, mientras que Éclair lee el JSON que sigue a esa primera línea.
¿Qué pasa si dos personas guardan a la vez?
Las escrituras pasan por Store::locked(), que pone un bloqueo exclusivo sobre data/.lock. Los dos guardados se ejecutan uno detrás de otro, y la versión anterior pasa al historial de revisiones.
¿Se puede colocar la carpeta data/ fuera de la carpeta pública?
Sí. Primero mueve la carpeta y después indica su nueva ubicación con define('ECLAIR_DATA', '/chemin/vers/eclair-data'); en eclair-config.php, junto a index.php. La vía rápida no depende de ella: solo lee cache/.