wepesi / routing
Simple and lightweight php module to write clean route of a web application
Requires
- php: >=7.4
- ext-json: *
- wepesi/optionsresolver: dev-master
This package is auto-updated.
Last update: 2024-10-30 16:48:14 UTC
README
A lightweight and simple object oriented PHP Router. Built by (Boss Ibrahim Mussa) and Contributors
Features
- Supports
GET
,POST
,PUT
,DELETE
,PATCH
request methods - Routing shorthands such as
get()
,post()
,put()
, … - Static Route Patterns
- Dynamic Route Patterns: Dynamic PCRE-based Route Patterns or Dynamic Placeholder-based Route Patterns
- Optional Route Subpatterns
- Subrouting / Group Routing
- Allowance of
Class@Method
calls - Before Route Middlewares
- Before Router Middlewares / Before App Middlewares
- API Groupe Routing
- Supports
X-HTTP-Method-Override
header - Custom 404
- Works fine in subfolders
Prerequisites/Requirements
- PHP 7.4 or greater
- URL Rewriting
Installation
Installation is possible using Composer
composer require wepesi/routing
Demo
A demo is included in the demo
folder. Serve it using your favorite web server, or using PHP 7.4+'s built-in server by executing php -S localhost:8080
on the shell. A .htaccess
for use with Apache is included.
Usage
Create an instance of \Wepesi\Routing\Router
, define some routes onto it, and run it.
// Require composer autoloader require __DIR__ . '/vendor/autoload.php'; // Create Router instance $router = new \Wepesi\Routing\Router(); // Define routes // ... // Run it! $router->run();
Routing
Wepesi/routing
supports GET
, POST
, PUT
, PATCH
, DELETE
HTTP request methods. Pass in a single request method.
When a route matches against the current URL (e.g. $_SERVER['REQUEST_URI']
), the attached route handling function will be executed. The route handling function must be a callable. Only the first route matched will be handled. When no matching route is found, a 404 handler will be executed.
Routing Shorthands
Shorthands for single request methods are provided:
$router->get('pattern', function() { /* ... */ }); $router->post('pattern', function() { /* ... */ }); $router->put('pattern', function() { /* ... */ }); $router->delete('pattern', function() { /* ... */ });
Note: Routes must be hooked before $router->run();
is being called.
Route Patterns
Route Patterns can be static or dynamic:
- Static Route Patterns contain no dynamic parts and must match exactly against the
path
part of the current URL. - Dynamic Route Patterns contain dynamic parts that can vary per request. The varying parts are named subpatterns and are defined using either Perl-compatible regular expressions (PCRE) or by using placeholders
Static Route Patterns
A static route pattern is a regular string representing a URI. It will be compared directly against the path
part of the current URL.
Examples:
/about
/contact
Usage Examples:
// This route handling function will only be executed when visiting http(s)://www.example.org/about $router->get('/about', function() { echo 'About Page Contents'; });
Dynamic Placeholder-based Route Patterns
This type of Route Patterns are the same as Dynamic PCRE-based Route Patterns, but with one difference: they don't use regexes to do the pattern matching but they use the more easy placeholders instead. Placeholders are strings surrounded by collumn, e.g. :name
.
Examples:
/movies/:id
/profile/:username
Placeholders are easier to use than PRCEs, but offer you less control as they internally get translated to a PRCE that matches any character (.*
).
$router->get('/movies/:movieId/photos/:photoId', function($movieId, $photoId) { echo 'Movie #' . $movieId . ', photo #' . $photoId; });
Note: the name of the placeholder should match with the name of the parameter that is passed into the route handling function.
Dynamic PCRE-based Route Patterns
This type of Route Patterns contain dynamic parts which can vary per request. The varying parts are named subpatterns and are defined using regular expressions.
Usage Examples:
// This route handling function will only be executed when visiting http(s)://www.example.org/movies/3 $router->get('/movies/:id', function($id) { echo 'Get a movie by ID:'.$id; })->with('id','[0-9]+');
Commonly used PCRE-based subpatterns within Dynamic Route Patterns are:
\d+
= One or more digits (0-9)\w+
= One or more word characters (a-z 0-9 _)[a-z0-9_-]+
= One or more word characters (a-z 0-9 _) and the dash (-).*
= Any character (including/
), zero or more[^/]+
= Any character but/
, one or more
When multiple subpatterns are defined, the resulting route handling parameters are passed into the route handling 'with` function in the order they are defined in:
// http(s)://www.example.org/articles/12-nyiragongo-volcano` $router->get('/artilces/:id-:name', function($id, $title) { echo 'Articles #' . $id . ', title #' . $title; }) ->with('id','[0-9]+') ->with('title','[a-z\0-9]+');
This will be like a kind of validation of your parameters.
Subrouting / Groupe Routing
Use $router->group($baseroute, $fn)
to group a collection of routes in to a subroute pattern. The subroute pattern is prefixed into all following routes defined in the scope. e.g. Mounting a callback $fn
onto /movies
will prefix /movies
onto all following routes.
$router->group('/movies', function() use ($router) { // will result in '/movies/' $router->get('/', function() { echo 'movies overview'; }); // will result in '/movies/id' $router->get('/:id', function($id) { echo 'movie id ' . $id; }); });
Nesting of subroutes is possible, just define a second $router->group()
in the callable that's already contained within a preceding $router->group()
.
$router->group('/articles', function() use ($router) { // will result in '/articles/' $router->get('/', function() { echo 'articles overview'; }); // $router->group('/themes', function() use ($router) { // will result in '/articles/themes' $router->get('/', function() { echo 'Articles themes overview'; }); // will result in '/articles/themes/4' $router->get('/:id', function($id) { echo 'Articles themes detail id: ' . $id; }); }); });
Class#Method
calls
We can route to the class action like so:
$router->get('/users/:id', '\Wepesi\Controller\UserController#get_users_detail');
or
$router->get('/users/:id', [\Wepesi\Controller\UserController::class,'get_users_detail']);
When a request matches the specified route URI, the get_users
method on the UserController
class will be executed. The defined route parameters will be passed to the class method.
The method can be static(not-recommend) or non-static (recommend). In case of a non-static method, a new instance of the class will be created.
$router->get('/users/profile', \Wepesi\Controller\Users::profile());
Note: In case you are using static method, dont pass as string or in array.
Before Route Middleware
wepesi/routing
supports Before Route Middlewares, which are executed before the route handling is processed.
$router->get('/articles/:id', function($id) { echo "article id is:".$id; })->middleware(function($id){ if(!filter_var($id,FILTER_VALIDATE_INT)){ echo "you should provide an integer"; exit; } });
Route middlewares are route specific, one or middleware can be set and will be executed before route function.
$router->get('/admin/:id', function($id) { echo "admin id is:".$id; })->middleware(function($id){ print_r("First middleware"); })->middleware(function($id){ print_r("Second middleware"); })->middleware(function($id){ print_r("Last middleware before Route function"); });
API Groupe Routing
You can define your API route inside the api method, and will auto complet api route fro you.
$router->api('/v1',function() use($router){ $router->group('/users',function() use($router){ $router->get('/',[appController::class,'getUsers']); }); }); // output // /api/v1/users
Overriding the request method
Use X-HTTP-Method-Override
to override the HTTP Request Method. Only works when the original Request Method is POST
. Allowed values for X-HTTP-Method-Override
are PUT
, DELETE
, or PATCH
.
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);
$router->set404(function() { header('HTTP/1.1 404 Not Found'); // ... do something special here });
You can also define multiple custom routes e.x. you want to define an /api
route, you can print a custom 404 page:
$router->set404('**)?', function() { header('HTTP/1.1 404 Not Found'); header('Content-Type: application/json'); $jsonArray = array(); $jsonArray['status'] = "404"; $jsonArray['status_text'] = "route not defined"; echo json_encode($jsonArray); });
Also supported are Class@Method
callables:
$router->set404([appController::class,'notfound']);
The 404 handler will be executed when no route pattern was matched to the current URL.
💡 You can also manually trigger the 404 handler by calling $router->trigger404()
Integration with other libraries
Integrate other libraries with wepesi/routing
by making good use of the use
keyword to pass dependencies into the handling functions.
$view = new \Wepesi\View(); $router->get('/', function() use ($view ) { $view->assign('email','ibmussafb@gmail.com'); $view->assign('github','bim-g'); $view->display("/profile.php"); }); $router->run();
Given this structure it is still possible to manipulate the output from within the After Router Middleware
A note on working with PUT
There's no such thing as $_PUT
in PHP. One must fake it:
$router->put('/movies/:username', function($username) { // Fake $_PUT $_PUT = array(); parse_str(file_get_contents('php://input'), $_PUT); // ... });
Subfolder support
$router->api('/v1',function() use($router){ include __DIR__.'/router/users.php'; });
License
wepesi/routing
is released under the Apache-2.0 license. See the enclosed LICENSE
for details.