Superpage
Superpage is a simple, fast and lightweight router for PHP apps.
Install / Use
/learn @dorkodu/SuperpageREADME
Superpage
Superpage is a simple, fast and lightweight, which means an awesome router for PHP apps!
Features
- Supports
GET,POST,PUT,DELETE,OPTIONS,PATCHandHEADrequest methods - Static Route Patterns
- Dynamic Route Patterns:
- Redirecting
- Sub-routing / Mounting Routes
- Optional Route Subpatterns
- Supports
X-HTTP-Method-Overrideheader - Custom 404 handling
- After Router Middleware / After App Middleware (Finish Callback)
- Works fine in subfolders
Prerequisites/Requirements
- PHP 7.2+ or greater
- URL Rewriting
Installation
You can require Dorkodu\Superpage\Superpage.php class, or use an autoloader like Loom or Composer.
Demo
A demo is included in the demo sub-folder. Serve it using your favorite web server, or using PHP 5.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
\Dorkodu\Superpage\Superpage. - Define your routes.
- Then run Superpage!
/*
* Require your autoloader script,
* We use Loom for that :) You can use Composer too.
*/
require __DIR__ . '/loot/loom-weaver.php';
# Create Router instance
$superpage = new \Dorkodu\Superpage\Superpage();
# Define routes
$superpage->to('/', 'GET', function() {
echo "Home";
});
$superpage->get('/about', function() {
echo "About";
});
# Run it!
$superpage->run();
Routing
Hook routes (a combination of one or more HTTP methods and a pattern) using <br>$superpage->to(pattern, method(s), callback) :
$superpage->to('...pattern', 'GET|POST|...[METHOD]', function() { ··· });
Superpage supports GET, POST, PUT, PATCH, DELETE, HEAD (see note), and OPTIONS HTTP request methods. Pass in a single request method, or multiple request methods separated by |.
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 :
$superpage->get('pattern', function() { ··· });
$superpage->post('pattern', function() { ··· });
$superpage->put('pattern', function() { ··· });
$superpage->delete('pattern', function() { ··· });
$superpage->options('pattern', function() { ··· });
$superpage->patch('pattern', function() { ··· });
You can use this shorthand for a route that can be accessed using any method :
$superpage->any('pattern', function() { ··· });
Redirecting
You can redirect a pattern to another absolute URI. This is what we choose for simplicity, may add parameters or pattern-to-pattern matches in the future. If you so desire, let us know by opening an issue. Maybe we can collaborate!
/*
* 3rd parameter is request method, defaults to 'GET'
* 4th parameter is status code, defaults to 301
*/
$superpage->redirect('pattern', '/redirect-to', $method, $statusCode);
Note : Routes must be defined before
$superpage->run();is being called.
Note : There is no shorthand like
head()as Superpage will internally re-route such requests to their equivalentGETrequest, in order to comply with RFC2616 (see note).
Route Patterns
Route Patterns can be static or dynamic:
-
Static Route Patterns
They contain no dynamic parts and must match exactly against the
pathpart of the current URL. -
Dynamic Route Patterns
They 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.
Example : /about
Usage Example :
// This route handling function will only be executed when visiting http(s)://www.example.org/about
$superpage->get('/about', function() {
echo 'About Page Contents';
});
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.
Examples:
/user/(\d+)/blog/(\w+)
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
Note: The PHP PCRE Cheat Sheet might come in handy.
The subpatterns defined in Dynamic PCRE-based Route Patterns are converted to parameters which are passed into the route handling function. Prerequisite is that these subpatterns need to be defined as parenthesized subpatterns, which means that they should be wrapped between parens:
// Bad
$superpage->get('/hello/\w+', function($name) {
echo 'Hello ' . htmlentities($name);
});
// Good
$superpage->get('/hello/(\w+)', function($name) {
echo 'Hello ' . htmlentities($name);
});
Note: The leading / at the very beginning of a route pattern is not mandatory, but recommended.
When multiple subpatterns are defined, the resulting route handling parameters are passed into the route handling function in the order they are defined in:
$superpage->get('/movies/(\d+)/photos/(\d+)', function($movieId, $photoId) {
echo 'Movie #' . $movieId . ', photo #' . $photoId;
});
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 regex to do the pattern matching but they use the more easy placeholders instead. Placeholders are strings surrounded by curly braces, e.g. {name}. You don't need to add paren's around placeholders.
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 (.*).
$superpage->get('/user/{userId}/post/{postId}', function($userId, $postId) {
echo 'User #' . $userId . ', post #' . $postId;
});
Note: the name of the placeholder does NOT NEED TO MATCH with the name of the parameter that is passed into the route handling function :
$superpage->get('/movies/{foo}/photos/{bar}', function($movieId, $photoId) {
echo 'Movie #' . $movieId . ', photo #' . $photoId;
});
Optional Route Subpatterns
Route subpatterns can be made optional by making the subpatterns optional by adding a ? after them. Think of blog URLs in the form of /blog(/year)(/month)(/day)(/slug):
$superpage->get(
'/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?',
function($year = null, $month = null, $day = null, $slug = null) {
if (!$year) { echo 'Blog overview'; return; }
if (!$month) { echo 'Blog year overview'; return; }
if (!$day) { echo 'Blog month overview'; return; }
if (!$slug) { echo 'Blog day overview'; return; }
echo 'Blogpost ' . htmlentities($slug) . ' detail';
}
);
The code snippet above responds to the URLs /blog, /blog/year, /blog/year/month, /blog/year/month/day, and /blog/year/month/day/slug.
Note: With optional parameters it is important that the leading / of the subpatterns is put inside the subpattern itself. Don't forget to set default values for the optional parameters.
The code snipped above unfortunately also responds to URLs like /blog/foo and states that the overview needs to be shown - which is incorrect. Optional subpatterns can be made successive by extending the parenthesized subpatterns so that they contain the other optional subpatterns: The pattern should resemble /blog(/year(/month(/day(/slug)))) instead of the previous /blog(/year)(/month)(/day)(/slug):
$superpage->get('/blog(/\d+(/\d+(/\d+(/[a-z0-9_-]+)?)?)?)?', function($year = null, $month = null, $day = null, $slug = null) {
# ...
});
Note: It is highly recommended to always define successive optional parameters.
To make things complete use quantifiers to require the correct amount of numbers in the URL:
$superpage->get('/blog(/\d{4}(/\d{2}(/\d{2}(/[a-z0-9_-]+)?)?)?)?', function($year = null, $month = null, $day = null, $slug = null) {
# ...
});
Sub-routing / Mounting Routes
Use $superpage->mount($baseroute, $callback) to mount a collection of routes onto a sub-route pattern. The sub-route pattern is prefixed onto all following routes defined in the scope.
e.g. Mounting a callback $callback onto /people will prefix /people onto all following routes.
$superpage->mount('/people', function() use ($superpage) {
# will result in '/people/'
$superpage->get('/', function() {
echo 'people overview';
});
# will result in '/people/id'
$superpage->get('/(\d+)', fu
