Merge pull request #591 from flightphp/method-not-found

Added ability to throw a method not found instead of 404
pull/594/head
n0nag0n 8 months ago committed by GitHub
commit 10165ebda3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -395,7 +395,6 @@ class Engine
$params = $route->params; $params = $route->params;
foreach ($middlewares as $middleware) { foreach ($middlewares as $middleware) {
// Assume that nothing is going to be executed for the middleware. // Assume that nothing is going to be executed for the middleware.
$middlewareObject = false; $middlewareObject = false;
@ -579,9 +578,15 @@ class Engine
if ($failedMiddlewareCheck === true) { if ($failedMiddlewareCheck === true) {
$this->halt(403, 'Forbidden', empty(getenv('PHPUNIT_TEST'))); $this->halt(403, 'Forbidden', empty(getenv('PHPUNIT_TEST')));
} elseif ($dispatched === false) { } elseif ($dispatched === false) {
// Get the previous route and check if the method failed, but the URL was good.
$lastRouteExecuted = $router->executedRoute;
if ($lastRouteExecuted !== null && $lastRouteExecuted->matchUrl($request->url) === true && $lastRouteExecuted->matchMethod($request->method) === false) {
$this->halt(405, 'Method Not Allowed', empty(getenv('PHPUNIT_TEST')));
} else {
$this->notFound(); $this->notFound();
} }
} }
}
/** /**
* Sends an HTTP 500 response for any errors. * Sends an HTTP 500 response for any errors.

@ -32,7 +32,7 @@ class Router
/** /**
* The current route that is has been found and executed. * The current route that is has been found and executed.
*/ */
protected ?Route $executedRoute = null; public ?Route $executedRoute = null;
/** /**
* Pointer to current route. * Pointer to current route.
@ -42,21 +42,21 @@ class Router
/** /**
* When groups are used, this is mapped against all the routes * When groups are used, this is mapped against all the routes
*/ */
protected string $group_prefix = ''; protected string $groupPrefix = '';
/** /**
* Group Middleware * Group Middleware
* *
* @var array<int,mixed> * @var array<int,mixed>
*/ */
protected array $group_middlewares = []; protected array $groupMiddlewares = [];
/** /**
* Allowed HTTP methods * Allowed HTTP methods
* *
* @var array<int, string> * @var array<int, string>
*/ */
protected array $allowed_methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']; protected array $allowedMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
/** /**
* Gets mapped routes. * Gets mapped routes.
@ -93,7 +93,7 @@ class Router
// Flight::route('', function() {}); // Flight::route('', function() {});
// } // }
// Keep the space so that it can execute the below code normally // Keep the space so that it can execute the below code normally
if ($this->group_prefix !== '') { if ($this->groupPrefix !== '') {
$url = ltrim($pattern); $url = ltrim($pattern);
} else { } else {
$url = trim($pattern); $url = trim($pattern);
@ -113,14 +113,14 @@ class Router
} }
// And this finishes it off. // And this finishes it off.
if ($this->group_prefix !== '') { if ($this->groupPrefix !== '') {
$url = rtrim($this->group_prefix . $url); $url = rtrim($this->groupPrefix . $url);
} }
$route = new Route($url, $callback, $methods, $pass_route, $route_alias); $route = new Route($url, $callback, $methods, $pass_route, $route_alias);
// to handle group middleware // to handle group middleware
foreach ($this->group_middlewares as $gm) { foreach ($this->groupMiddlewares as $gm) {
$route->addMiddleware($gm); $route->addMiddleware($gm);
} }
@ -197,20 +197,20 @@ class Router
/** /**
* Group together a set of routes * Group together a set of routes
* *
* @param string $group_prefix group URL prefix (such as /api/v1) * @param string $groupPrefix group URL prefix (such as /api/v1)
* @param callable $callback The necessary calling that holds the Router class * @param callable $callback The necessary calling that holds the Router class
* @param array<int, callable|object> $group_middlewares * @param array<int, callable|object> $groupMiddlewares
* The middlewares to be applied to the group. Example: `[$middleware1, $middleware2]` * The middlewares to be applied to the group. Example: `[$middleware1, $middleware2]`
*/ */
public function group(string $group_prefix, callable $callback, array $group_middlewares = []): void public function group(string $groupPrefix, callable $callback, array $groupMiddlewares = []): void
{ {
$old_group_prefix = $this->group_prefix; $oldGroupPrefix = $this->groupPrefix;
$old_group_middlewares = $this->group_middlewares; $oldGroupMiddlewares = $this->groupMiddlewares;
$this->group_prefix .= $group_prefix; $this->groupPrefix .= $groupPrefix;
$this->group_middlewares = array_merge($this->group_middlewares, $group_middlewares); $this->groupMiddlewares = array_merge($this->groupMiddlewares, $groupMiddlewares);
$callback($this); $callback($this);
$this->group_prefix = $old_group_prefix; $this->groupPrefix = $oldGroupPrefix;
$this->group_middlewares = $old_group_middlewares; $this->groupMiddlewares = $oldGroupMiddlewares;
} }
/** /**
@ -221,9 +221,14 @@ class Router
public function route(Request $request) public function route(Request $request)
{ {
while ($route = $this->current()) { while ($route = $this->current()) {
if ($route->matchMethod($request->method) && $route->matchUrl($request->url, $this->case_sensitive)) { $urlMatches = $route->matchUrl($request->url, $this->case_sensitive);
$methodMatches = $route->matchMethod($request->method);
if ($urlMatches === true && $methodMatches === true) {
$this->executedRoute = $route; $this->executedRoute = $route;
return $route; return $route;
// capture the route but don't execute it. We'll use this in Engine->start() to throw a 405
} elseif ($urlMatches === true && $methodMatches === false) {
$this->executedRoute = $route;
} }
$this->next(); $this->next();
} }
@ -299,12 +304,20 @@ class Router
return $this->routes[$this->index] ?? false; return $this->routes[$this->index] ?? false;
} }
/**
* Gets the previous route.
*/
public function previous(): void
{
--$this->index;
}
/** /**
* Gets the next route. * Gets the next route.
*/ */
public function next(): void public function next(): void
{ {
$this->index++; ++$this->index;
} }
/** /**
@ -312,6 +325,6 @@ class Router
*/ */
public function reset(): void public function reset(): void
{ {
$this->index = 0; $this->rewind();
} }
} }

@ -899,4 +899,47 @@ class EngineTest extends TestCase
$engine->start(); $engine->start();
} }
public function testRouteFoundButBadMethod() {
$engine = new class extends Engine {
public function getLoader()
{
return $this->loader;
}
};
// doing this so we can overwrite some parts of the response
$engine->getLoader()->register('response', function () {
return new class extends Response {
public function setRealHeader(
string $header_string,
bool $replace = true,
int $response_code = 0
): self {
return $this;
}
};
});
$engine->route('POST /path1/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('GET /path2/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('PATCH /path3/@id', function ($id) {
echo 'OK' . $id;
});
$engine->request()->url = '/path1/123';
$engine->request()->method = 'GET';
$engine->start();
$this->expectOutputString('Method Not Allowed');
$this->assertEquals(405, $engine->response()->status());
$this->assertEquals('Method Not Allowed', $engine->response()->getBody());
}
} }

@ -11,7 +11,6 @@ use PHPUnit\Framework\TestCase;
class ControllerCommandTest extends TestCase class ControllerCommandTest extends TestCase
{ {
protected static $in = __DIR__ . '/input.test'; protected static $in = __DIR__ . '/input.test';
protected static $ou = __DIR__ . '/output.test'; protected static $ou = __DIR__ . '/output.test';
@ -75,5 +74,4 @@ class ControllerCommandTest extends TestCase
$this->assertFileExists(__DIR__ . '/controllers/TestController.php'); $this->assertFileExists(__DIR__ . '/controllers/TestController.php');
} }
} }

@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase;
class RouteCommandTest extends TestCase class RouteCommandTest extends TestCase
{ {
protected static $in = __DIR__ . '/input.test'; protected static $in = __DIR__ . '/input.test';
protected static $ou = __DIR__ . '/output.test'; protected static $ou = __DIR__ . '/output.test';
@ -106,7 +105,8 @@ PHP;
+---------+-----------+-------+----------+----------------+', $this->removeColors(file_get_contents(static::$ou))); +---------+-----------+-------+----------+----------------+', $this->removeColors(file_get_contents(static::$ou)));
} }
public function testGetPostRoute() { public function testGetPostRoute()
{
$app = $this->newApp('test', '0.0.1'); $app = $this->newApp('test', '0.0.1');
$this->createIndexFile(); $this->createIndexFile();
$app->add(new RouteCommand(['index_root' => 'tests/commands/index.php'])); $app->add(new RouteCommand(['index_root' => 'tests/commands/index.php']));
@ -119,5 +119,4 @@ PHP;
| /post | POST | | No | Closure | | /post | POST | | No | Closure |
+---------+---------+-------+----------+------------+', $this->removeColors(file_get_contents(static::$ou))); +---------+---------+-------+----------+------------+', $this->removeColors(file_get_contents(static::$ou)));
} }
} }

Loading…
Cancel
Save