Documentación de Leviatan Framework

Guía detallada para usar el proyecto base de forma correcta: convenciones, comandos y buenas prácticas.

Introducción

Leviatan es un esqueleto de aplicación PHP con un núcleo reusable en framework/, código de producto en app/ y front controller en public/index.php. Integra AltoRouter para rutas, Doctrine DBAL + ORM 3 para datos, Smarty 5 para plantillas y una pila PSR-7 / PSR-15 (Nyholm PSR-7 + middlewares propios).

El objetivo es predecibilidad: pocos conceptos, nombres claros y documentación que puedas seguir sin adivinar comportamientos ocultos.

Requisitos

Instalación

composer install
cp .env.example .env

Opcional: ajusta DATABASE_URL en .env. Si la omites, el proyecto usa SQLite en var/app.db (crea el directorio var/ con permisos de escritura).

Aplica migraciones cuando toque una base nueva:

composer migrate

Servidor de desarrollo:

composer serve

URLs útiles: / (landing), /documentacion (este manual), /consola (panel técnico de ejemplo), /showcase, /health (JSON).

Estructura del proyecto

Bootstrap

Cada front controller hace require de config/bootstrap.php, que devuelve una instancia de Leviatan\Framework\Bootstrap\Application ya configurada con servicios (db, em, view, logger, kernel, etc.).

Variables de entorno relevantes

Enrutado

Las rutas HTTP se registran en app/config/routes.web.php con el router inyectado:

$router->map('GET', '/ruta', [MiController::class, 'metodo'], 'nombre_opcional');

El primer patrón que coincida gana. Para APIs REST suele bastar con métodos y paths explícitos; consulta la documentación de AltoRouter si necesitas parámetros nombrados en el path.

Controladores

Los controladores son clases en app/Controllers/. El método de acción recibe Psr\Http\Message\ServerRequestInterface $request y array $params (parámetros de ruta).

Pueden devolver una cadena HTML (vista) o un objeto Psr\Http\Message\ResponseInterface (por ejemplo JSON; usa HttpResponseFactory si conviene).

El contenedor League Container con ReflectionContainer permite inyectar dependencias en el constructor (ViewRendererInterface, EntityManager, HealthService, etc.) sin registrar cada clase manualmente.

Inyección de dependencias

En config/bootstrap.php se registran servicios compartidos: Connection, EntityManager, Application, LoggerInterface, etc. Los controladores se resuelven con $container->get(Clase::class) cuando el contenedor puede construirlos.

Vistas Smarty

Las plantillas viven en app/Views/. La clase correcta es Smarty\Smarty (Smarty 5). Cualquier bloque CSS con llaves { } debe ir dentro de ... para no chocar con la sintaxis Smarty.

Asigna variables desde el controlador con el array pasado a render(); en la plantilla usa salvo que confíes al 100% en el origen del dato.

Base de datos (DBAL)

Obtén la conexión con $app->get('db') o inyectando Doctrine\DBAL\Connection. Usa siempre consultas parametrizadas; no concatenes entrada de usuario en SQL crudo.

ORM y entidades

Las entidades están en app/Entity/ con atributos PHP de Doctrine ORM 3. El EntityManager está disponible como $app->get('em') o por inyección. Los repositorios personalizados van en app/Repository/ y se referencian en #[Entity(repositoryClass: ...)].

Herramienta web de modelos

La ruta /herramienta/modelos (requiere contexto WEB y proyecto ya instalado) abre un asistente con formularios para definir entidades (estilo Doctrine del proyecto), repositorios y, si lo indicas, una migración y su aplicación. Opcionalmente puedes pegar un JSON declarativo; las FK referencian otras clases del mismo envío. En el primer acceso debes crear usuario y contraseña (se guardan en var/model_tool_auth.json; no versionar). Vuelve a entrar cuando quieras; el acceso está protegido por sesión tras autenticarte.

Migraciones

Configuración en config/cli-config.php. Comando habitual: composer migrate. Tras cambiar el esquema, genera y revisa migraciones antes de desplegar en entornos compartidos.

HTTP y middlewares

El HttpKernel envuelve la aplicación con middlewares en orden: identificación de petición, manejo de errores (con logs), cabeceras de seguridad, sesión y CSRF en métodos que alteran estado.

Application::handleRequest() trabaja con ServerRequestInterface y devuelve ResponseInterface; el último middleware interno es ApplicationRequestHandler.

Sesión y CSRF

Formularios que muten datos (POST, PUT, PATCH, DELETE) deben incluir el token CSRF que expone la aplicación tras abrir sesión (campo _csrf o cabecera equivalente). Configura SESSION_SECURE=true cuando sirvas solo por HTTPS.

Logging

Usa Psr\Log\LoggerInterface desde el contenedor. En depuración los logs van a stderr; en producción, a var/log/app.log con rotación (o a stderr si no hay permisos de escritura). Las respuestas 500 registran contexto con request_id cuando la petición pasó por el middleware de ID.

Salud y monitorización

GET /health devuelve JSON con status y comprobaciones (p. ej. base de datos). Usa códigos HTTP adecuados (503 si degradado). La cabecera X-Request-ID ayuda a correlacionar logs en soporte.

Tests y calidad

Despliegue

El document root del servidor debe ser solo public/. No expongas vendor/, .env ni var/ por HTTP. Lee docs/production.md y sustituye public/.well-known/security.txt con datos reales de contacto.

Preguntas frecuentes

¿Necesito Docker?

No. Docker es opcional para desarrollo; en servidor puedes usar Apache o Nginx + PHP-FPM como cualquier app PHP.

¿Dónde pongo la lógica de negocio?

En app/ (servicios, modelos de dominio). Mantén framework/ libre de reglas de negocio del producto.

¿Puedo quitar Smarty?

Sí: el bootstrap cae en PhpViewRenderer si Smarty no está disponible; las plantillas .tpl tendrían que migrarse a PHP puro o a otro motor.

¿Algo no cuadra? Revisa AGENTS.md en el repositorio para convenciones orientadas a IA y equipos.