feat(router): atualizar versão do AxiumPHP e melhorar gerenciamento de rotas

This commit is contained in:
Claudecio Martins
2025-10-26 21:07:28 +01:00
parent 9cfa79db4b
commit 6071550f20
5 changed files with 176 additions and 198 deletions

View File

@@ -7,7 +7,7 @@
"firebase/php-jwt": "v6.11.1", "firebase/php-jwt": "v6.11.1",
"vlucas/phpdotenv": "v5.6.2", "vlucas/phpdotenv": "v5.6.2",
"symfony/var-dumper": "^v6.4.26", "symfony/var-dumper": "^v6.4.26",
"claudecio/axiumphp": "dev-dev-update-initializing", "claudecio/axiumphp": "dev-feature-updated-router",
"ext-json": "*" "ext-json": "*"
}, },
"repositories": [ "repositories": [

14
composer.lock generated
View File

@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3e440c7d7c70dfe80c1e1f3c59e6adc5", "content-hash": "f3706a4f041aac68b4feb4e9d952a8a2",
"packages": [ "packages": [
{ {
"name": "claudecio/axiumphp", "name": "claudecio/axiumphp",
"version": "dev-dev-update-initializing", "version": "dev-feature-updated-router",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/claudecio/AxiumPHP.git", "url": "https://github.com/claudecio/AxiumPHP.git",
"reference": "ccc3c7de5462371150ea8d82549701519503f9f7" "reference": "c7353947a667ea857b461f2b6251441784a4b8ef"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/ccc3c7de5462371150ea8d82549701519503f9f7", "url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/c7353947a667ea857b461f2b6251441784a4b8ef",
"reference": "ccc3c7de5462371150ea8d82549701519503f9f7", "reference": "c7353947a667ea857b461f2b6251441784a4b8ef",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -35,10 +35,10 @@
], ],
"description": "Meu framework PHP para sistemas MVC modulares.", "description": "Meu framework PHP para sistemas MVC modulares.",
"support": { "support": {
"source": "https://github.com/claudecio/AxiumPHP/tree/dev-update-initializing", "source": "https://github.com/claudecio/AxiumPHP/tree/feature-updated-router",
"issues": "https://github.com/claudecio/AxiumPHP/issues" "issues": "https://github.com/claudecio/AxiumPHP/issues"
}, },
"time": "2025-10-18T18:31:17+00:00" "time": "2025-10-26T20:05:19+00:00"
}, },
{ {
"name": "firebase/php-jwt", "name": "firebase/php-jwt",

View File

@@ -2,27 +2,19 @@
namespace AxiumPHP\Core; namespace AxiumPHP\Core;
use Exception; use Exception;
use AxiumPHP\Helpers\RequestHelper;
class Router { class Router {
private static $routes = []; private static $routes = [];
private static $params = []; private static $params = [];
private static $ROUTER_MODE = null; private static $ROUTER_MODE = null;
private static $APP_SYS_MODE = null; private static $APP_SYS_MODE = null;
private static $ROUTER_ALLOWED_ORIGINS = ['*']; private static string $basePath = '';
private static $currentGroupPrefix = ''; private static $currentGroupPrefix = '';
private static $currentGroupMiddlewares = []; private static $currentGroupMiddlewares = [];
private static array $requiredConstants = [ private static $ROUTER_ALLOWED_ORIGINS = ['*'];
'ROUTER_MODE', private static array $requiredConstants = ['ROUTER_MODE', 'APP_SYS_MODE'];
'APP_SYS_MODE' private static array $allowedHttpRequests = ['GET','POST','PUT','PATCH','DELETE','OPTIONS'];
];
private static array $allowedHttpRequests = [
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
];
/** /**
* Construtor que vai garantir que as constantes necessárias estejam definidas antes de * Construtor que vai garantir que as constantes necessárias estejam definidas antes de
@@ -32,6 +24,11 @@
// Verificar as constantes no momento da criação da instância // Verificar as constantes no momento da criação da instância
self::checkRequiredConstant(); self::checkRequiredConstant();
// Verifica se tem diretório base
if(defined(constant_name: 'ROUTER_BASE_PATH')) {
self::$basePath = ROUTER_BASE_PATH;
}
// Define constante // Define constante
self::$ROUTER_MODE = strtoupper(string: ROUTER_MODE); self::$ROUTER_MODE = strtoupper(string: ROUTER_MODE);
self::$APP_SYS_MODE = strtoupper(string: APP_SYS_MODE); self::$APP_SYS_MODE = strtoupper(string: APP_SYS_MODE);
@@ -74,99 +71,26 @@
} }
} }
/**
* Adiciona uma rota com método GET à lista de rotas da aplicação. // =======================
* // Métodos HTTP
* Este método é um atalho para adicionar rotas com o método HTTP GET. Ele // =======================
* chama o método `addRoute` internamente, passando os parâmetros
* fornecidos e o método 'GET'.
*
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
* @param array $handler Um array contendo o nome do controlador e o nome da ação
* que devem ser executados quando a rota for
* corresponder (ex: ['UsuarioController', 'index']).
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
* devem ser executados antes do handler da rota.
*
* @return void
*/
public static function GET(string $uri, array $handler, array $middlewares = []): void { public static function GET(string $uri, array $handler, array $middlewares = []): void {
self::addRoute(method: "GET", uri: $uri, handler: $handler, middlewares: $middlewares); self::addRoute(method: "GET", uri: $uri, handler: $handler, middlewares: $middlewares);
} }
/**
* Adiciona uma rota com método POST à lista de rotas da aplicação.
*
* Este método é um atalho para adicionar rotas com o método HTTP POST. Ele
* chama o método `addRoute` internamente, passando os parâmetros
* fornecidos e o método 'POST'.
*
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
* @param array $handler Um array contendo o nome do controlador e o nome da ação
* que devem ser executados quando a rota for
* corresponder (ex: ['UsuarioController', 'salvar']).
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
* devem ser executados antes do handler da rota.
*
* @return void
*/
public static function POST(string $uri, array $handler, array $middlewares = []): void { public static function POST(string $uri, array $handler, array $middlewares = []): void {
self::addRoute(method: "POST", uri: $uri, handler: $handler, middlewares: $middlewares); self::addRoute(method: "POST", uri: $uri, handler: $handler, middlewares: $middlewares);
} }
/**
* Adiciona uma rota com método PUT à lista de rotas da aplicação.
*
* Este método é um atalho para adicionar rotas com o método HTTP PUT. Ele
* chama o método `addRoute` internamente, passando os parâmetros
* fornecidos e o método 'PUT'.
*
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
* @param array $handler Um array contendo o nome do controlador e o nome da ação
* que devem ser executados quando a rota for
* corresponder (ex: ['UsuarioController', 'salvar']).
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
* devem ser executados antes do handler da rota.
*
* @return void
*/
public static function PUT(string $uri, array $handler, array $middlewares = []): void { public static function PUT(string $uri, array $handler, array $middlewares = []): void {
self::addRoute(method: "PUT", uri: $uri, handler: $handler, middlewares: $middlewares); self::addRoute(method: "PUT", uri: $uri, handler: $handler, middlewares: $middlewares);
} }
/**
* Adiciona uma nova rota que responde a requisições PATCH.
*
* Este é um método estático de conveniência que simplifica o registro de rotas
* para o método HTTP PATCH. Ele delega a tarefa principal para o método
* privado `addRoute`, passando o método HTTP, a URI, a função de manipulação
* (`handler`) e quaisquer middlewares associados.
*
* @param string $uri A URI da rota (ex: '/api/resource/{id}').
* @param array $handler Um array de dois elementos que define o manipulador da rota: o nome da classe do controlador e o nome do método a ser executado.
* @param array $middlewares Um array opcional de middlewares a serem executados antes do manipulador da rota.
* @return void
*/
public static function PATCH(string $uri, array $handler, array $middlewares = []): void { public static function PATCH(string $uri, array $handler, array $middlewares = []): void {
self::addRoute(method: "PATCH", uri: $uri, handler: $handler, middlewares: $middlewares); self::addRoute(method: "PATCH", uri: $uri, handler: $handler, middlewares: $middlewares);
} }
/**
* Adiciona uma rota com método DELETE à lista de rotas da aplicação.
*
* Este método é um atalho para adicionar rotas com o método HTTP DELETE. Ele
* chama o método `addRoute` internamente, passando os parâmetros
* fornecidos e o método 'DELETE'.
*
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
* @param array $handler Um array contendo o nome do controlador e o nome da ação
* que devem ser executados quando a rota for
* corresponder (ex: ['UsuarioController', 'salvar']).
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
* devem ser executados antes do handler da rota.
*
* @return void
*/
public static function DELETE(string $uri, array $handler, array $middlewares = []): void { public static function DELETE(string $uri, array $handler, array $middlewares = []): void {
self::addRoute(method: "DELETE", uri: $uri, handler: $handler, middlewares: $middlewares); self::addRoute(method: "DELETE", uri: $uri, handler: $handler, middlewares: $middlewares);
} }
@@ -189,7 +113,7 @@
* @return void * @return void
*/ */
private static function addRoute(string $method, string $uri, array $handler, array $middlewares = []): void { private static function addRoute(string $method, string $uri, array $handler, array $middlewares = []): void {
self::$routes[] = [ self::$routes[$method][] = [
'method' => strtoupper(string: $method), 'method' => strtoupper(string: $method),
'path' => '/' . trim(string: self::$currentGroupPrefix . '/' . trim(string: $uri, characters: '/'), characters: '/'), 'path' => '/' . trim(string: self::$currentGroupPrefix . '/' . trim(string: $uri, characters: '/'), characters: '/'),
'controller' => $handler[0], 'controller' => $handler[0],
@@ -198,6 +122,46 @@
]; ];
} }
/**
* Redireciona para um caminho específico.
*
* Este método estático constrói uma URL de redirecionamento com base no
* caminho fornecido e no `basePath` configurado. Ele garante que o
* `basePath` e o `path` comecem com uma barra (`/`), monta a URL
* completa, limpa quaisquer duplicações de barras e executa o
* redirecionamento HTTP.
*
* @param string|null $path O caminho para o qual redirecionar. Se nulo ou vazio,
* redireciona para o `basePath`.
*
* @return void
*/
public static function redirect(?string $path): void {
// Garante que o basePath começa com "/"
$basePath = self::$basePath ? '/' . trim(string: self::$basePath, characters: '/') : '/';
// Se não tiver caminho, redireciona pro basePath direto
if (empty($path)) {
header(header: "Location: {$basePath}/");
exit;
}
// Garante que o path começa com "/"
if (!str_starts_with(haystack: $path, needle: '/')) {
$path = "/{$path}";
}
// Monta o destino completo
$redirectPath = "{$basePath}{$path}";
// Limpa duplicações tipo "//"
$redirectPath = preg_replace(pattern: '#/+#', replacement: '/', subject: $redirectPath);
// Executa o redirecionamento
header(header: "Location: {$redirectPath}");
exit;
}
/** /**
* Verifica se um caminho de rota corresponde a um caminho de requisição. * Verifica se um caminho de rota corresponde a um caminho de requisição.
* *
@@ -497,6 +461,50 @@
return $preparedParams; return $preparedParams;
} }
/**
* Exibe a página de erro 404 (Página não encontrada).
*
* Este método estático define o código de resposta HTTP como 404 e renderiza
* a view "/Errors/404" para exibir a página de erro. Após a renderização,
* o script é encerrado.
*
* @return void
*/
private static function pageNotFound(): void {
switch (self::$ROUTER_MODE) {
case 'VIEW':
// Notifica erro em caso constante não definida
if(!defined(constant_name: 'ERROR_404_VIEW_PATH')) {
RequestHelper::sendJsonResponse(
response_code: 500,
status: 'error',
message: "Constante 'ERROR_404_VIEW_PATH' não foi definida."
);
}
// Caso o arquivo da constante não exista, notifica erro
if(!file_exists(filename: ERROR_404_VIEW_PATH)) {
RequestHelper::sendJsonResponse(
response_code: 500,
status: 'error',
message: "Arquivo da constante 'ERROR_404_VIEW_PATH' não foi encontrado."
);
}
http_response_code(response_code: 404);
require_once ERROR_404_VIEW_PATH;
break;
case 'JSON':
RequestHelper::sendJsonResponse(
response_code: 404,
status: 'error',
message: "Página não encontrada."
);
break;
}
}
/** /**
* Despacha a requisição para o controlador e ação correspondentes. * Despacha a requisição para o controlador e ação correspondentes.
* *
@@ -513,10 +521,21 @@
// Verificar as constantes necessárias antes de processar a requisição // Verificar as constantes necessárias antes de processar a requisição
self::checkRequiredConstant(); self::checkRequiredConstant();
// Inicializa dados da requisição
$requestData = null;
$method = $_SERVER['REQUEST_METHOD']; $method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH); $uri = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH);
$uri = trim(string: rtrim(string: $uri, characters: '/'), characters: '/'); $uri = trim(string: rtrim(string: $uri, characters: '/'), characters: '/');
// Remove o basePath do início da URI (se tiver)
$basePath = rtrim(string: self::$basePath ?? '', characters: '/');
if (!empty($basePath) && str_starts_with(haystack: $uri, needle: $basePath)) {
$uri = substr(string: $uri, offset: strlen(string: $basePath));
}
// Limpa possíveis barras extras
$uri = trim(string: $uri, characters: '/');
// Suporte ao _method em POST para PUT/DELETE/PATCH // Suporte ao _method em POST para PUT/DELETE/PATCH
if ($method === 'POST' && isset($_POST['_method'])) { if ($method === 'POST' && isset($_POST['_method'])) {
$method = strtoupper(string: $_POST['_method']); $method = strtoupper(string: $_POST['_method']);
@@ -524,13 +543,11 @@
// Verifica se o método HTTP é permitido // Verifica se o método HTTP é permitido
if(!in_array(needle: $method, haystack: self::$allowedHttpRequests)) { if(!in_array(needle: $method, haystack: self::$allowedHttpRequests)) {
http_response_code(response_code: 405); RequestHelper::sendJsonResponse(
header(header: 'Content-Type: application/json; charset=utf-8'); response_code: 405,
echo json_encode(value: [ status: 'error',
"status" => 'error', message: "Método HTTP '{$method}' não permitido."
"message" => "Método HTTP '{$method}' não permitido." );
]);
exit;
} }
// ============================= // =============================
@@ -572,16 +589,18 @@
// ============================= // =============================
// EXTRAI DADOS DE PUT, DELETE, PATCH // EXTRAI DADOS DE PUT, DELETE, PATCH
// ============================= // =============================
$requestData = match($method) { if(in_array(needle: $method, haystack: ['PUT', 'DELETE', 'PATCH'])) {
'GET' => null, $requestData = self::extractRequestData(method: $method);
'POST' => null, }
default => self::extractRequestData(method: $method)
}; // Seleciona apenas as rotas do método atual
$routes = self::$routes[$method] ?? [];
// ============================= // =============================
// LOOP PARA PROCESSAR ROTAS // LOOP PARA PROCESSAR ROTAS
// ============================= // =============================
foreach (self::$routes as $route) { if(!empty($routes)) {
foreach ($routes as $route) {
if (self::matchRoute(method: $method, uri: $uri, route: $route)) { if (self::matchRoute(method: $method, uri: $uri, route: $route)) {
// Executa middlewares // Executa middlewares
if (!empty($route['middlewares']) && !self::runMiddlewares(middlewares: $route['middlewares'])) { if (!empty($route['middlewares']) && !self::runMiddlewares(middlewares: $route['middlewares'])) {
@@ -609,6 +628,15 @@
exit; exit;
} }
break; break;
default:
RequestHelper::sendJsonResponse(
response_code: 500,
status: 'error',
message: "Modo de roteamento inválido."
);
break;
}
} }
} }
} }
@@ -619,54 +647,4 @@
self::pageNotFound(); self::pageNotFound();
exit; exit;
} }
/**
* Exibe a página de erro 404 (Página não encontrada).
*
* Este método estático define o código de resposta HTTP como 404 e renderiza
* a view "/Errors/404" para exibir a página de erro. Após a renderização,
* o script é encerrado.
*
* @return void
*/
private static function pageNotFound(): void {
switch (self::$ROUTER_MODE) {
case 'VIEW':
// Notifica erro em caso constante não definida
if(!defined(constant_name: 'ERROR_404_VIEW_PATH')) {
http_response_code(response_code: 500);
header(header: 'Content-Type: application/json; charset=utf-8');
echo json_encode( value: [
"status" => 'error',
"message" => "Constante 'ERROR_404_VIEW_PATH' não foi definida.",
]);
exit;
}
// Caso o arquivo da constante não exista, notifica erro
if(!file_exists(filename: ERROR_404_VIEW_PATH)) {
http_response_code(response_code: 500);
header(header: 'Content-Type: application/json; charset=utf-8');
echo json_encode( value: [
"status" => 'error',
"message" => "Arquivo da constante 'ERROR_404_VIEW_PATH' não foi encontrado.",
]);
exit;
}
http_response_code(response_code: 404);
require_once ERROR_404_VIEW_PATH;
break;
case 'JSON':
http_response_code(response_code: 404);
header(header: 'Content-Type: application/json; charset=utf-8');
echo json_encode( value: [
"status" => 'error',
"message" => "Página não encontrada.",
]);
break;
}
}
} }

View File

@@ -2,24 +2,24 @@
"packages": [ "packages": [
{ {
"name": "claudecio/axiumphp", "name": "claudecio/axiumphp",
"version": "dev-dev-update-initializing", "version": "dev-feature-updated-router",
"version_normalized": "dev-dev-update-initializing", "version_normalized": "dev-feature-updated-router",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/claudecio/AxiumPHP.git", "url": "https://github.com/claudecio/AxiumPHP.git",
"reference": "ccc3c7de5462371150ea8d82549701519503f9f7" "reference": "c7353947a667ea857b461f2b6251441784a4b8ef"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/ccc3c7de5462371150ea8d82549701519503f9f7", "url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/c7353947a667ea857b461f2b6251441784a4b8ef",
"reference": "ccc3c7de5462371150ea8d82549701519503f9f7", "reference": "c7353947a667ea857b461f2b6251441784a4b8ef",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=8.1",
"vlucas/phpdotenv": "v5.6.2" "vlucas/phpdotenv": "v5.6.2"
}, },
"time": "2025-10-18T18:31:17+00:00", "time": "2025-10-26T20:05:19+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@@ -32,7 +32,7 @@
], ],
"description": "Meu framework PHP para sistemas MVC modulares.", "description": "Meu framework PHP para sistemas MVC modulares.",
"support": { "support": {
"source": "https://github.com/claudecio/AxiumPHP/tree/dev-update-initializing", "source": "https://github.com/claudecio/AxiumPHP/tree/feature-updated-router",
"issues": "https://github.com/claudecio/AxiumPHP/issues" "issues": "https://github.com/claudecio/AxiumPHP/issues"
}, },
"install-path": "../claudecio/axiumphp" "install-path": "../claudecio/axiumphp"

View File

@@ -3,7 +3,7 @@
'name' => 'cybercore/workbloom', 'name' => 'cybercore/workbloom',
'pretty_version' => 'dev-main', 'pretty_version' => 'dev-main',
'version' => 'dev-main', 'version' => 'dev-main',
'reference' => '311cadc1d12a6f269a473c6861dfc268a201ef6f', 'reference' => '9cfa79db4b2f3aca5feab04bb1212cf87d9a8565',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@@ -11,9 +11,9 @@
), ),
'versions' => array( 'versions' => array(
'claudecio/axiumphp' => array( 'claudecio/axiumphp' => array(
'pretty_version' => 'dev-dev-update-initializing', 'pretty_version' => 'dev-feature-updated-router',
'version' => 'dev-dev-update-initializing', 'version' => 'dev-feature-updated-router',
'reference' => 'ccc3c7de5462371150ea8d82549701519503f9f7', 'reference' => 'c7353947a667ea857b461f2b6251441784a4b8ef',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../claudecio/axiumphp', 'install_path' => __DIR__ . '/../claudecio/axiumphp',
'aliases' => array(), 'aliases' => array(),
@@ -22,7 +22,7 @@
'cybercore/workbloom' => array( 'cybercore/workbloom' => array(
'pretty_version' => 'dev-main', 'pretty_version' => 'dev-main',
'version' => 'dev-main', 'version' => 'dev-main',
'reference' => '311cadc1d12a6f269a473c6861dfc268a201ef6f', 'reference' => '9cfa79db4b2f3aca5feab04bb1212cf87d9a8565',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),