dlunire/dlroute

Sistema avanzado de enrutamiento HTTP impulsado por autómatas finitos. Altamente eficiente, flexible y de alto rendimiento. Úsalo como motor central de DLUnire o intégralo fácilmente en cualquier proyecto PHP.

Maintainers

Package info

github.com/dlunire/dlroute

pkg:composer/dlunire/dlroute

Transparency log

Fund package maintenance!

dlunire

Statistics

Installs: 279

Dependents: 1

Suggesters: 0

Stars: 2

Open Issues: 0

v2.0.1 2026-07-08 21:42 UTC

This package is auto-updated.

Last update: 2026-07-08 21:43:16 UTC


README

✨ Sponsors

Gracias a las siguientes personas y empresas por apoyar el desarrollo de DLRoute y la futura v2.0.0 (autómatas puros).

Patrocinadores Activos

¿Quieres formar parte de esto?

❤️ Patrocíname en GitHub Sponsors

DLRoute no es "otro enrutador PHP más". Es un pipeline de enrutamiento construido sobre la teoría de lenguajes formales —los mismos fundamentos utilizados en los compiladores— aplicada por primera vez al despacho de peticiones HTTP en PHP.

composer require dlunire/dlroute

Requiere PHP 8.2+. Funciona con cualquier proyecto PHP —con o sin framework—.

Por qué DLRoute es diferente

Cualquier otro enrutador de PHP —FastRoute, Symfony Routing, Laravel Router— fue construido en torno a un único objetivo: mapear URLs a controladores lo más rápido posible. El emparejamiento (matching) era el problema; todo lo demás era secundario.

DLRoute se diseñó bajo una premisa distinta: el enrutamiento es un pipeline de procesamiento formal, no una tabla de búsqueda.

Esa premisa produce una arquitectura que no existe en ningún otro enrutador de PHP.

Lo que ningún otro enrutador de PHP hace

1. Parser de querystring mediante autómata finito

Todos los demás enrutadores utilizan parse_str(), una función que existe desde PHP 4.

DLRoute la reemplaza con un autómata finito que procesa la cadena de consulta byte por byte en una sola pasada, con estados explícitos (QUERY_NAMEQUERY_VALUE), emitiendo DTOs tipados e mutables con el desplazamiento exacto en bytes (offset) de cada token en la cadena original.

// GET /?campo=valor&activo
$params = (new QueryParamComposer())->get_query_params();

$params['campo']->value;         // "valor"
$params['campo']->offset;        // 0   — posición en bytes del nombre
$params['campo']->offset_value;  // 6   — posición en bytes del valor
$params['campo']->length;        // 5

$params['activo']->value;        // null — parámetro sin valor

Ningún otro enrutador de PHP expone metadatos de posición a nivel de byte para los parámetros de la querystring.

2. Lexer de sintaxis de rutas con diagnóstico de posición exacta

Cuando defines una ruta con una sintaxis inválida, DLRoute no lanza una excepción genérica. El RouterLexer analiza la definición de la ruta carácter por carácter y emite un diagnóstico completamente accionable:

// Ruta inválida
DLRoute::get('/{ciencia?=algo}/users', fn() => []);
RouteException: Expected closing brace (}) after «?» (position 9).
Received instead: «?=algo}/users».
Optional parameters must follow the format → «{param?}»
Route defined: «/{ciencia?=algo}/users»

Compara esto con lo que hace Laravel ante un método HTTP inválido:

Laravel → Una página silenciosa 404 HTML

DLRoute → JSON estructurado con el error exacto, archivo, línea y traza de la pila (stack trace)

Esa es la diferencia entre un sistema con contratos formales y uno sin ellos.

3. Telemetría como ciudadano de primera clase en el núcleo

TelemetryRequest reside en DLRoute\Core\Telemetry —no es un middleware, no es un plugin—. Fue diseñado desde el inicio como parte del motor.

DLRoute::get('/{resource?}', function() {
    return TelemetryRequest::telemetry("Mi API");
});
{
    "message":     "Mi API",
    "route":       "/api/users",
    "uri":         "/api/users?filter=active",
    "base_url":    "[https://mi-dominio.com](https://mi-dominio.com)",
    "domain":      "mi-dominio.com",
    "is_https":    true,
    "port":        443,
    "local_port":  80,
    "timestamp":   "2026-06-18T01:20:47+00:00",
    "cliente_ip":  "203.0.113.1",
    "method":      "GET",
    "proxy":       true,
    "query_param": {
        "filter": {
            "name":         "filter",
            "offset":       0,
            "value":        "active",
            "offset_value": 7,
            "length":       6
        }
    }
}

Una sola llamada. Cero configuración. Funciona correctamente detrás de Cloudflare, proxies inversos de Nginx y túneles, diferenciando automáticamente el port (de cara al cliente) del local_port (puerto interno del servidor).

Para lograr un resultado equivalente en Laravel necesitas: Telescope + configuración de proxies de confianza + un paquete de logging externo.

4. Contratos tipados en el registro de rutas

Methods::GET es un enum, no una cadena de texto. El enrutador valida el tipo antes de registrar la ruta. Si pasas algo inválido, falla inmediatamente con un error JSON estructurado.

// ❌ Incorrecto
DLRoute::match(['david'], new RouteHandler(...));

// ✅ Correcto
DLRoute::match([Methods::GET, Methods::POST], new RouteHandler(...));
{
    "status": false,
    "error": "DLRoute::match: Expected «DLRoute\\Enums\\Methods». Received «david» instead.",
    "details": { "filename": "...", "line": 200 }
}

Laravel responde silenciosamente con un 404 HTML ante la misma entrada.

5. Detección de subdirectorios sin configuración

DLRoute calcula la ruta real de la petición mediante aritmética de posición de bytes —sin str_replace(), sin expresiones regulares—:

OFFSET = LENGTH(dir) - 1
route  = substr(uri, OFFSET)

Determinista y O(1), independientemente de si el nombre del subdirectorio aparece repetido en la URI.

{
    "route":    "/api/products",
    "uri":      "/subdir/subdir/api/products",
    "dir":      "/subdir/subdir",
    "base_url": "[https://example.com/subdir/subdir](https://example.com/subdir/subdir)"
}

Comparativa de características

Capacidad DLRoute FastRoute Symfony Router Laravel Router
Parser de querystring por autómata finito
Metadatos de posición de token a nivel de byte
Lexer de sintaxis de rutas con diagnósticos
Posición exacta en bytes en errores de sintaxis
Telemetría nativa en el núcleo
Errores JSON estructurados
Contratos de métodos HTTP tipados (enum)
Detección de subdirectorios sin configuración
Respuesta JSON automática desde un array
Parámetros opcionales de forma nativa solución alternativa
Tipo MIME explícito por ruta
Cero dependencias externas

Inicio rápido

1. Estructura del proyecto

mi-proyecto/
├── public/
│   └── index.php
├── app/
│   └── Controllers/
│       └── ApiController.php
└── vendor/

2. Punto de entrada

<?php
declare(strict_types=1);

use DLRoute\Requests\DLRoute;

require dirname(__DIR__) . '/vendor/autoload.php';

// Define las rutas aquí

DLRoute::execute();

3. Ruta básica

DLRoute::get('/', fn() => ['status' => 'ok']);

Los arrays y objetos se serializan automáticamente como JSON con el encabezado Content-Type correcto.

4. Ruta con parámetro tipado

DLRoute::get('/api/{id}', function(object $params) {
    return ['id' => $params->id];
})->filter_by_type(['id' => 'integer']);

Si {id} no es un entero, DLRoute responde automáticamente con un 404. No se requiere código adicional.

5. Parámetro opcional

// Registra tanto /products como /products/{uuid}/detail simultáneamente
DLRoute::get('/products/{uuid?}/detail', [ProductController::class, 'show'])
    ->filter_by_type(['uuid' => 'uuid']);

6. Múltiples métodos HTTP

use DLRoute\Core\Data\RouteHandler;
use DLRoute\Enums\Methods;

DLRoute::match(
    [Methods::GET, Methods::POST],
    new RouteHandler(
        uri:             '/api/{uuid}',
        controller:      [ApiController::class, 'handle'],
        mime_type:       'application/json',
        handler_filters: ['uuid' => 'uuid'],
    )
);

7. Tipos de parámetros soportados

Tipo Descripción
string Cualquier cadena de texto
uuid Formato UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
email Dirección de correo electrónico válida
integer Número entero
float Número decimal
numeric Número con o sin decimales
boolean Valor booleano

Expresión regular personalizada:

->filter_by_type(['token' => '/^[a-f0-9]{64}$/'])

Métodos HTTP soportados

GET · HEAD · POST · PUT · PATCH · DELETE · OPTIONS

Tutorial de uso

Guía progresiva en español (16 capítulos): docs/tutorial/README.md

# Tema
1 Inicio rápido
2 Ciclo de despacho
3 DLServer y contexto
4 Registro de rutas y controladores
5 Parámetros dinámicos
6 filter_by_type()
7 match() y RouteHandler
8 DLOutput y respuestas
9 Controladores y peticiones
10 Query string y autómata
11 Router y telemetría
12 Subida de archivos
13 Peticiones salientes
14 Errores y diagnósticos
15 Despliegue en producción
16 Integración con DLCore

Referencia por módulo: docs/README.md. Kernel DLUnire: tutorial DLCore.

Parte del ecosistema DLUnire

DLRoute es el motor de enrutamiento de DLUnire —un framework moderno de PHP para construir aplicaciones web orientadas a APIs de forma rápida y con rigor formal—.

Apoya este proyecto

DLRoute tiene licencia AGPL-3.0 y es gratuito para siempre.

Si tu empresa depende de la infraestructura PHP y valora la corrección formal por encima de la magia de las convenciones, considera convertirte en patrocinador:

Disponemos de niveles de patrocinio corporativo con colocación de logotipo, respuesta prioritaria a incidencias y consultoría de arquitectura. Contacto: dlunireframework@gmail.com

Autor

David E Luna M — Creador y desarrollador principal de DLUnire

Licencia

GNU Affero General Public License v3.0 (AGPL-3.0)