170 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace Core\Objects\Router;
 | |
| 
 | |
| use Core\Driver\Logger\Logger;
 | |
| use Core\Objects\Context;
 | |
| use Core\Objects\DatabaseEntity\Route;
 | |
| 
 | |
| class Router {
 | |
| 
 | |
|   private Context $context;
 | |
|   private Logger $logger;
 | |
|   private ?Route $activeRoute;
 | |
|   private ?string $requestedUri;
 | |
|   protected array $routes;
 | |
|   protected array $statusCodeRoutes;
 | |
| 
 | |
|   public function __construct(Context $context) {
 | |
|     $this->context = $context;
 | |
|     $this->routes = [];
 | |
|     $this->statusCodeRoutes = [];
 | |
|     $this->activeRoute = null;
 | |
|     $this->requestedUri = null;
 | |
|     $this->context->router = $this;
 | |
| 
 | |
|     $sql = $context->getSQL();
 | |
|     if ($sql) {
 | |
|       $this->addRoute(new ApiRoute());
 | |
|       $this->logger = new Logger("Router", $sql);
 | |
|     } else {
 | |
|       $this->logger = new Logger("Router");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public function getActiveRoute(): ?Route {
 | |
|     return $this->activeRoute;
 | |
|   }
 | |
| 
 | |
|   public function getRequestedUri(): ?string {
 | |
|     return $this->requestedUri;
 | |
|   }
 | |
| 
 | |
|   public function run(string $url): string {
 | |
| 
 | |
|     // TODO: do we want a global try cache and return status page 500 on any error?
 | |
|     $this->requestedUri = $url;
 | |
| 
 | |
|     $url = strtok($url, "?");
 | |
|     foreach ($this->routes as $route) {
 | |
|       $pathParams = $route->match($url);
 | |
|       if ($pathParams !== false) {
 | |
|         $this->activeRoute = $route;
 | |
|         return $route->call($this, $pathParams);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return $this->returnStatusCode(404);
 | |
|   }
 | |
| 
 | |
|   public function returnStatusCode(int $code, array $params = []): string {
 | |
|     http_response_code($code);
 | |
|     $params["status_code"] = $code;
 | |
|     $params["status_description"] = HTTP_STATUS_DESCRIPTIONS[$code] ?? "Unknown Error";
 | |
|     $route = $this->statusCodeRoutes[strval($code)] ?? null;
 | |
|     if ($route) {
 | |
|       return $route->call($this, $params);
 | |
|     } else {
 | |
|       $req = new \Core\API\Template\Render($this->context);
 | |
|       $res = $req->execute(["file" => "error_document.twig", "parameters" => $params]);
 | |
|       if ($res) {
 | |
|         return $req->getResult()["html"];
 | |
|       } else {
 | |
|         $description = htmlspecialchars($params["status_description"]);
 | |
|         return "<b>$code - $description</b>";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public function addRoute(Route $route) {
 | |
|     if (preg_match("/^\/(\d+)$/", $route->getPattern(), $re)) {
 | |
|       $this->statusCodeRoutes[$re[1]] = $route;
 | |
|     }
 | |
| 
 | |
|     $this->routes[] = $route;
 | |
|   }
 | |
| 
 | |
|   public function writeCache(string $file): bool {
 | |
| 
 | |
|     $routes = "";
 | |
|     $uses = [
 | |
|       "Core\Objects\Context",
 | |
|       "Core\Objects\Router\Router",
 | |
|     ];
 | |
| 
 | |
|     foreach ($this->routes as $route) {
 | |
|       // do not generate cache for static api route
 | |
|       if ($route instanceof ApiRoute) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       $class = $route->getClass();
 | |
|       $constructor = $route->generateCache();
 | |
|       $uses[] = $class->getName();
 | |
|       $routes .= "\n    \$this->addRoute($constructor);";
 | |
|     }
 | |
| 
 | |
|     $uses = implode("\n", array_map(function ($use) { return "use $use;"; }, array_unique($uses)));
 | |
|     $date = (new \DateTime())->format("Y/m/d H:i:s");
 | |
|     $code = "<?php
 | |
| 
 | |
| /**
 | |
|  * DO NOT EDIT! 
 | |
|  * This file is automatically generated by the RoutesAPI on $date.
 | |
|  */
 | |
| 
 | |
| namespace Core\Cache;
 | |
| $uses
 | |
| 
 | |
| class RouterCache extends Router {
 | |
| 
 | |
|   public function __construct(Context \$context) {
 | |
|     parent::__construct(\$context);$routes
 | |
|   }
 | |
| }
 | |
| ";
 | |
| 
 | |
|     if (@file_put_contents($file, $code) === false) {
 | |
|       $this->logger->severe("Could not write Router cache file: $file");
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   public function getContext(): Context {
 | |
|     return $this->context;
 | |
|   }
 | |
| 
 | |
|   public function getLogger(): Logger {
 | |
|     return $this->logger;
 | |
|   }
 | |
| 
 | |
|   public static function cleanURL(string $url, bool $cleanGET = true): string {
 | |
|     // strip GET parameters
 | |
|     if ($cleanGET) {
 | |
|       if (($index = strpos($url, "?")) !== false) {
 | |
|         $url = substr($url, 0, $index);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // strip document reference part
 | |
|     if (($index = strpos($url, "#")) !== false) {
 | |
|       $url = substr($url, 0, $index);
 | |
|     }
 | |
| 
 | |
|     // strip leading slash
 | |
|     return preg_replace("/^\/+/", "", $url);
 | |
|   }
 | |
| 
 | |
|   public function getRoutes(bool $includeStatusRoutes = false): array {
 | |
| 
 | |
|     if (!$includeStatusRoutes && !empty($this->statusCodeRoutes)) {
 | |
|       return array_filter($this->routes, function ($route) {
 | |
|         return !in_array($route, $this->statusCodeRoutes);
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     return $this->routes;
 | |
|   }
 | |
| } | 
