From e1ad307b531c27ec6bfaf8b4d018991b0d4a78f3 Mon Sep 17 00:00:00 2001 From: winter Date: Fri, 6 Dec 2024 20:43:26 +0000 Subject: initial commit hardcoded functionality mostly! --- WpfTest/GlobalConfig.php | 25 ++++++ WpfTest/HttpReturnStatus.php | 7 ++ WpfTest/HttpReturnStatus/BadRequest.php | 18 ++++ WpfTest/HttpReturnStatus/InternalServerError.php | 16 ++++ WpfTest/HttpReturnStatus/NoHandlerFound.php | 18 ++++ WpfTest/HttpReturnStatus/NotFound.php | 18 ++++ WpfTest/Router.php | 101 +++++++++++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 WpfTest/GlobalConfig.php create mode 100644 WpfTest/HttpReturnStatus.php create mode 100644 WpfTest/HttpReturnStatus/BadRequest.php create mode 100644 WpfTest/HttpReturnStatus/InternalServerError.php create mode 100644 WpfTest/HttpReturnStatus/NoHandlerFound.php create mode 100644 WpfTest/HttpReturnStatus/NotFound.php create mode 100644 WpfTest/Router.php (limited to 'WpfTest') diff --git a/WpfTest/GlobalConfig.php b/WpfTest/GlobalConfig.php new file mode 100644 index 0000000..4bbfe68 --- /dev/null +++ b/WpfTest/GlobalConfig.php @@ -0,0 +1,25 @@ +getCanonicalHost(); // fix this? + } +} \ No newline at end of file diff --git a/WpfTest/HttpReturnStatus.php b/WpfTest/HttpReturnStatus.php new file mode 100644 index 0000000..60622bb --- /dev/null +++ b/WpfTest/HttpReturnStatus.php @@ -0,0 +1,7 @@ +message = $reason ?? 'Request does not conform to expectations'; + } + + public function httpCode(): int { + return 400; + } + + public function httpReason(): string { + return "Bad Request"; + } +} \ No newline at end of file diff --git a/WpfTest/HttpReturnStatus/InternalServerError.php b/WpfTest/HttpReturnStatus/InternalServerError.php new file mode 100644 index 0000000..0141e26 --- /dev/null +++ b/WpfTest/HttpReturnStatus/InternalServerError.php @@ -0,0 +1,16 @@ +message = $reason->getMessage(); + } + public function httpCode(): int { + return 500; + } + public function httpReason(): string { + return "Internal Server Error"; + } +} \ No newline at end of file diff --git a/WpfTest/HttpReturnStatus/NoHandlerFound.php b/WpfTest/HttpReturnStatus/NoHandlerFound.php new file mode 100644 index 0000000..d914ca2 --- /dev/null +++ b/WpfTest/HttpReturnStatus/NoHandlerFound.php @@ -0,0 +1,18 @@ +message = "No handler found for path $path"; + } + + public function httpCode(): int { + return 404; + } + + public function httpReason(): string { + return "Not Found"; + } +} \ No newline at end of file diff --git a/WpfTest/HttpReturnStatus/NotFound.php b/WpfTest/HttpReturnStatus/NotFound.php new file mode 100644 index 0000000..eef23b3 --- /dev/null +++ b/WpfTest/HttpReturnStatus/NotFound.php @@ -0,0 +1,18 @@ +message = $reason ?? 'Request was handled but no appropriate resource found'; + } + + public function httpCode(): int { + return 404; + } + + public function httpReason(): string { + return "Not Found"; + } +} \ No newline at end of file diff --git a/WpfTest/Router.php b/WpfTest/Router.php new file mode 100644 index 0000000..ea71657 --- /dev/null +++ b/WpfTest/Router.php @@ -0,0 +1,101 @@ +requestPath = $_GET['requestPath'] ?? $_SERVER['PATH_INFO'] ?? explode('?', $_SERVER['REQUEST_URI'], 2)[0] ?? '/'; + $this->routes = []; + } + + /** + * @return Router the singleton instance + */ + public static function getInstance() { + if (!isset(self::$instance)) { + self::$instance = new self(); + } + return self::$instance; + } + + public function processRequest() { + try { + $this->processRequestInner(); + } catch (HttpReturnStatus $e) { + http_response_code($e->httpCode()); + echo '

' . $e->httpReason() . '

' . $e->getMessage() . '
'; + } + } + + private function processRequestInner() { + try { + $route = $this->matchPath($this->requestPath); + if ($route == null) { + throw new NoHandlerFound($this->requestPath); + } + $route[1]($route[2]); // call handler with args + } catch (\Exception $e) { + if (is_a($e, HttpReturnStatus::class)) { + throw $e; + } + throw new InternalServerError($e); + } + } + + public function mount(string $pathSpec, callable $handler) { + $this->routes[] = [$this->explodePathPieces($pathSpec), $handler]; + } + + private function explodePathPieces(string $pathSpec): array { + $pieces = explode('/', $pathSpec); + if ($pieces[0] == '') + array_shift($pieces); + return $pieces; + } + + private function matchPath(string $requestPath): ?array { + // find matching candidates + $pieces = $this->explodePathPieces($requestPath); + $candidates = []; + foreach ($this->routes as $route) { + $routePieces = $route[0]; + if (count($routePieces) != count($pieces)) + continue; + $args = []; + $matches = true; + for ($i = 0; $i < count($pieces); $i++) { + if (str_starts_with($routePieces[$i], ':')) { // this is a parameter + $args[substr($routePieces[$i], 1)] = $pieces[$i]; + } elseif ($routePieces[$i] != $pieces[$i]) { + $matches = false; + break; + } + } + if ($matches) { + $candidates[] = [$route[0], $route[1], $args]; + } + } + + // select the best matching one (has the longest path, for now) + $bestScore = 0; + $bestItem = null; + foreach ($candidates as $c) { + $score = count($c[0]) - count(array_filter($c[0], function ($item) { + return str_starts_with($item, ':'); + })) * 0.1; + if ($score > $bestScore) { + $bestItem = $c; + $bestScore = $score; + } + } + return $bestItem; + } + +} -- cgit v1.3