refactor!: add Synapsis for dependency resolution and injection

- Remove Middleware class and custom callback handling logic.
- Implement Synapsis as a dependency injection container for automatic
resolution.
- Refactor Router to use Synapsis for process route callbacks and not found
handler.
- Update Request to remove middleware-specific properties and use
Router::$currentParams for path parameters.
This commit is contained in:
kj
2025-10-10 17:44:49 -03:00
parent b19e7d8789
commit a1a15f492c
4 changed files with 166 additions and 87 deletions

154
src/Libs/Synapsis.php Normal file
View File

@@ -0,0 +1,154 @@
<?php
namespace Libs;
use Closure;
use Exception;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
use ReflectionParameter;
/**
* Synapsis - DuckBrain
*
* Clase encargada de la resolución e inyección de dependencias
*
* @author KJ
* @website https://kj2.me
* @licence MIT
*/
class Synapsis
{
/**
* @var array<class-string, class-string> $bindings
*/
private static array $bindings = [];
/**
* @var array<class-string, mixed> $instances
*/
private static array $instances = [];
/**
* Asocia una interface a una clase para instanciarla en su lugar.
*
* @param class-string $interfaceName
* @param class-string $className
*
* @return void
*/
public static function bind(string $interfaceName, string $className): void
{
if (interface_exists($interfaceName) && class_exists($className)) {
static::$bindings[$interfaceName] = $className;
return;
}
throw new Exception('Error at binding non existant Interface or ClassName.');
}
/**
* Asocia en lote múltiples interfaces con múltiples clases.
*
* @param array<class-string, class-string> $bindings
*
* @return void
*/
public static function bulkBind(array $bindings): void
{
foreach ($bindings as $interfaceName => $className) {
static::bind($interfaceName, $className);
}
}
/**
* Resuelve e inyecta las dependencias de un callable y devuelve su resultado.
*
* @param callable $action
*
* @return mixed
*/
public static function resolve(callable $action): mixed
{
if ($action instanceof Closure) { // si es función anónima
$reflectionCallback = new ReflectionFunction($action);
} else { // Areglo o string con el método
if (is_string($action)) {
$action = preg_split('/::/', $action);
}
// Revisamos su es un método o solo una función
if (count($action) == 2) {
$reflectionCallback = new ReflectionMethod($action[0], $action[1]);
} else {
$reflectionCallback = new ReflectionFunction($action[0]);
}
}
// Obtenemos los parámetros
return call_user_func_array(
$action,
static::resolveParameterValues($reflectionCallback->getParameters())
);
}
/**
* Resuelve e inyecta las dependencias de una clase y devuelve su resultado.
*
* @param class-string $className
*
* @return mixed
*/
public static function &resolveInstance(string $className): mixed
{
if (interface_exists($className)) {
if (isset(static::$bindings[$className])) {
$className = static::$bindings[$className];
} else {
throw new Exception("Missing binding for interface: {$className}");
}
}
if (!class_exists($className)) {
throw new Exception("Cannot resolve: {$className}");
}
if (!isset(static::$instances[$className])) {
// Si la instancia no ha sido resuelta antes, la devolvemos
$reflectionClass = new ReflectionClass($className);
$constructor = $reflectionClass->getConstructor();
static::$instances[$className] = new $className(
...static::resolveParameterValues($constructor->getParameters())
);
}
return static::$instances[$className];
}
/**
* resolveParameterValues
*
* @param array<ReflectionParameter> $parameters
*
* @return array
*/
public static function resolveParameterValues(array $parameters): array
{
$values = [];
foreach ($parameters as $parameter) {
if ($parameter->isOptional()) { // Siempre usamos el valor por defecto primero
$values[] = $parameter->getDefaultValue();
continue;
}
// Intentamos darle un valor equivalente a nulo si es una clase primitiva pare evitar
// errores innecesarios, lo
if ($parameter->getType()->isBuiltin()) {
throw new Exception('Primitive parameters expect at least a default value.');
}
$values[] = static::resolveInstance($parameter->getType()->getName());
}
return $values;
}
}