WizdomWeb/app/Core/Router.php

139 lines
4.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* ============================================
* File: Router.php
* Path: /app/Core/
* Purpose: Core router handling HTTP methodspecific route dispatching with dynamic path and closure support.
* Version: 1.4
* Author: Wizdom Networks
* ============================================
*/
namespace WizdomNetworks\WizeWeb\Core;
use WizdomNetworks\WizeWeb\Utilities\Logger;
use WizdomNetworks\WizeWeb\Utilities\ErrorHandler;
use WizdomNetworks\WizeWeb\Core\View;
class Router
{
/**
* Array of registered routes indexed by HTTP method and path.
* @var array
*/
private array $routes = [];
/**
* Registers a controller-based route with optional path parameters.
*
* @param string $path The route path (e.g. "/contact" or "/verify/{code}").
* @param string $controller Fully qualified controller class.
* @param string $method Method in controller to invoke.
* @param string $httpMethod HTTP method (GET, POST, etc.). Defaults to GET.
*/
public function add(string $path, string $controller, string $method, string $httpMethod = 'GET'): void
{
$normalizedPath = trim($path, '/');
$routeKey = strtoupper($httpMethod) . ':' . $normalizedPath;
// Transform path into regex pattern and extract parameter names
$paramKeys = [];
$regexPath = preg_replace_callback('/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/', function ($matches) use (&$paramKeys) {
$paramKeys[] = $matches[1];
return '([^\/]+)';
}, $normalizedPath);
$this->routes[$routeKey] = [
'controller' => $controller,
'method' => $method,
'pattern' => "#^" . $regexPath . "$#",
'params' => $paramKeys
];
Logger::debug("Registering route: [$httpMethod] $path -> $controller::$method");
}
/**
* Registers a closure-based route.
*
* @param string $path The route path.
* @param \Closure $callback Anonymous function to handle the route.
* @param string $httpMethod HTTP method (GET, POST, etc.). Defaults to GET.
*/
public function addClosure(string $path, \Closure $callback, string $httpMethod = 'GET'): void
{
$routeKey = strtoupper($httpMethod) . ':' . trim($path, '/');
Logger::debug("Registering closure route: [$httpMethod] $path");
$this->routes[$routeKey] = $callback;
}
/**
* Dispatches the current request to the matching route or fallback to 404.
*
* @param string $path The requested path, usually from index.php.
*/
public function dispatch($path)
{
$httpMethod = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
$cleanPath = trim($path, '/');
$routeKeyBase = $httpMethod . ':';
foreach ($this->routes as $key => $route) {
if (strpos($key, $routeKeyBase) !== 0) {
continue;
}
// Handle closure route directly
if ($route instanceof \Closure && $key === $routeKeyBase . $cleanPath) {
Logger::info("Executing closure route: [$httpMethod] $cleanPath");
$route();
return;
}
// Only continue if route is an array
if (!is_array($route)) {
continue;
}
$routePattern = $route['pattern'] ?? null;
// Match dynamic route patterns and extract parameters
if ($routePattern && preg_match($routePattern, $cleanPath, $matches)) {
array_shift($matches); // Remove full match
$params = array_combine($route['params'], $matches) ?: [];
$controllerName = $route['controller'];
$method = $route['method'];
try {
if (!class_exists($controllerName)) {
throw new \Exception("Controller not found: $controllerName");
}
$controller = new $controllerName();
if (!method_exists($controller, $method)) {
throw new \Exception("Method $method not found in $controllerName");
}
Logger::info("Executing controller: $controllerName::$method with params: " . json_encode($params));
call_user_func_array([$controller, $method], $params);
return;
} catch (\Throwable $e) {
echo "<pre>";
echo "Exception: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . "\n";
echo "Line: " . $e->getLine() . "\n";
echo "Trace:\n" . $e->getTraceAsString();
echo "</pre>";
exit;
}
}
}
// If no route matched, render 404 page
Logger::error("Route not found: [$httpMethod] $path");
http_response_code(404);
View::render('pages/404');
}
}