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:
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Libs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Middleware - DuckBrain
|
|
||||||
*
|
|
||||||
* Librería base para middlewares.
|
|
||||||
*
|
|
||||||
* @author KJ
|
|
||||||
* @website https://kj2.me
|
|
||||||
* @licence MIT
|
|
||||||
*/
|
|
||||||
class Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Llama al siguiente callback.
|
|
||||||
*
|
|
||||||
* @param Neuron $req
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public static function next(Neuron $req): mixed
|
|
||||||
{
|
|
||||||
$next = array_pop($req->next);
|
|
||||||
return call_user_func_array($next, [$req]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,6 @@ class Request extends Neuron
|
|||||||
public string $path;
|
public string $path;
|
||||||
public string $error;
|
public string $error;
|
||||||
public string $body;
|
public string $body;
|
||||||
public array $next;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __construct
|
* __construct
|
||||||
@@ -39,6 +38,7 @@ class Request extends Neuron
|
|||||||
$this->put = new Neuron();
|
$this->put = new Neuron();
|
||||||
$this->patch = new Neuron();
|
$this->patch = new Neuron();
|
||||||
$this->delete = new Neuron();
|
$this->delete = new Neuron();
|
||||||
|
$this->params = new Neuron(Router::$currentParams);
|
||||||
$this->body = file_get_contents("php://input");
|
$this->body = file_get_contents("php://input");
|
||||||
|
|
||||||
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
||||||
@@ -60,21 +60,10 @@ class Request extends Neuron
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->params = new Neuron();
|
// Corremos las validaciones configuradas
|
||||||
}
|
if (!$this->validate()) {
|
||||||
|
exit();
|
||||||
/**
|
|
||||||
* Corre las validaciones e intenta continuar con la pila de callbacks.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle(): mixed
|
|
||||||
{
|
|
||||||
if ($this->validate()) {
|
|
||||||
return Middleware::next($this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class Router
|
|||||||
private static $patch = [];
|
private static $patch = [];
|
||||||
private static $delete = [];
|
private static $delete = [];
|
||||||
private static $last;
|
private static $last;
|
||||||
|
public static $currentParams = [];
|
||||||
public static $notFoundCallback = 'Libs\Router::defaultNotFound';
|
public static $notFoundCallback = 'Libs\Router::defaultNotFound';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -325,55 +326,18 @@ class Router
|
|||||||
if (preg_match_all('/^' . $router['path'] . '\/?$/si', $path, $matches, PREG_PATTERN_ORDER)) {
|
if (preg_match_all('/^' . $router['path'] . '\/?$/si', $path, $matches, PREG_PATTERN_ORDER)) {
|
||||||
unset($matches[0]);
|
unset($matches[0]);
|
||||||
|
|
||||||
// Objtener un reflection del callback
|
|
||||||
$lastCallback = $router['callback'][0];
|
|
||||||
if ($lastCallback instanceof \Closure) { // si es función anónima
|
|
||||||
$reflectionCallback = new \ReflectionFunction($lastCallback);
|
|
||||||
} else {
|
|
||||||
if (is_string($lastCallback)) {
|
|
||||||
$lastCallback = preg_split('/::/', $lastCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revisamos su es un método o solo una función
|
|
||||||
if (count($lastCallback) == 2) {
|
|
||||||
$reflectionCallback = new \ReflectionMethod($lastCallback[0], $lastCallback[1]);
|
|
||||||
} else {
|
|
||||||
$reflectionCallback = new \ReflectionFunction($lastCallback[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtener los parámetros
|
|
||||||
$arguments = $reflectionCallback->getParameters();
|
|
||||||
if (isset($arguments[0])) {
|
|
||||||
// Obtenemos la clase del primer parámetro
|
|
||||||
$argumentClass = strval($arguments[0]->getType());
|
|
||||||
|
|
||||||
// Verificamos si la clase está o no tipada
|
|
||||||
if (empty($argumentClass)) {
|
|
||||||
$request = new Request();
|
|
||||||
} else {
|
|
||||||
$request = new $argumentClass();
|
|
||||||
|
|
||||||
// Verificamos que sea instancia de Request (requerimiento)
|
|
||||||
if (!($request instanceof Request)) {
|
|
||||||
throw new \Exception('Bad argument type on router callback.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$request = new Request();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprobando y guardando los parámetros variables de la ruta
|
// Comprobando y guardando los parámetros variables de la ruta
|
||||||
if (isset($matches[1])) {
|
if (isset($matches[1])) {
|
||||||
foreach ($matches as $index => $match) {
|
foreach ($matches as $index => $match) {
|
||||||
$paramName = $router['paramNames'][$index - 1];
|
$paramName = $router['paramNames'][$index - 1];
|
||||||
$request->params->$paramName = urldecode($match[0]);
|
static::$currentParams[$paramName] = urldecode($match[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Llama a la validación y luego procesa la cola de callbacks
|
// Procesa la cola de callbacks
|
||||||
$request->next = $router['callback'];
|
foreach (array_reverse($router['callback']) as $callback) {
|
||||||
$data = $request->handle();
|
$data = Synapsis::resolve($callback);
|
||||||
|
}
|
||||||
|
|
||||||
// Por defecto imprime como JSON si se retorna algo
|
// Por defecto imprime como JSON si se retorna algo
|
||||||
if (isset($data)) {
|
if (isset($data)) {
|
||||||
@@ -386,6 +350,6 @@ class Router
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Si no hay router que coincida llamamos a $notFoundCallBack
|
// Si no hay router que coincida llamamos a $notFoundCallBack
|
||||||
call_user_func_array(static::$notFoundCallback, [new Request()]);
|
Synapsis::resolve(static::$notFoundCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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