From 65b3d5445fcafb9246ed46ba818c7c373c9dd580 Mon Sep 17 00:00:00 2001 From: Austin Collier Date: Thu, 4 Jan 2024 20:21:01 -0700 Subject: [PATCH] Lots more unit testing for group based routing --- README.md | 27 ++++++++++------ flight/Engine.php | 1 - flight/Flight.php | 17 ++++++++++- flight/net/Router.php | 65 +++++++++++++++++++++++++++++++++++++-- tests/FlightTest.php | 71 +++++++++++++++++++++++++++++++++++++++++-- tests/RouterTest.php | 60 +++++++++++++++++++++++++++++++++++- 6 files changed, 224 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c8f25c0..f2856ff 100644 --- a/README.md +++ b/README.md @@ -313,17 +313,24 @@ You can even nest groups of groups: ```php Flight::group('/api', function () { Flight::group('/v1', function () { - Flight::route('/users', function () { - // Matches /api/v1/users + // Flight::get() gets variables, it doesn't set a route! See object context below + Flight::route('GET /users', function () { + // Matches GET /api/v1/users }); - Flight::route('/posts', function () { - // Matches /api/v1/posts + Flight::post('/posts', function () { + // Matches POST /api/v1/posts + }); + + Flight::put('/posts/1', function () { + // Matches PUT /api/v1/posts }); }); Flight::group('/v2', function () { - Flight::route('/users', function () { - // Matches /api/v2/users + + // Flight::get() gets variables, it doesn't set a route! See object context below + Flight::route('GET /users', function () { + // Matches GET /api/v2/users }); }); }); @@ -336,12 +343,12 @@ You can still use route grouping with the `Engine` object in the following way: ```php $app = new \flight\Engine(); $app->group('/api/v1', function (Router $router) { - $router->map('/users', function () { - // Matches /api/v1/users + $router->get('/users', function () { + // Matches GET /api/v1/users }); - $router->map('/posts', function () { - // Matches /api/v1/posts + $router->post('/posts', function () { + // Matches POST /api/v1/posts }); }); ``` diff --git a/flight/Engine.php b/flight/Engine.php index 47866ca..2628a11 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -34,7 +34,6 @@ use Throwable; * Routing * @method void route(string $pattern, callable $callback, bool $pass_route = false) Routes a URL to a callback function. * @method void group(string $pattern, callable $callback) Groups a set of routes together under a common prefix. - * @method void get(string $pattern, callable $callback, bool $pass_route = false) Routes a GET URL to a callback function. * @method void post(string $pattern, callable $callback, bool $pass_route = false) Routes a POST URL to a callback function. * @method void put(string $pattern, callable $callback, bool $pass_route = false) Routes a PUT URL to a callback function. * @method void patch(string $pattern, callable $callback, bool $pass_route = false) Routes a PATCH URL to a callback function. diff --git a/flight/Flight.php b/flight/Flight.php index 7dac34d..b1a9b1f 100644 --- a/flight/Flight.php +++ b/flight/Flight.php @@ -25,6 +25,10 @@ use flight\template\View; * * @method static void route(string $pattern, callable $callback, bool $pass_route = false) Maps a URL pattern to a callback. * @method static void group(string $pattern, callable $callback) Groups a set of routes together under a common prefix. + * @method void post(string $pattern, callable $callback, bool $pass_route = false) Routes a POST URL to a callback function. + * @method void put(string $pattern, callable $callback, bool $pass_route = false) Routes a PUT URL to a callback function. + * @method void patch(string $pattern, callable $callback, bool $pass_route = false) Routes a PATCH URL to a callback function. + * @method void delete(string $pattern, callable $callback, bool $pass_route = false) Routes a DELETE URL to a callback function. * @method static Router router() Returns Router instance. * * @method static void map(string $name, callable $callback) Creates a custom framework method. @@ -124,11 +128,22 @@ class Flight if (!$initialized) { require_once __DIR__ . '/autoload.php'; - self::$engine = new Engine(); + self::setEngine(new Engine()); $initialized = true; } return self::$engine; } + + /** + * Set the engine instance + * + * @param Engine $engine Vroom vroom! + * @return void + */ + public static function setEngine(Engine $engine): void + { + self::$engine = $engine; + } } diff --git a/flight/net/Router.php b/flight/net/Router.php index d08e039..078948c 100644 --- a/flight/net/Router.php +++ b/flight/net/Router.php @@ -63,10 +63,11 @@ class Router * @param string $pattern URL pattern to match * @param callable $callback Callback function * @param bool $pass_route Pass the matching route object to the callback + * @return void */ public function map(string $pattern, callable $callback, bool $pass_route = false): void { - $url = $this->group_prefix.trim($pattern); + $url = trim($pattern); $methods = ['*']; if (false !== strpos($url, ' ')) { @@ -75,9 +76,69 @@ class Router $methods = explode('|', $method); } - $this->routes[] = new Route($url, $callback, $methods, $pass_route); + $this->routes[] = new Route($this->group_prefix.$url, $callback, $methods, $pass_route); } + /** + * Creates a GET based route + * + * @param string $pattern URL pattern to match + * @param callable $callback Callback function + * @param bool $pass_route Pass the matching route object to the callback + * @return void + */ + public function get(string $pattern, callable $callback, bool $pass_route = false): void { + $this->map('GET ' . $pattern, $callback, $pass_route); + } + + /** + * Creates a POST based route + * + * @param string $pattern URL pattern to match + * @param callable $callback Callback function + * @param bool $pass_route Pass the matching route object to the callback + * @return void + */ + public function post(string $pattern, callable $callback, bool $pass_route = false): void { + $this->map('POST ' . $pattern, $callback, $pass_route); + } + + /** + * Creates a PUT based route + * + * @param string $pattern URL pattern to match + * @param callable $callback Callback function + * @param bool $pass_route Pass the matching route object to the callback + * @return void + */ + public function put(string $pattern, callable $callback, bool $pass_route = false): void { + $this->map('PUT ' . $pattern, $callback, $pass_route); + } + + /** + * Creates a PATCH based route + * + * @param string $pattern URL pattern to match + * @param callable $callback Callback function + * @param bool $pass_route Pass the matching route object to the callback + * @return void + */ + public function patch(string $pattern, callable $callback, bool $pass_route = false): void { + $this->map('PATCH ' . $pattern, $callback, $pass_route); + } + + /** + * Creates a DELETE based route + * + * @param string $pattern URL pattern to match + * @param callable $callback Callback function + * @param bool $pass_route Pass the matching route object to the callback + * @return void + */ + public function delete(string $pattern, callable $callback, bool $pass_route = false): void { + $this->map('DELETE ' . $pattern, $callback, $pass_route); + } + /** * Group together a set of routes * diff --git a/tests/FlightTest.php b/tests/FlightTest.php index 504ebc1..12b2772 100644 --- a/tests/FlightTest.php +++ b/tests/FlightTest.php @@ -1,5 +1,6 @@ url = '/test'; - + $this->expectOutputString('test'); Flight::start(); - } public function testStaticRouteGroup() { @@ -118,6 +120,71 @@ class FlightTest extends PHPUnit\Framework\TestCase $this->expectOutputString('test'); Flight::start(); + } + + public function testStaticRouteGet() { + + // can't actually get "get" because that gets a variable + Flight::route('GET /test', function() { + echo 'test get'; + }); + + $_SERVER['REQUEST_METHOD'] = 'GET'; + Flight::request()->url = '/test'; + + $this->expectOutputString('test get'); + Flight::start(); + } + + public function testStaticRoutePost() { + + Flight::post('/test', function() { + echo 'test post'; + }); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + Flight::request()->url = '/test'; + + $this->expectOutputString('test post'); + Flight::start(); + } + + public function testStaticRoutePut() { + + Flight::put('/test', function() { + echo 'test put'; + }); + + $_SERVER['REQUEST_METHOD'] = 'PUT'; + Flight::request()->url = '/test'; + + $this->expectOutputString('test put'); + Flight::start(); + } + public function testStaticRoutePatch() { + + Flight::patch('/test', function() { + echo 'test patch'; + }); + + $_SERVER['REQUEST_METHOD'] = 'PATCH'; + Flight::request()->url = '/test'; + + $this->expectOutputString('test patch'); + Flight::start(); + } + + public function testStaticRouteDelete() { + + Flight::delete('/test', function() { + echo 'test delete'; + }); + + $_SERVER['REQUEST_METHOD'] = 'DELETE'; + Flight::request()->url = '/test'; + + $this->expectOutputString('test delete'); + Flight::start(); } } diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 45b1f7c..5cc222d 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -106,7 +106,6 @@ class RouterTest extends PHPUnit\Framework\TestCase } // Simple path with trailing slash - // Simple path public function testPathRouteTrailingSlash() { $this->router->map('/path/', [$this, 'ok']); @@ -115,6 +114,15 @@ class RouterTest extends PHPUnit\Framework\TestCase $this->check('OK'); } + public function testGetRouteShortcut() + { + $this->router->get('/path', [$this, 'ok']); + $this->request->url = '/path'; + $this->request->method = 'GET'; + + $this->check('OK'); + } + // POST route public function testPostRoute() { @@ -125,6 +133,15 @@ class RouterTest extends PHPUnit\Framework\TestCase $this->check('OK'); } + public function testPostRouteShortcut() + { + $this->router->post('/path', [$this, 'ok']); + $this->request->url = '/path'; + $this->request->method = 'POST'; + + $this->check('OK'); + } + // Either GET or POST route public function testGetPostRoute() { @@ -135,6 +152,30 @@ class RouterTest extends PHPUnit\Framework\TestCase $this->check('OK'); } + public function testPutRouteShortcut() { + $this->router->put('/path', [$this, 'ok']); + $this->request->url = '/path'; + $this->request->method = 'PUT'; + + $this->check('OK'); + } + + public function testPatchRouteShortcut() { + $this->router->patch('/path', [$this, 'ok']); + $this->request->url = '/path'; + $this->request->method = 'PATCH'; + + $this->check('OK'); + } + + public function testDeleteRouteShortcut() { + $this->router->delete('/path', [$this, 'ok']); + $this->request->url = '/path'; + $this->request->method = 'DELETE'; + + $this->check('OK'); + } + // Test regular expression matching public function testRegEx() { @@ -403,4 +444,21 @@ class RouterTest extends PHPUnit\Framework\TestCase $this->request->url = '/client/user/123/abc'; $this->check('123abc'); } + + public function testGroupNestedRoutesWithCustomMethods() + { + $this->router->group('/client', function(Router $router) { + $router->group('/user', function(Router $router) { + $router->get('/@id', function ($id) { + echo $id; + }); + $router->post('/@id/@name', function ($id, $name) { + echo $id . $name; + }); + }); + }); + $this->request->url = '/client/user/123/abc'; + $this->request->method = 'POST'; + $this->check('123abc'); + } }