diff --git a/composer.json b/composer.json index 2572194..651da57 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "firebase/php-jwt": "v6.11.1", "vlucas/phpdotenv": "v5.6.2", "symfony/var-dumper": "^v6.4.26", - "claudecio/axiumphp": "dev-dev-update-initializing", + "claudecio/axiumphp": "dev-feature-updated-router", "ext-json": "*" }, "repositories": [ diff --git a/composer.lock b/composer.lock index 7e5157a..99f02f3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3e440c7d7c70dfe80c1e1f3c59e6adc5", + "content-hash": "f3706a4f041aac68b4feb4e9d952a8a2", "packages": [ { "name": "claudecio/axiumphp", - "version": "dev-dev-update-initializing", + "version": "dev-feature-updated-router", "source": { "type": "git", "url": "https://github.com/claudecio/AxiumPHP.git", - "reference": "ccc3c7de5462371150ea8d82549701519503f9f7" + "reference": "c7353947a667ea857b461f2b6251441784a4b8ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/ccc3c7de5462371150ea8d82549701519503f9f7", - "reference": "ccc3c7de5462371150ea8d82549701519503f9f7", + "url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/c7353947a667ea857b461f2b6251441784a4b8ef", + "reference": "c7353947a667ea857b461f2b6251441784a4b8ef", "shasum": "" }, "require": { @@ -35,10 +35,10 @@ ], "description": "Meu framework PHP para sistemas MVC modulares.", "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" }, - "time": "2025-10-18T18:31:17+00:00" + "time": "2025-10-26T20:05:19+00:00" }, { "name": "firebase/php-jwt", diff --git a/vendor/claudecio/axiumphp/src/Core/Router.php b/vendor/claudecio/axiumphp/src/Core/Router.php index 0086cf9..aa7eadb 100755 --- a/vendor/claudecio/axiumphp/src/Core/Router.php +++ b/vendor/claudecio/axiumphp/src/Core/Router.php @@ -2,27 +2,19 @@ namespace AxiumPHP\Core; use Exception; + use AxiumPHP\Helpers\RequestHelper; class Router { private static $routes = []; private static $params = []; private static $ROUTER_MODE = null; private static $APP_SYS_MODE = null; - private static $ROUTER_ALLOWED_ORIGINS = ['*']; + private static string $basePath = ''; private static $currentGroupPrefix = ''; private static $currentGroupMiddlewares = []; - private static array $requiredConstants = [ - 'ROUTER_MODE', - 'APP_SYS_MODE' - ]; - private static array $allowedHttpRequests = [ - 'GET', - 'POST', - 'PUT', - 'PATCH', - 'DELETE', - 'OPTIONS' - ]; + private static $ROUTER_ALLOWED_ORIGINS = ['*']; + private static array $requiredConstants = ['ROUTER_MODE', 'APP_SYS_MODE']; + private static array $allowedHttpRequests = ['GET','POST','PUT','PATCH','DELETE','OPTIONS']; /** * 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 self::checkRequiredConstant(); + // Verifica se tem diretório base + if(defined(constant_name: 'ROUTER_BASE_PATH')) { + self::$basePath = ROUTER_BASE_PATH; + } + // Define constante self::$ROUTER_MODE = strtoupper(string: ROUTER_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. - * - * 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 - */ + + // ======================= + // Métodos HTTP + // ======================= public static function GET(string $uri, array $handler, array $middlewares = []): void { 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 { 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 { 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 { 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 { self::addRoute(method: "DELETE", uri: $uri, handler: $handler, middlewares: $middlewares); } @@ -189,7 +113,7 @@ * @return void */ private static function addRoute(string $method, string $uri, array $handler, array $middlewares = []): void { - self::$routes[] = [ + self::$routes[$method][] = [ 'method' => strtoupper(string: $method), 'path' => '/' . trim(string: self::$currentGroupPrefix . '/' . trim(string: $uri, characters: '/'), characters: '/'), '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. * @@ -497,6 +461,50 @@ 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. * @@ -513,10 +521,21 @@ // Verificar as constantes necessárias antes de processar a requisição self::checkRequiredConstant(); + // Inicializa dados da requisição + $requestData = null; $method = $_SERVER['REQUEST_METHOD']; $uri = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH); $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 if ($method === 'POST' && isset($_POST['_method'])) { $method = strtoupper(string: $_POST['_method']); @@ -524,13 +543,11 @@ // Verifica se o método HTTP é permitido if(!in_array(needle: $method, haystack: self::$allowedHttpRequests)) { - http_response_code(response_code: 405); - header(header: 'Content-Type: application/json; charset=utf-8'); - echo json_encode(value: [ - "status" => 'error', - "message" => "Método HTTP '{$method}' não permitido." - ]); - exit; + RequestHelper::sendJsonResponse( + response_code: 405, + status: 'error', + message: "Método HTTP '{$method}' não permitido." + ); } // ============================= @@ -572,43 +589,54 @@ // ============================= // EXTRAI DADOS DE PUT, DELETE, PATCH // ============================= - $requestData = match($method) { - 'GET' => null, - 'POST' => null, - default => self::extractRequestData(method: $method) - }; + if(in_array(needle: $method, haystack: ['PUT', 'DELETE', 'PATCH'])) { + $requestData = self::extractRequestData(method: $method); + } + + // Seleciona apenas as rotas do método atual + $routes = self::$routes[$method] ?? []; // ============================= // LOOP PARA PROCESSAR ROTAS // ============================= - foreach (self::$routes as $route) { - if (self::matchRoute(method: $method, uri: $uri, route: $route)) { - // Executa middlewares - if (!empty($route['middlewares']) && !self::runMiddlewares(middlewares: $route['middlewares'])) { - return; // Middleware bloqueou - } + if(!empty($routes)) { + foreach ($routes as $route) { + if (self::matchRoute(method: $method, uri: $uri, route: $route)) { + // Executa middlewares + if (!empty($route['middlewares']) && !self::runMiddlewares(middlewares: $route['middlewares'])) { + return; // Middleware bloqueou + } - $controller = new $route['controller'](); - $action = $route['action']; - $params = self::prepareMethodParameters(method: $method, params: [$requestData]); + $controller = new $route['controller'](); + $action = $route['action']; + $params = self::prepareMethodParameters(method: $method, params: [$requestData]); - switch (self::$ROUTER_MODE) { - case 'VIEW': - if (method_exists(object_or_class: $controller, method: $action)) { - http_response_code(response_code: 200); - call_user_func_array(callback: [$controller, $action], args: $params); - exit; - } - break; + switch (self::$ROUTER_MODE) { + case 'VIEW': + if (method_exists(object_or_class: $controller, method: $action)) { + http_response_code(response_code: 200); + call_user_func_array(callback: [$controller, $action], args: $params); + exit; + } + break; - case 'JSON': - if (method_exists(object_or_class: $controller, method: $action)) { - http_response_code(response_code: 200); - header(header: 'Content-Type: application/json; charset=utf-8'); - call_user_func_array(callback: [$controller, $action], args: $params); - exit; - } - break; + case 'JSON': + if (method_exists(object_or_class: $controller, method: $action)) { + http_response_code(response_code: 200); + header(header: 'Content-Type: application/json; charset=utf-8'); + call_user_func_array(callback: [$controller, $action], args: $params); + exit; + } + break; + + default: + RequestHelper::sendJsonResponse( + response_code: 500, + status: 'error', + message: "Modo de roteamento inválido." + ); + break; + } } } } @@ -619,54 +647,4 @@ self::pageNotFound(); 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; - } - } } \ No newline at end of file diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 070700a..792281d 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2,24 +2,24 @@ "packages": [ { "name": "claudecio/axiumphp", - "version": "dev-dev-update-initializing", - "version_normalized": "dev-dev-update-initializing", + "version": "dev-feature-updated-router", + "version_normalized": "dev-feature-updated-router", "source": { "type": "git", "url": "https://github.com/claudecio/AxiumPHP.git", - "reference": "ccc3c7de5462371150ea8d82549701519503f9f7" + "reference": "c7353947a667ea857b461f2b6251441784a4b8ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/ccc3c7de5462371150ea8d82549701519503f9f7", - "reference": "ccc3c7de5462371150ea8d82549701519503f9f7", + "url": "https://api.github.com/repos/claudecio/AxiumPHP/zipball/c7353947a667ea857b461f2b6251441784a4b8ef", + "reference": "c7353947a667ea857b461f2b6251441784a4b8ef", "shasum": "" }, "require": { "php": ">=8.1", "vlucas/phpdotenv": "v5.6.2" }, - "time": "2025-10-18T18:31:17+00:00", + "time": "2025-10-26T20:05:19+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -32,7 +32,7 @@ ], "description": "Meu framework PHP para sistemas MVC modulares.", "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" }, "install-path": "../claudecio/axiumphp" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 212c194..39fb355 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'cybercore/workbloom', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '311cadc1d12a6f269a473c6861dfc268a201ef6f', + 'reference' => '9cfa79db4b2f3aca5feab04bb1212cf87d9a8565', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -11,9 +11,9 @@ ), 'versions' => array( 'claudecio/axiumphp' => array( - 'pretty_version' => 'dev-dev-update-initializing', - 'version' => 'dev-dev-update-initializing', - 'reference' => 'ccc3c7de5462371150ea8d82549701519503f9f7', + 'pretty_version' => 'dev-feature-updated-router', + 'version' => 'dev-feature-updated-router', + 'reference' => 'c7353947a667ea857b461f2b6251441784a4b8ef', 'type' => 'library', 'install_path' => __DIR__ . '/../claudecio/axiumphp', 'aliases' => array(), @@ -22,7 +22,7 @@ 'cybercore/workbloom' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '311cadc1d12a6f269a473c6861dfc268a201ef6f', + 'reference' => '9cfa79db4b2f3aca5feab04bb1212cf87d9a8565', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(),