From aec1a91f9302019d9afd39f16f0b5ef6c317398a Mon Sep 17 00:00:00 2001 From: Igor Ilic Date: Fri, 1 Oct 2021 14:23:53 +0200 Subject: [PATCH] Request method wrappers (#12) Add new method wrappers: * Add a wrapper for adding `get` endpoint * Add a wrapper for adding `post` endpoint * Add a wrapper for adding `put` endpoint * Add a wrapper for adding `patch` endpoint * Add a wrapper for adding `delete` endpoint Sample: ```php $routes ->prefix("/test") ->middleware(['decode_token']) ->route("/t0", function(Request $request){}) ->get("/t1", function (){}) ->post("/t2", function (){}) ->put("/t3", function (){}) ->patch("/t4", function (){}) ->delete("/t5", function (){}) ->save(); ``` Which is equivalent to doing: ```php $routes ->prefix("/test") ->middleware(['decode_token']) ->route("/t0", function(Request $request){}) ->route("/t1", function (){}, [Routes::GET]) ->route("/t2", function (){}, [Routes::POST]) ->route("/t3", function (){}, [Routes::PATCH]) ->route("/t4", function (){}, [Routes::PUT]) ->route("/t5", function (){}, [Routes::DELETE]) ->save(); ``` --- README.md | 33 ++++++++++++-- Request.php | 6 ++- Routes.php | 112 ++++++++++++++++++++++++++++++++++++++++------- sample/index.php | 51 ++++++++++++++++----- 4 files changed, 171 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index c4fdf38..dfd8044 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,12 @@ try { ->status(200, "OK") ->send(["message" => "Welcome"]); }); + + $routes->route('/', function (Request $request) { + $request + ->status(200, "OK") + ->send(["message" => "Welcome"]); + })->save(); $routes->route(); } catch (RouteNotFoundException $ex) { @@ -80,12 +86,18 @@ try { ## Examples -Using chained method to wrap multiple routes with a same middleware or a route prefix +### Chained routes + +Using chained method to wrap multiple routes with a same middleware or a route prefix When using chained method either +use `save()` or `add()` as the last method to indicate the end of a chain; + +**NOTE** +Both the `save` and `add` methods **CAN'T** be chained on, so they need to be the last one in the chain. ```php $routes ->prefix('/user') // all the routes add will have the /user prefix - ->middleware([ 'verify_token' ]) // all the routes added will have the verify_token middelware applied + ->middleware([ 'verify_token' ]) // all the routes added will have the verify_token middleware applied ->route('/', [ HomeController::class, 'getUsers' ], Routes::GET) ->route('/', [ HomeController::class, 'addUser' ], Routes::POST) ->route('/', [ HomeController::class, 'updateUser' ], Routes::PATCH) @@ -93,7 +105,20 @@ $routes ->add('/test', [ HomeController::class, 'deleteUser' ], Routes::DELETE); ``` -Dynamic routes example: +```php +$routes + ->prefix("/test") + ->middleware(['decode_token']) + ->route("/t0", function(Request $request){}) + ->get("/t1", function (){}) + ->post("/t2", function (){}) + ->put("/t3", function (){}) + ->patch("/t4", function (){}) + ->delete("/t5", function (){}) + ->save(); +``` + +### Dynamic routes example ```php $routes->add('/test/{int:userID}-{username}/{float:amount}/{bool:valid}', function ( @@ -107,7 +132,7 @@ $routes->add('/test/{int:userID}-{username}/{float:amount}/{bool:valid}', functi }); ``` -For more example look in the [sample folder](/sample). +For more example look in the [sample folder](/sample) `index.php` file ## Documentation diff --git a/Request.php b/Request.php index 12b493a..9e64339 100644 --- a/Request.php +++ b/Request.php @@ -76,7 +76,11 @@ public function send(string|array|object $output) { echo json_encode($output); } - + /** + * Private method used for parsing request body data for PUT and PATCH requests + * + * @return array Return an array of request body data + */ private function parse_patch_and_put_request_data() : array { /* PUT data comes in on the stdin stream */ diff --git a/Routes.php b/Routes.php index 871665c..d7b0b1f 100644 --- a/Routes.php +++ b/Routes.php @@ -86,7 +86,7 @@ public function __construct() * * @param string $prefix Prefix to be added to all the routes in that chain. * - * @return Routes Returns an instance of it self so that other methods could be chained onto it + * @return Routes Returns an instance of itself so that other methods could be chained onto it */ public function prefix(string $prefix = ''): self { @@ -99,7 +99,7 @@ public function prefix(string $prefix = ''): self * * @param array $data List of middlewares to be executed before the routes * - * @return Routes Returns an instance of it self so that other methods could be chained onto it + * @return Routes Returns an instance of itself so that other methods could be chained onto it */ public function middleware(array $data): self { @@ -110,9 +110,16 @@ public function middleware(array $data): self /** * Method used to handle execution of routes and middlewares * + * @param string $path Path for the route + * @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed + * @param string|array $methods Allowed request method(s) (GET, POST, PUT...) + * + * @return Routes Returns an instance of itself so that other methods could be chained onto it */ - public function route(string $path, callable|array|string $callback, string|array $methods = self::GET): self - { + public function route(string $path, + callable|array|string|null $callback, + string|array $methods = self::GET + ) : self { if ( is_string($methods) ) $methods = [ $methods ]; if ( !empty($this->prefix) ) $path = $this->prefix . $path; // Prepend prefix to routes @@ -143,6 +150,7 @@ public function route(string $path, callable|array|string $callback, string|arra } } + $this->save(false); return $this; } @@ -154,10 +162,16 @@ public function route(string $path, callable|array|string $callback, string|arra * @param string|array|null $methods Allowed request method(s) (GET, POST, PUT...) * */ - public function add(string $path = '', callable|array|string|null $callback = NULL, string|array|null $methods = self::GET) - { + public function add( + string $path = '', + callable|array|string|null $callback = NULL, + string|array|null $methods = self::GET + ) { $this->route($path, $callback, $methods); + $this->save(); + } + public function save(bool $cleanData = true) { foreach ( $this->tmpRoutes as $method => $route ) { if ( !isset($this->routes[$method]) ) $this->routes[$method] = []; $path = array_key_first($route); @@ -175,9 +189,11 @@ public function add(string $path = '', callable|array|string|null $callback = NU $this->routes[$method] = array_merge($this->routes[$method], $route); } - $this->prefix = ''; - $this->middlewares = []; - $this->tmpRoutes = []; + if ( $cleanData ) { + $this->prefix = ''; + $this->middlewares = []; + $this->tmpRoutes = []; + } } /** @@ -186,8 +202,7 @@ public function add(string $path = '', callable|array|string|null $callback = NU * @throws RouteNotFoundException When the route was not found * @throws CallbackNotFound When the callback for the route was not found */ - public function handle() - { + public function handle() { $path = $this->getPath(); $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; @@ -245,7 +260,7 @@ public function handle() if ( ( is_string($callback) && class_exists($callback) ) || is_array($callback) ) { $controller = is_string($callback) ? new $callback : new $callback[0]; // make a new instance of a controller class - $fn = is_string($callback) ? 'index' : $callback[1] ?? 'index'; // get the method to be execute or fallback to index method + $fn = is_string($callback) ? 'index' : $callback[1] ?? 'index'; // get the method to be executed or fallback to index method $callback = [ $controller, $fn ]; } @@ -266,9 +281,10 @@ public function handle() } /** - * Private method used to fetch the arguments of the routs callback methods + * Private method used to fetch the arguments of the route's callback methods * * @param object|array|string $func + * * @return array|null Returns a list of arguments for a method or null on error */ private function get_all_arguments(object|array|string $func): array|null @@ -302,7 +318,7 @@ private function get_all_arguments(object|array|string $func): array|null } /** - * Method which executes each specified middleware before the routes callback is executed + * Method which executes each specified middleware before the route's callback is executed * * @param array $data List of middlewares to be executed before accessing the endpoint * @@ -341,8 +357,72 @@ private function getPath(): string * * @return array */ - public function getRoutes(): array - { + public function getRoutes() : array { return $this->routes; } + + /** + * Wrapper method used for adding new GET routes + * + * @param string $path Path for the route + * @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed + * + * @return Routes Returns an instance of itself so that other methods could be chained onto it + */ + public function get(string $path, callable|array|string|null $callback = NULL) : self { + $this->route($path, $callback, [ self::GET ]); + return $this; + } + + /** + * Wrapper method used for adding new POST routes + * + * @param string $path Path for the route + * @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed + * + * @return Routes Returns an instance of itself so that other methods could be chained onto it + */ + public function post(string $path, callable|array|string|null $callback = NULL) : self { + $this->route($path, $callback, [ self::POST ]); + return $this; + } + + /** + * Wrapper method used for adding new POST routes + * + * @param string $path Path for the route + * @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed + * + * @return Routes Returns an instance of itself so that other methods could be chained onto it + */ + public function put(string $path, callable|array|string|null $callback = NULL) : self { + $this->route($path, $callback, [ self::PUT ]); + return $this; + } + + /** + * Wrapper method used for adding new POS routes + * + * @param string $path Path for the route + * @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed + * + * @return Routes Returns an instance of itself so that other methods could be chained onto it + */ + public function patch(string $path, callable|array|string|null $callback = NULL) : self { + $this->route($path, $callback, [ self::PATCH ]); + return $this; + } + + /** + * Wrapper method used for adding new POST routes + * + * @param string $path Path for the route + * @param callable|array|string|null $callback Callback method, an anonymous function or a class and method name to be executed + * + * @return Routes Returns an instance of itself so that other methods could be chained onto it + */ + public function delete(string $path, callable|array|string|null $callback = NULL) : self { + $this->route($path, $callback, [ self::DELETE ]); + return $this; + } } \ No newline at end of file diff --git a/sample/index.php b/sample/index.php index 70a9f43..422d0be 100644 --- a/sample/index.php +++ b/sample/index.php @@ -18,10 +18,25 @@ $routes = new Routes(); try { + // When using chained method either use `save()` or `add()` method at the end to indicate an end of chain + $routes + ->prefix("/test") + ->middleware([ 'decode_token' ]) + ->get("/t1", function () { }) + ->get("/t2", function () { }) + ->get("/t3", function () { }) + ->save(); + $routes->add('/', function (Request $request) { echo json_encode([ 'message' => 'Hello World' ]); }); + $routes->add('/test', function (Request $request) { + $request + ->status(200, 'OK') + ->send([ 'message' => 'Welcome' ]); + }); + $routes ->prefix('/user') ->middleware([ 'verify_token' ]) @@ -31,26 +46,42 @@ ->route('/', [ HomeController::class, 'replaceUser' ], Routes::PUT) ->add('/test', [ HomeController::class, 'deleteUser' ], Routes::DELETE); - $routes->add('/test', function (Request $request) { - $request - ->status(200, 'OK') - ->send([ 'message' => 'Welcome' ]); - }); $routes->add('/test', function () { }, [ Routes::PATCH, Routes::POST ]); + $routes->get("/test-get", function () { + echo "Hello from test-get"; + }); + + $routes->post("/test-post", function () { + echo "Hello from test-post"; + }); + + $routes->put("/test-put", function () { + echo "Hello from test-put"; + }); + + $routes->patch("/test-patch", function () { + echo "Hello from test-patch"; + }); + + $routes->delete("/test-delete", function () { + echo "Hello from test-delete"; + }); + $routes->add('/test/{int:userID}-{username}/{float:amount}/{bool:valid}', function ( Request $request, - int $userID, - string $username, - float $amount, - bool $valid + int $userID, + string $username, + float $amount, + bool $valid ) { echo 'Dynamic route here'; }); - $routes->add('/test/{int:userID}-{username}/{float:amount}/{bool:valid}', [ HomeController::class, 'test' ]); # It works like this also + $routes->add('/test/{int:userID}-{username}/{float:amount}/{bool:valid}', + [ HomeController::class, 'test' ]); # It works like this also $routes ->middleware([