Addin a lot of changes

This commit is contained in:
2026-08-20 07:49:40 -03:00
parent 795062bb6d
commit 5b6d9553ac
5 changed files with 437 additions and 232 deletions
+131
View File
@@ -0,0 +1,131 @@
<?php
namespace Routes;
use ReflectionMethod;
use ReflectionFunction;
/**
* Object Parameter Spawner
*
*/
class ObjectSpawners
{
/**
* Return the list of available Spawners
*
* @return array{array: callable, callable: callable, string: callable}
*/
public static function getDefaultSpawners(): array
{
return [
"string" => self::classPathSpawner(),
"callable" => self::clousureSpawner(),
"array" => self::arraySpawner()
];
}
/**
* Return the Spawner for classPath
*
* /App/Module/ModuleController:method
*
* @return (callable(array ,Route ,array ):array)
*/
private static function classPathSpawner(): callable
{
return function (array $classPath, Route $route, array $returnParameters = array ()): array {
$controller = new $classPath[0]();
$routeParameters = array_values($route->_params);
$reflectionFunction = new ReflectionMethod($controller::class, $classPath[1]);
$reflectionParameters = $reflectionFunction->getParameters();
foreach ($reflectionParameters as $key => $signatureItem) {
if (!isset($routeParameters[$key])) {
continue;
}
if ($signatureItem->getType()->isBuiltin()) {
$returnParameters[] = $routeParameters[$key];
}
if (isset($routeParameters[$key])) {
$class = $signatureItem->getType()->__tostring();
$returnParameters[] = new $class($routeParameters[$key]);
}
}
return $returnParameters;
};
}
/**
* Return the Spawner for a closuer
*
* RouteCollection::get("*", function(int $id ....) {});
*
* @return (callable(callable ,Route ,array ):array)
*/
private static function clousureSpawner(): callable
{
return function (callable $callback, Route $route, array $returnParameters = array ()): mixed {
$routeParameters = array_values($route->_params);
$reflectionFunction = new ReflectionFunction($callback);
$reflectionParameters = $reflectionFunction->getParameters();
foreach ($reflectionParameters as $key => $signatureItem) {
if (!isset($routeParameters[$key])) {
continue;
}
if ($signatureItem->getType()->isBuiltin()) {
$returnParameters[] = $routeParameters[$key];
}
if (isset($routeParameters[$key])) {
$class = $signatureItem->getType()->__tostring();
$returnParameters[] = new $class($routeParameters[$key]);
}
}
return $returnParameters;
};
}
/**
* Return the Spawner for array definition Laravel Style
*
* RouteCollection::get("*", [ModuleController:class, "method"]);
*
* @return (callable(array ,Route ,array ):array)
*/
private static function arraySpawner(): callable
{
return function (array $array, Route $route, Array $returnParameters = Array()): mixed {
$routeParameters = array_values($route->_params);
$reflectionFunction = new ReflectionMethod($array[0], $array[1]);
$reflectionParameters = $reflectionFunction->getParameters();
foreach ($reflectionParameters as $key => $signatureItem) {
if (!isset($routeParameters[$key])) {
continue;
}
if ($signatureItem->getType()->isBuiltin()) {
$returnParameters[] = $routeParameters[$key];
}
if (isset($routeParameters[$key])) {
$class = $signatureItem->getType()->__tostring();
$returnParameters[] = new $class($routeParameters[$key]);
}
}
return $returnParameters;
};
}
}
+159 -130
View File
@@ -2,55 +2,72 @@
namespace Routes;
use stdClass;
use Clousure;
/**
* Summary of Route
*/
class Route
{
public Array $_verb = Array(); // Route Verbs to match
public string $_source; // Execution Source (HTTP | Cli | SOAP)
public string $_name; // Route Name
public string $_uri; // Route URI
public int $_weight = 0; // Wight of the route, for sorting
public Array $_uriSegments = Array(); // Route Full URI Segments
public Array $_segments = Array(); // Route parsed URI Segments
public Array $_params = Array(); // Prepared Routes parameters
public Array $_before = Array(); // Functions to execute Before the Route
public Array|string $_callback = Array(); // Route Callback
public Array $_after = Array(); // Functions to execute After the Route
public string $_regex = ''; // The URI regex to match
public bool $_block = true; // Will this route block the remaining execution?
/*
* Is there a problem witch one?
*/
public bool $_httpError = false;
public bool $_ignore = false; // Wil the execution of this route be counted. To know 404
public array $_middlewaresToIgnore = array(); // Middlewares this Route will ignore
public array $_tags = array(); // Tags of the Route
public private(set) Array $_verb = Array(); // Route Verbs to match
public private(set) string $_source; // Execution Source (HTTP | Cli | SOAP)
public private(set) string $_name; // Route Name
public private(set) string $_uri; // Route URI
public private(set) int $_weight = 0; // Wight of the route, for sorting
public private(set) Array $_uriSegments = Array(); // Route Full URI Segments
public private(set) Array $_segments = Array(); // Route parsed URI Segments
public private(set) Array $_params = Array(); // Prepared Routes parameters
public private(set) Array $_before = Array(); // Functions to execute Before the Route
public private(set) mixed $_callback; // Route Callback
public private(set) Array $_after = Array(); // Functions to execute After the Route
public private(set) string $_regex = ''; // The URI regex to match
public private(set) bool $_block = true; // Will this route block the remaining execution?
public private(set) bool $_ignore = false; // Wil the execution of this route be counted. To know 404
public private(set) Array $_middlewaresToIgnore = Array(); // Middlewares this Route will ignore
public private(set) Array $_tags = Array(); // Tags of the Route
public private(set) Array $_variableSegments = Array(); // Segments of the route that are variables
// ========
//=======================================================
// Getters and Setters
//
/**
* Set The Route Verb
*
* @param array $verb
* @return Route
*/
function setVerb(Array $verb): Route
{
$this->_verb = $verb;
return $this;
}
/**
* Append a verb to the Route
*
* @param string $verb
* @return Route
*/
function appendVerb(string $verb): Route
{
$this->_verb = array_merge($this->_verb, [$verb]);
return $this;
}
/**
* Set Route Callback
*
* @param mixed $callback
* @return Route
*/
function setCallback(mixed $callback): Route
{
$this->_callback = $callback;
return $this;
}
/**
* Set the name of this Route
*
@@ -144,72 +161,78 @@ class Route
return $this;
}
//=======================================================
// Middlewares
//
/**
* Add a middleware for this route
*
* @param string $name Name of the Middleware
* @param Array $params Array of parametrs to pass on the function
*
* @return Route
*/
function middlewareAdd(string $name, array $params = []): Route
{
$this->_before[$name] = $params;
return $this;
}
/**
* Middlewares this Route will ignore
*
* @param mixed $name
* @return Route
*/
function middlewareIgnore(string $name): Route
{
$this->_middlewaresToIgnore[] = $name;
return $this;
}
/**
* Overwrite Middlewares
*
* @param array $middlewares
* @return Route
*/
function setMiddlewares(Array $middlewares): Route
{
$this->_before = $middlewares;
return $this;
}
//=======================================================
// Route Functionality
//
/**
* Execute the route
* Set Variables Segments to pass as function args
*
* @return mixed
* @param string $segment
* @return void
*/
function execute(): mixed
function registerVariableSegment(string $segment): void
{
$routeReturn = "";
//Middlewares with function or class method
foreach ($this->_before as $key => $before) {
if (!in_array($key, $this->_middlewaresToIgnore) && !$this->_ignore) {
if (is_string($before)) {
$before = explode('@', $before);
$class = new $before[0]();
$class->{$before[1]}();
} else if (is_array($before)) {
call_user_func($before['callback'], $before['params']);
} else {
call_user_func($before);
}
}
if($segment == ""){
return;
}
foreach ($this->_callback as $callback) {
if (is_string($callback)) {
$segments = explode('@', $callback);
$class = new $segments[0]();
$segment = str_replace("]", "", str_replace("[", "", $segment));
$segments = explode(":", $segment);
$r = new \ReflectionMethod($segments[0], $segments[1]);
$firstType = count($r->getParameters()) > 0 ? $r->getParameters()[0]->getType() : null;
if ($firstType instanceof \ReflectionNamedType && !$firstType->isBuiltin()) {
$element = $firstType->getName();
$element = new $element;
if (isset($this->_params['id'])) {
$element->load($this->_params['id']);
}
$this->_params['id'] = $element;
}
$routeReturn = $class->{$segments[1]}(...array_values($this->_params));
} else {
$r = new \ReflectionFunction($callback);
$firstType = count($r->getParameters()) > 0 ? $r->getParameters()[0]->getType() : null;
if ($firstType instanceof \ReflectionNamedType && !$firstType->isBuiltin()) {
$element = $firstType->getName();
$element = new $element;
if (isset($this->_params['id'])) {
$element->load($this->_params['id']);
}
$this->_params['id'] = $element;
}
$routeReturn = call_user_func_array($callback, $this->_params);
}
if(sizeof($segments) == 1){
return;
}
foreach ($this->_after as $after) {
call_user_func($after);
switch ($segments[0]) {
case 'i':
$this->_variableSegments[] = ["name" => $segments[1], "type" => "int"];
break;
default:
$this->_variableSegments[] = ["name" => $segments[1], "type" => "string"];
}
return $routeReturn;
}
/**
@@ -222,6 +245,8 @@ class Route
$this->_segments = explode('/', $this->_uri);
foreach ($this->_segments as $segment) {
$this->registerVariableSegment($segment);
if (strpos($segment, 'i:') > -1) {
$this->_regex .= "\/[0-9]+";
} else if (strpos($segment, 'h:') > -1) {
@@ -256,46 +281,6 @@ class Route
}
}
/**
* Add a middleware for this route
*
* @param string $name Name of the Middleware
* @param Array $params Array of parametrs to pass on the function
*
* @return Route
*/
function middlewareAdd(string $name, array $params): Route
{
$this->_before[$name] = $params;
return $this;
}
/**
* Middlewares this Route will ignore
*
* @param mixed $name
* @return Route
*/
function middlewareIgnore(string $name): Route
{
$this->_middlewaresToIgnore[] = $name;
return $this;
}
function setHttpError($code = 400, $message = null)
{
if ($message == null) {
$message = self::$httpMessages[$code];
}
$info = new stdClass();
$info->code = $code;
$info->message = $message;
$this->_httpError = $info;
return $this;
}
/**
* Check if the URI match the Route Signature
*
@@ -337,4 +322,48 @@ class Route
];
}
/**
* Execute the route
*
* @return mixed
*/
function execute(mixed $routeReturn = ""): mixed
{
//Middlewares with function or class method
foreach ($this->_before as $key => $before) {
if (!in_array($key, $this->_middlewaresToIgnore) && !$this->_ignore) {
if (is_string($before)) {
$before = explode('@', $before);
$class = new $before[0]();
$class->{$before[1]}();
} else if (is_array($before)) {
call_user_func($before['callback'], $before['params']);
} else {
call_user_func($before);
}
}
}
if (is_string($this->_callback)) {
$segments = explode('@', $this->_callback);
$class = new $segments[0]();
$parameters = RouteCollection::getObjectSpawner('string')($segments, $this);
$routeReturn = $class->{$segments[1]}(...$parameters);
} else if (is_array($this->_callback)) {
[$class, $method] = $this->_callback;
$parameters = RouteCollection::getObjectSpawner('array')($this->_callback, $this);
$classInstance = new $class;
$routeReturn = $classInstance->$method(...$parameters);
} else {
$parameters = RouteCollection::getObjectSpawner('callable')($this->_callback, $this);
$routeReturn = call_user_func_array($this->_callback, $parameters);
}
foreach ($this->_after as $after) {
call_user_func($after);
}
return $routeReturn;
}
}
+102 -50
View File
@@ -10,26 +10,29 @@ class RouteCollection
private static RouteCollection $_routeCollection;
public string $_uri = '/';
public array $_routes = array();
private string $_verb = '';
private array $_loadedFiles = array();
private array $_errors = array();
private array $_defaultMiddlewares = array();
private array $_middlewareSet = array();
private static array $_verbsWhitelist = array('*', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'CLI');
private static array $_verbsWeb = array('GET', 'POST', 'PUT', 'PATCH', 'DELETE');
public bool $_groupIn = false;
public string $_groupBase = '';
public array $_groupList = array();
public array $_groups = array();
public array $_contentParsers = array(); // Configure parsers for possible Route Returns
public Route $_currentRoute; // The Route Being Dispatched
public bool $_trace = false; // For debuggin
public int $_traceVerbosity = 1; // How Verbose to trace
public private(set) string $_uri = '/';
public private(set) Array $_routes = Array();
public private(set) string $_verb = '';
public private(set) Array $_loadedFiles = Array();
public private(set) Array $_errors = Array();
public private(set) Array $_defaultMiddlewares = Array();
public private(set) Array $_middlewareSet = Array();
public private(set) bool $_groupIn = false;
public private(set) string $_groupBase = '';
public private(set) Array $_groupList = Array();
public private(set) Array $_groups = Array();
public private(set) Array $_contentParsers = Array(); // Configure parsers for possible Route Returns
public private(set) Route $_currentRoute; // The Route Being Dispatched
public private(set) bool $_trace = false; // For debuggin
public private(set) int $_traceVerbosity = 1; // How Verbose to trace
public private(set) Array $_objectSpawners = Array();
private static Array $_verbsWhitelist = Array('*', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'CLI');
private static Array $_verbsWeb = Array('GET', 'POST', 'PUT', 'PATCH', 'DELETE');
//SINGLETON==============================================
//=======================================================
// SINGLETON
//
/**
* Empty constructor for Singleton
@@ -52,8 +55,10 @@ class RouteCollection
} else {
self::$_routeCollection->_uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI'])[0] : '/';
}
self::$_routeCollection->defineVerb();
self::$_routeCollection->setDefaultParsers();
self::$_routeCollection->setDefaultObjectSpawners();
}
self::$_routeCollection->_uri = str_replace("//", "/", self::$_routeCollection->_uri);
return self::$_routeCollection;
@@ -212,16 +217,16 @@ class RouteCollection
*/
private function setDefaultParsers(): void
{
$this->_contentParsers["string"] = function (string $content) {
$this->_contentParsers["string"] = function (string $content):void {
echo $content;
};
$this->_contentParsers["array"] = function (array $content) {
$this->_contentParsers["array"] = function (array $content):void {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($content);
};
$this->_contentParsers["stdClass"] = function (object $content) {
$this->_contentParsers["stdClass"] = function (object $content):void {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($content);
};
@@ -250,11 +255,11 @@ class RouteCollection
{
$contentType = gettype($content);
if ($contentType == "null") { // Route didn't return anything
if ($contentType == "NULL") { // Route didn't return anything
return;
}
if($contentType == "object"){
if ($contentType == "object") {
$contentType = get_class($content);
}
@@ -264,6 +269,61 @@ class RouteCollection
return;
}
}
call_user_func(self::getInstance()->_contentParsers['stdClass'], $content);
}
//=======================================================
// Object Spawner
//
/**
* Get a Spawner type
*
* @param string $type
* @return mixed
*/
public static function getObjectSpawner(string $type): mixed
{
return self::getInstance()->_objectSpawners[$type];
}
/**
* Register some Object Spawner Types
*
* This will serve to be overwritten for each case so this
* library can have a default spawner and allow custom spawners
*
* @return void
*/
private function setDefaultObjectSpawners(): void
{
$this->_objectSpawners = ObjectSpawners::getDefaultSpawners();
}
/**
* Function to append a Spawner type on the list
*
* @param string $name
* @param callable $callback
* @return RouteCollection
*/
protected function addSpawner(string $name, callable $callback): RouteCollection
{
$this->_objectSpawners[$name] = $callback;
return $this;
}
/**
* API to append a Spawner type on the list
*
* @param string $name
* @param callable $callback
* @return RouteCollection
*/
public static function appendObjectSpawner(string $name, callable $callback): RouteCollection
{
return self::getInstance()->addSpawner($name, $callback);
}
//=======================================================
@@ -299,11 +359,11 @@ class RouteCollection
* Add a GET route
*
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|Array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function get(string $uri, string|callable $callback, int $weight = 0): Route
static function get(string $uri, string|callable|array $callback, int $weight = 0): Route
{
return self::add('GET', $uri, $callback, $weight);
}
@@ -312,11 +372,11 @@ class RouteCollection
* Add a CLI route
*
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function cli(string $uri, string|callable $callback, int $weight = 0): Route
static function cli(string $uri, string|callable|array $callback, int $weight = 0): Route
{
return self::add('CLI', $uri, $callback, $weight);
}
@@ -325,11 +385,11 @@ class RouteCollection
* Add a CLI route
*
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|Array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function post(string $uri, string|callable $callback, int $weight = 0): Route
static function post(string $uri, string|callable|array $callback, int $weight = 0): Route
{
return self::add('POST', $uri, $callback, $weight);
}
@@ -338,11 +398,11 @@ class RouteCollection
* Add a CLI route
*
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|Array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function put(string $uri, string|callable $callback, int $weight = 0): Route
static function put(string $uri, string|callable|array $callback, int $weight = 0): Route
{
return self::add('PUT', $uri, $callback, $weight);
}
@@ -351,11 +411,11 @@ class RouteCollection
* Add a CLI route
*
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|Array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function patch(string $uri, string|callable $callback, int $weight = 0): Route
static function patch(string $uri, string|callable|array $callback, int $weight = 0): Route
{
return self::add('PATCH', $uri, $callback, $weight);
}
@@ -364,11 +424,11 @@ class RouteCollection
* Add a CLI route
*
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|Array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function delete(string $uri, string|callable $callback, int $weight = 0): Route
static function delete(string $uri, string|callable|array $callback, int $weight = 0): Route
{
return self::add('DELETE', $uri, $callback, $weight);
}
@@ -390,11 +450,11 @@ class RouteCollection
*
* @param Array|string $verb Verbs to match the Route
* @param string $uri URI to Match
* @param string|callable $callback Callback to Execute. Might be a class or a callable
* @param string|callable|Array $callback Callback to Execute. Might be a class or a callable
* @param int $weight Weight of the Route
* @return Route
*/
static function add(array|string $verb, string $uri, string|callable $callback, int $weight = 0): Route
static function add(array|string $verb, string $uri, string|callable|array $callback, int $weight = 0): Route
{
$route = new Route();
@@ -406,17 +466,14 @@ class RouteCollection
$uri = rtrim($uri, '/');
if (is_array($verb)) {
$route->_verb = array_map('strtoupper', $verb);
$route->setVerb(array_map('strtoupper', $verb));
} else if (strtoupper($verb) == 'WEB') {
$route->_verb = array_merge($route->_verb, self::$_verbsWeb);
$route->setVerb(array_merge($route->_verb, self::$_verbsWeb));
} else {
$route->_verb[] = strtoupper($verb);
$route->appendVerb(strtoupper($verb));
}
$route->_uri = $uri;
$route->_callback[] = $callback;
$route->_weight = $weight;
$route->prepare();
$route->setUri($uri)->setCallback($callback)->setWeight($weight)->prepare();
self::getInstance()->_routes[] = $route;
@@ -537,7 +594,7 @@ class RouteCollection
);
}
$route->_before = array_merge($this->_defaultMiddlewares, $route->_before);
$route->setMiddlewares(array_merge($this->_defaultMiddlewares, $route->_before));
$routeResult = $route->execute();
@@ -545,11 +602,6 @@ class RouteCollection
$one_hit = true;
}
if ($route->_httpError) {
$this->httpError($route->_httpError);
return $this;
}
$this->parseContent($routeResult);
if ($route->_block) { //If Executed, interrupt route chain?
+10 -26
View File
@@ -53,7 +53,7 @@ class RouteGroup
function middlewareIgnore($name)
{
foreach ($this->_routeGroup as $route) {
$route->_middlewaresToIgnore[] = $name;
$route->middlewareIgnore($name);
}
return $this;
}
@@ -61,39 +61,23 @@ class RouteGroup
function middlewareAdd($name)
{
foreach ($this->_routeGroup as $route) {
$route->_before[] = $name;
$route->middlewareAdd($name);
}
return $this;
}
function middlewareAppend($name, $function)
{
foreach ($this->_routeGroup as $route) {
$route->_before[$name] = $function;
}
return $this;
}
//function middlewareAppend($name, $function)
//{
// foreach ($this->_routeGroup as $route) {
// $route->_before[$name] = $function;
// }
// return $this;
//}
function setWeight($weigth = 0)
{
foreach ($this->_routeGroup as $route) {
$route->_weight = $weigth;
}
return $this;
}
function setHttpError($code = 400, $message = null)
{
foreach ($this->_routeGroup as $route) {
if ($message == null) {
$message = self::$httpMessages[$code];
}
$info = new \stdClass();
$info->code = $code;
$info->message = $message;
$route->_httpError = $info;
$route->setWeight($weigth);
}
return $this;
}