https://github.com/rareloop/router
A powerful PHP Router for PSR7 messages inspired by the Laravel API.
https://github.com/rareloop/router
middleware php-router psr7 psr7-compliant psr7-http router
Last synced: 10 months ago
JSON representation
A powerful PHP Router for PSR7 messages inspired by the Laravel API.
- Host: GitHub
- URL: https://github.com/rareloop/router
- Owner: Rareloop
- License: mit
- Created: 2017-07-13T20:47:42.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2024-05-20T14:12:41.000Z (over 1 year ago)
- Last Synced: 2024-12-06T19:26:53.584Z (about 1 year ago)
- Topics: middleware, php-router, psr7, psr7-compliant, psr7-http, router
- Language: PHP
- Homepage:
- Size: 122 KB
- Stars: 91
- Watchers: 12
- Forks: 14
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Rare Router
[](https://packagist.org/packages/rareloop/router)

[](https://coveralls.io/github/Rareloop/router)
A simple PHP router built on [AltoRouter](https://github.com/dannyvankooten/AltoRouter) but inspired by the [Laravel](https://laravel.com/docs/5.4/routing) API.
## Installation
```
composer require rareloop/router
```
## Usage
### Creating Routes
#### Map
Creating a route is done using the `map` function:
```php
use Rareloop\Router\Router;
$router = new Router;
// Creates a route that matches the uri `/posts/list` both GET
// and POST requests.
$router->map(['GET', 'POST'], 'posts/list', function () {
return 'Hello World';
});
```
`map()` takes 3 parameters:
- `methods` (array): list of matching request methods, valid values:
+ `GET`
+ `POST`
+ `PUT`
+ `PATCH`
+ `DELETE`
+ `OPTIONS`
- `uri` (string): The URI to match against
- `action` (function|string): Either a closure or a Controller string
#### Route Parameters
Parameters can be defined on routes using the `{keyName}` syntax. When a route matches that contains parameters, an instance of the `RouteParams` object is passed to the action.
```php
$router->map(['GET'], 'posts/{id}', function(RouteParams $params) {
return $params->id;
});
```
If you need to add constraints to a parameter you can pass a regular expression pattern to the `where()` function of the defined `Route`:
```php
$router->map(['GET'], 'posts/{id}/comments/{commentKey}', function(RouteParams $params) {
return $params->id;
})->where('id', '[0-9]+')->where('commentKey', '[a-zA-Z]+');
// or
$router->map(['GET'], 'posts/{id}/comments/{commentKey}', function(RouteParams $params) {
return $params->id;
})->where([
'id', '[0-9]+',
'commentKey', '[a-zA-Z]+',
]);
```
#### Optional route Parameters
Sometimes your route parameters needs to be optional, in this case you can add a `?` after the parameter name:
```php
$router->map(['GET'], 'posts/{id?}', function(RouteParams $params) {
if (isset($params->id)) {
// Param provided
} else {
// Param not provided
}
});
```
#### Named Routes
Routes can be named so that their URL can be generated programatically:
```php
$router->map(['GET'], 'posts/all', function () {})->name('posts.index');
$url = $router->url('posts.index');
```
If the route requires parameters you can be pass an associative array as a second parameter:
```php
$router->map(['GET'], 'posts/{id}', function () {})->name('posts.show');
$url = $router->url('posts.show', ['id' => 123]);
```
If a passed in parameter fails the regex constraint applied, a `RouteParamFailedConstraintException` will be thrown.
#### HTTP Verb Shortcuts
Typically you only need to allow one HTTP verb for a route, for these cases the following shortcuts can be used:
```php
$router->get('test/route', function () {});
$router->post('test/route', function () {});
$router->put('test/route', function () {});
$router->patch('test/route', function () {});
$router->delete('test/route', function () {});
$router->options('test/route', function () {});
```
#### Setting the basepath
The router assumes you're working from the route of a domain. If this is not the case you can set the base path:
```php
$router->setBasePath('base/path');
$router->map(['GET'], 'route/uri', function () {}); // `/base/path/route/uri`
```
#### Controllers
If you'd rather use a class to group related route actions together you can pass a Controller String to `map()` instead of a closure. The string takes the format `{name of class}@{name of method}`. It is important that you use the complete namespace with the class name.
Example:
```php
// TestController.php
namespace \MyNamespace;
class TestController
{
public function testMethod()
{
return 'Hello World';
}
}
// routes.php
$router->map(['GET'], 'route/uri', '\MyNamespace\TestController@testMethod');
```
### Creating Groups
It is common to group similar routes behind a common prefix. This can be achieved using Route Groups:
```php
$router->group('prefix', function ($group) {
$group->map(['GET'], 'route1', function () {}); // `/prefix/route1`
$group->map(['GET'], 'route2', function () {}); // `/prefix/route2§`
});
```
### Middleware
PSR-15/7 Middleware can be added to both routes and groups.
#### Adding Middleware to a route
At it's simplest, adding Middleware to a route can be done by passing an object to the `middleware()` function:
```php
$middleware = new AddHeaderMiddleware('X-Key1', 'abc');
$router->get('route/uri', '\MyNamespace\TestController@testMethod')->middleware($middleware);
```
Multiple middleware can be added by passing more params to the `middleware()` function:
```php
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();
$router->get('route/uri', '\MyNamespace\TestController@testMethod')->middleware($header, $auth);
```
Or alternatively, you can also pass an array of middleware:
```php
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();
$router->get('route/uri', '\MyNamespace\TestController@testMethod')->middleware([$header, $auth]);
```
#### Adding Middleware to a group
Middleware can also be added to a group. To do so you need to pass an array as the first parameter of the `group()` function instead of a string.
```php
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$router->group(['prefix' => 'my-prefix', 'middleware' => $header]), function ($group) {
$group->map(['GET'], 'route1', function () {}); // `/my-prefix/route1`
$group->map(['GET'], 'route2', function () {}); // `/my-prefix/route2§`
});
```
You can also pass an array of middleware if you need more than one:
```php
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();
$router->group(['prefix' => 'my-prefix', 'middleware' => [$header, $auth]]), function ($group) {
$group->map(['GET'], 'route1', function () {}); // `/my-prefix/route1`
$group->map(['GET'], 'route2', function () {}); // `/my-prefix/route2§`
});
```
#### Defining Middleware on Controllers
You can also apply Middleware on a Controller class too. In order to do this your Controller must extend the `Rareloop\Router\Controller` base class.
Middleware is added by calling the `middleware()` function in your Controller's `__constructor()`.
```php
use Rareloop\Router\Controller;
class MyController extends Controller
{
public function __construct()
{
// Add one at a time
$this->middleware(new AddHeaderMiddleware('X-Key1', 'abc'));
$this->middleware(new AuthMiddleware());
// Add multiple with one method call
$this->middleware([
new AddHeaderMiddleware('X-Key1', 'abc',
new AuthMiddleware(),
]);
}
}
```
By default all Middleware added via a Controller will affect all methods on that class. To limit what methods Middleware applies to you can use `only()` and `except()`:
```php
use Rareloop\Router\Controller;
class MyController extends Controller
{
public function __construct()
{
// Only apply to `send()` method
$this->middleware(new AddHeaderMiddleware('X-Key1', 'abc'))->only('send');
// Apply to all methods except `show()` method
$this->middleware(new AuthMiddleware())->except('show');
// Multiple methods can be provided in an array to both methods
$this->middleware(new AuthMiddleware())->except(['send', 'show']);
}
}
```
### Matching Routes to Requests
Once you have routes defined, you can attempt to match your current request against them using the `match()` function. `match()` accepts an instance of Symfony's `Request` and returns an instance of Symfony's `Response`:
```php
$request = Request::createFromGlobals();
$response = $router->match($request);
$response->send();
```
#### Return values
If you return an instance of `Response` from your closure it will be sent back un-touched. If however you return something else, it will be wrapped in an instance of `Response` with your return value as the content.
#### Responsable objects
If you return an object from your closure that implements the `Responsable` interface, it's `toResponse()` object will be automatically called for you.
```php
class MyObject implements Responsable
{
public function toResponse(RequestInterface $request) : ResponseInterface
{
return new TextResponse('Hello World!');
}
}
$router->get('test/route', function () {
return new MyObject();
});
```
#### 404
If no route matches the request, a `Response` object will be returned with it's status code set to `404`;
#### Accessing current route
The currently matched `Route` can be retrieved by calling:
```php
$route = $router->currentRoute();
```
If no route matches or `match()` has not been called, `null` will be returned.
You can also access the name of the currently matched `Route` by calling:
```php
$name = $router->currentRouteName();
```
If no route matches or `match()` has not been called or the matched route has no name, `null` will be returned.
### Using with a Dependency Injection Container
The router can also be used with a PSR-11 compatible Container of your choosing. This allows you to type hint dependencies in your route closures or Controller methods.
To make use of a container, simply pass it as a parameter to the Router's constructor:
```php
use MyNamespace\Container;
use Rareloop\Router\Router;
$container = new Container();
$router = new Router($container);
```
After which, your route closures and Controller methods will be automatically type hinted:
```php
$container = new Container();
$testServiceInstance = new TestService();
$container->set(TestService::class, $testServiceInstance);
$router = new Router($container);
$router->get('/my/route', function (TestService $service) {
// $service is now the same object as $testServiceInstance
});
```