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:
154
src/Libs/Synapsis.php
Normal file
154
src/Libs/Synapsis.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user