From 6835db5e1fa88a2be9c0814a350a54bf93d61dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 31 May 2020 20:33:34 +0200 Subject: [PATCH] MAPG-86 add classes for routing --- src/Routing/Route.php | 76 ++++++++++++++++++++++++++++++ src/Routing/RouteCollection.php | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/Routing/Route.php create mode 100644 src/Routing/RouteCollection.php diff --git a/src/Routing/Route.php b/src/Routing/Route.php new file mode 100644 index 0000000..af5f659 --- /dev/null +++ b/src/Routing/Route.php @@ -0,0 +1,76 @@ +id = $id; + $this->pattern = $pattern; + $this->handler = $handler; + } + + public function getId(): string + { + return $this->id; + } + + public function generateLink(array $parameters = []): string + { + $link = []; + + foreach ($this->pattern as $fragment) { + if (preg_match('/^{(\\w+)(\\?)?}$/', $fragment, $matches)) { + if (isset($parameters[$matches[1]])) { + $link[] = $parameters[$matches[1]]; + unset($parameters[$matches[1]]); + } elseif (!isset($matches[2])) {//TODO: why? parameter not found but not optional + $link[] = $fragment; + } + } else { + $link[] = $fragment; + } + } + + $queryParams = []; + foreach ($parameters as $key => $value) { + if ($value === null) { + continue; + } + + $queryParams[$key] = $value; + } + + $query = count($queryParams) > 0 ? '?' . http_build_query($queryParams) : ''; + + return implode('/', $link) . $query; + } + + public function callController(array $parameters) + { + $controllerName = $this->handler[0]; + $controller = new $controllerName(); + + return call_user_func([$controller, $this->handler[1]], $parameters); + } + + public function testAgainst(array $path): ?array + { + $parameters = []; + + foreach ($path as $i => $fragment) { + if (preg_match('/^{(\\w+)(?:\\?)?}$/', $this->pattern[$i], $matches)) { + $parameters[$matches[1]] = $fragment; + } elseif ($fragment != $this->pattern[$i]) { + return null; + } + } + + return $parameters; + } +} diff --git a/src/Routing/RouteCollection.php b/src/Routing/RouteCollection.php new file mode 100644 index 0000000..c729c3e --- /dev/null +++ b/src/Routing/RouteCollection.php @@ -0,0 +1,83 @@ + [], + 'post' => [] + ]; + + private array $groupStack = []; + + public function get(string $id, string $pattern, array $handler): void + { + $this->addRoute('get', $id, $pattern, $handler); + } + + public function post(string $id, string $pattern, array $handler): void + { + $this->addRoute('post', $id, $pattern, $handler); + } + + public function group(string $pattern, Closure $group): void + { + $this->groupStack[] = $pattern; + + $group($this); + + array_pop($this->groupStack); + } + + public function getRoute(string $id): ?Route + { + if (!isset($this->routes[$id])) { + return null; + } + + return $this->routes[$id]; + } + + public function match(string $method, array $uri): ?array + { + $groupNumber = count($uri); + + if (!isset($this->searchTable[$method][$groupNumber])) { + return null; + } + + foreach ($this->searchTable[$method][$groupNumber] as $route) { + if (($parameters = $route->testAgainst($uri)) !== null) { + return [$route, $parameters]; + } + } + + return null; + } + + private function addRoute(string $method, string $id, string $pattern, array $handler): void + { + if (isset($this->routes[$id])) { + throw new \Exception('Route already exists: ' . $id); + } + + $pattern = array_merge($this->groupStack, explode('/', $pattern)); + $route = new Route($id, $pattern, $handler); + + $groupNumber = count($pattern); + + $this->searchTable[$method][$groupNumber][] = $route; + + while (preg_match('/^{\\w+\\?}$/', end($pattern))) { + $groupNumber--; + array_pop($pattern); + + $this->searchTable[$method][$groupNumber][] = $route; + } + + $this->routes[$id] = $route; + } +}