Quick start
A simple router create with Opis Pattern
This library is abandoned. Please consider using a different library.
To illustrate the usage of Opis Pattern we will create a simple router which will be able to register route patterns with corresponding callable functions, and execute the matching route.
If you are interested in an advanced routing library, check out Opis Routing.
Router class
use Opis\Pattern\RegexBuilder;
class Router {
protected $builder;
protected $routes = [];
public function __construct()
{
$this->builder = new RegexBuilder();
}
public function register(string $route, callable $action): self
{
// Generate a regex for the route
$regex = $this->builder->getRegex($route);
// Save the regex and the action
$this->routes[$regex] = $action;
return $this;
}
public function execute(string $path)
{
// We reverse the routes order, so the last registered
// is the first called
$ordered_routes = array_reverse($this->routes, true);
// Loop through all routes until one is matched
foreach ($ordered_routes as $regex => $action) {
if ($this->builder->matches($regex, $path)) {
// Get the values of placeholders
$values = $this->builder->getValues($regex, $path);
// Invoke the action
$data = $action($path, $values);
// If the action returned false,
// we continue to search for another route
if ($data === false) {
continue;
}
return $data;
}
}
// Nothing matched
return false;
}
}
__construct
We are just creating a Opis\Pattern\RegexBuilder
using the
default options,
this means that the separator symbol is /
(slash),
the capture mode is left
with trailing allowed.
usage
$router = new Router();
register
This method will assign a callable to a pattern, by converting the pattern to a regex and storing them into an internal mapper.
usage
$router->register($pattern, function (string $path, array $values) {
// ...
});
execute
Will search for first regex that matches the path, and invoke
the corresponding callback with the path and placeholder values
as parameters. If the callback returns false
, the search for a callback
will continue until something different than false
is returned.
The search starts from the last registered route.
usage
$result = $router->execute($path);
Usage example
Lets start by creating a router that can perform an action to a target,
so the pattern will look like this: action/target
.
$router = new Router();
// Registering routes
$router->register('greet/friend', function () {
return "Hi";
});
$router->register('call/mom', function () {
return "Dial 0123456789";
});
// Invokig
echo $router->execute('greet/friend'); //> Hi
echo $router->execute('call/mom'); //> Dial 0123456789
We have a call call
action which currently can phone to
a single person only. Lets use the contact list.
$router->register('call/{person}', function (string $path, array $values) {
$contacts = [
'mom' => '0123456789',
'dad' => '9876543210',
'bro' => '9999999999',
];
$person = $values['person'];
if (!isset($contacts[$person])) {
// We don't have the person in the contact list
return false;
}
return "Dial " . $contacts[$person];
});
// Invokig
echo $router->execute('call/mom'); //> Dial 0123456789
echo $router->execute('call/data'); //> Dial 9876543210
echo $router->execute('call/bro'); //> Dial 9999999999
Well, we should be able to dial a number directly, without being in contact list, so a new route can handle that.
$router->register('call/{number=\d+}', function (string $path, array $values) {
$number = $values['number'];
return "Dial " . $number;
});
// Invoking
echo $router->execute('call/991111'); //> Dial 991111
echo $router->execute('call/111122'); //> Dial 111122
echo $router->execute('call/mom'); //> Dial 0123456789
As you can see, we restricted the number placeholder to match only digits.
Now lets handle the greet
action. We consider the target as being the person’s
name.
$router->register('greet/{name}', function (string $path, array $values) {
return "Hello, " . $values['name'] . '!';
});
// Invokig
echo $router->execute('greet/World'); //> Hello, World!
echo $router->execute('greet/developers'); //> Hello, developers!
It will be cool if we can use the time of the day to have a personalized greeting.
$router->register('greet/{name}/{time_of_day}', function (string $path,
array $values) {
$map = [
'morning' => 'Good morning',
'noon' => 'Good afternoon',
'evening' => 'Good evening',
'night' => 'Good night',
];
$time = $values['time_of_day'];
if (!isset($map[$time]) {
return false;
}
return $map[$time] . ',' . $values['name'] . '!';
});
// Invokig
echo $router->execute('greet/World/morning'); //> Good morning, World!
echo $router->execute('greet/people/evening'); //> Good evening, people!
If you want to handle all unhandled actions, you should register a route right after the router creation.
$router = new Router();
$router->register('{action}/{target?}', function (string $path, array $values) {
if (isset($values['target']) && $values['target'] !== '') {
return "I don't know how to "
. $values['action'] . " "
. $values['target'] . ".";
}
return "I can't " . $values['action'] . ".";
});
// Rest of the code ...
$router->execute('stop/war'); //> I don't know how to stop war.
$router->execute('sleep'); //> I can't sleep.