From 5e478cca5311138899f24b23ba1c0fbdca1cfd92 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 17 Jan 2022 22:31:59 +0100 Subject: [PATCH] Support HTTP 405 response code --- README.md | 10 +++++++--- src/Bramus/Router/Router.php | 35 ++++++++++++++++++++++++++--------- tests/RouterTest.php | 14 ++++++++++++-- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b0f61a5..2d8d4b6 100755 --- a/README.md +++ b/README.md @@ -280,11 +280,15 @@ $router->get('/cars/(\d+)', 'Car@showProfile'); ### Custom 404 -The default 404 handler sets a 404 status code and exits. You can override this default 404 handler by using `$router->set404(callable);` +The default 404 handler sets a 404 or 405 status code and exits. You can override this default 404 handler by using `$router->set404(callable);` ```php -$router->set404(function() { - header('HTTP/1.1 404 Not Found'); +$router->set404(function($handledByOtherMethod) { + if ($handledByOtherMethod) { + header('HTTP/1.1 405 Method Not Allowed'); + } else { + header('HTTP/1.1 404 Not Found'); + } // ... do something special here }); ``` diff --git a/src/Bramus/Router/Router.php b/src/Bramus/Router/Router.php index 5f05227..bfb3361 100644 --- a/src/Bramus/Router/Router.php +++ b/src/Bramus/Router/Router.php @@ -85,10 +85,10 @@ public function match($methods, $pattern, $fn) $pattern = $this->baseRoute ? rtrim($pattern, '/') : $pattern; $handledMethods = is_array($methods) ? $methods : explode('|', $methods); - foreach ($handledMethods as $method) { + foreach (self::ALL_METHODS as $method) { $this->afterRoutes[$method][] = array( 'pattern' => $pattern, - 'fn' => $fn, + 'fn' => in_array($method, $handledMethods, true) ? $fn : null, ); } } @@ -289,13 +289,16 @@ public function run($callback = null) // Handle all routes $numHandled = 0; + $numHandledByOtherMethods = 0; if (isset($this->afterRoutes[$this->requestedMethod])) { - $numHandled = $this->handle($this->afterRoutes[$this->requestedMethod], true); + $handled = $this->handle($this->afterRoutes[$this->requestedMethod], true); + $numHandled = $handled->numHandled; + $numHandledByOtherMethods = $handled->numHandledByOtherMethods; } // If no route was handled, trigger the 404 (if any) if ($numHandled === 0) { - $this->trigger404(); + $this->trigger404($numHandledByOtherMethods > 0); } // If a route was handled, perform the finish callback (if any) else { if ($callback && is_callable($callback)) { @@ -329,8 +332,10 @@ public function set404($match_fn, $fn = null) /** * Triggers 404 response + * + * @param bool $handledByOtherMethod Whether the match is handled by other method. */ - public function trigger404(){ + public function trigger404($handledByOtherMethod = false){ // Counter to keep track of the number of routes we've handled $numHandled = 0; @@ -366,16 +371,20 @@ public function trigger404(){ return isset($match[0][0]) && $match[0][1] != -1 ? trim($match[0][0], '/') : null; }, $matches, array_keys($matches)); - $this->invoke($route_callable); + $this->invoke($route_callable, [$handledByOtherMethod]); ++$numHandled; } } } if (($numHandled == 0) && (isset($this->notFoundCallback['/']))) { - $this->invoke($this->notFoundCallback['/']); + $this->invoke($this->notFoundCallback['/'], [$handledByOtherMethod]); } elseif ($numHandled == 0) { - header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); + if ($handledByOtherMethod) { + header($_SERVER['SERVER_PROTOCOL'] . ' 405 Method Not Allowed'); + } else { + header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); + } } } @@ -411,6 +420,7 @@ private function handle($routes, $quitAfterRun = false) { // Counter to keep track of the number of routes we've handled $numHandled = 0; + $numHandledByOtherMethods = 0; // The current page URL $uri = $this->getCurrentUri(); @@ -423,6 +433,10 @@ private function handle($routes, $quitAfterRun = false) // is there a valid match? if ($is_match) { + if ($route['fn'] === null) { + ++$numHandledByOtherMethods; + continue; + } // Rework matches to only contain the matches, not the orig string $matches = array_slice($matches, 1); @@ -453,7 +467,10 @@ private function handle($routes, $quitAfterRun = false) } // Return the number of routes handled - return $numHandled; + return (object) [ + 'numHandled' => $numHandled, + 'numHandledByOtherMethods' => $numHandledByOtherMethods, + ]; } private function invoke($fn, $params = array()) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index e92639c..0682ea0 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -605,8 +605,8 @@ public function test404() $router->get('/', function () { echo 'home'; }); - $router->set404(function () { - echo 'route not found'; + $router->set404(function ($handledByOtherMethod) { + echo $handledByOtherMethod ? 'method not allowed' : 'route not found'; }); $router->set404('/api(/.*)?', function () { @@ -618,11 +618,21 @@ public function test404() $this->assertEquals('home', $responseBody); }); + // Test route existing for other method + run_request($router, 'POST', '/', function($responseBody) { + $this->assertEquals('method not allowed', $responseBody); + }); + // Test non-existing route run_request($router, 'GET', '/foo', function($responseBody) { $this->assertEquals('route not found', $responseBody); }); + // Test non-existing route + run_request($router, 'POST', '/foo', function($responseBody) { + $this->assertEquals('route not found', $responseBody); + }); + // Test the custom api 404 run_request($router, 'POST', '/api/getUser', function($responseBody) { $this->assertEquals('api route not found', $responseBody);