Skip to content

Commit 0a83b9c

Browse files
committed
Refactor HTTP router for PHP 8.1, method override, controller formats
1 parent 979c066 commit 0a83b9c

16 files changed

Lines changed: 1206 additions & 101 deletions

README.md

Lines changed: 70 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,130 @@
11
# InitPHP Router
22

3-
This is an open source library that lets you create and manage advanced routes for HTTP requests.
3+
A fast, framework-agnostic HTTP router for PHP. Define expressive routes for any
4+
HTTP method, group them by prefix, domain, port or client IP, attach middleware,
5+
inject dependencies into your handlers, generate URLs from named routes and
6+
serve static files — all on top of any [PSR-7](https://www.php-fig.org/psr/psr-7/)
7+
request/response implementation.
48

5-
[![Latest Stable Version](http://poser.pugx.org/initphp/router/v)](https://packagist.org/packages/initphp/router) [![Total Downloads](http://poser.pugx.org/initphp/router/downloads)](https://packagist.org/packages/initphp/router) [![Latest Unstable Version](http://poser.pugx.org/initphp/router/v/unstable)](https://packagist.org/packages/initphp/router) [![License](http://poser.pugx.org/initphp/router/license)](https://packagist.org/packages/initphp/router) [![PHP Version Require](http://poser.pugx.org/initphp/router/require/php)](https://packagist.org/packages/initphp/router)
9+
[![Latest Stable Version](http://poser.pugx.org/initphp/router/v)](https://packagist.org/packages/initphp/router) [![Total Downloads](http://poser.pugx.org/initphp/router/downloads)](https://packagist.org/packages/initphp/router) [![License](http://poser.pugx.org/initphp/router/license)](https://packagist.org/packages/initphp/router) [![PHP Version Require](http://poser.pugx.org/initphp/router/require/php)](https://packagist.org/packages/initphp/router)
610

711
## Features
812

9-
- Full support for GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD and ANY request methods.
10-
- Variable request methods with (Laravel-like) `$_REQUEST['_method']`. (Default is off, optionally can be activated)
11-
- Controller support. (HomeController@about or HomeController::about)
12-
- Middleware/Filter (before and after) support.
13-
- Static and dynamic route patterns.
14-
- Ability to create custom parameter pattern.
15-
- Namespace support.
16-
- Route grouping support.
17-
- Domain-based routing support.
18-
- Ability to define custom 404 errors
19-
- Ability to name routes
20-
- Ability to call a class in (Symfony-like) callable functions or parameters of controller methods.
21-
- Routing by request ports.
22-
- Routing by client IP address (Via `$_SERVER['REMOTE_ADDR']`. Locally, this value can be something like `::1` or `127.0.0.1`.)
23-
- A directory path can be defined as a virtual link.
13+
- GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD and ANY methods (plus virtual `LINK` routes).
14+
- Optional Laravel-style method override via `$_REQUEST['_method']`.
15+
- Controller handlers (`Home@about`, `Home::about`, `Home->about`, or `[Home::class, 'about']`).
16+
- Before/after middleware (filters), per route and per controller.
17+
- Static and dynamic route patterns, with a customizable pattern registry.
18+
- Route grouping by prefix, domain, port and client IP.
19+
- Named routes and URL generation.
20+
- Reflection-based dependency injection for handlers, with optional [PSR-11](https://www.php-fig.org/psr/psr-11/) container support.
21+
- Customizable 404 handling.
22+
- Serve files or whole directories as virtual links (with path-traversal protection).
23+
- Optional route caching.
2424

2525
## Requirements
2626

27-
- PHP 7.2 or later
28-
- Apache is; **AllowOverride All** should be set to and **mod_rewrite** should be on.
29-
- Any library that implements the [Psr-7 HTTP Message Interface](https://www.php-fig.org/psr/psr-7/) and an emitter written for Psr-7. For example; [InitPHP HTTP](https://github.com/InitPHP/HTTP) Library
27+
- PHP 8.1 or later
28+
- Any [PSR-7](https://www.php-fig.org/psr/psr-7/) HTTP message implementation and a PSR-7 emitter. The examples below use [InitPHP HTTP](https://github.com/InitPHP/HTTP).
29+
- For pretty URLs, a front controller (`index.php`) with URL rewriting (see below).
3030

3131
## Installation
3232

33-
```
33+
```bash
3434
composer require initphp/router
3535
```
3636

37-
Is Apache `.htaccess`
37+
The examples in the documentation use the InitPHP HTTP library:
3838

39+
```bash
40+
composer require initphp/http
3941
```
42+
43+
### Web server
44+
45+
Apache (`.htaccess`):
46+
47+
```apacheconf
4048
RewriteEngine On
4149
RewriteCond %{REQUEST_FILENAME} !-f
4250
RewriteCond %{REQUEST_FILENAME} !-d
4351
RewriteRule ^(.*)$ index.php [L]
4452
```
4553

46-
Is NGINX;
54+
NGINX:
4755

48-
```
49-
server {
50-
listen 80;
51-
server_name myinitphpdomain.dev;
52-
root /var/www/myInitPHPDomain/public;
53-
index index.php;
54-
location / {
55-
try_files $uri $uri/ /index.php?$query_string;
56-
}
57-
location ~ \.php$ {
58-
fastcgi_split_path_info ^(.+\.php)(/.+)$;
59-
fastcgi_pass unix:/var/run/php7.4-fpm.sock;
60-
fastcgi_index index.php;
61-
include fastcgi.conf;
62-
fastcgi_intercept_errors on;
63-
}
56+
```nginx
57+
location / {
58+
try_files $uri $uri/ /index.php?$query_string;
6459
}
6560
```
6661

67-
## Configuration
62+
## Quick start
6863

6964
```php
70-
$config = [
71-
'paths' => [
72-
'controller' => null, //The full path to the directory where the Controller classes are kept.
73-
'middleware' => null, //The full path to the directory where the Middleware classes are kept.
74-
],
75-
'namespaces' => [
76-
'controller' => null, //Namespace prefix of Controller classes, if applicable.
77-
'middleware' => null, //Namespace prefix of Middleware classes, if applicable.
78-
],
79-
'base_path' => '/', // If you are working in a subdirectory; identifies your working directory.
80-
'variable_method' => false, // It makes the request method mutable with Laravel-like $_REQUEST['_method'].
81-
'argument_new_instance' => false, // This configuration is used for Request and Response objects that you want as arguments.
82-
];
83-
```
84-
85-
## Usage
86-
87-
The following example uses the [InitPHP HTTP](https://github.com/InitPHP/HTTP) library. If you wish, you can use this library using the command below, or you can perform similar operations using another library that uses the Psr-7 HTTP Message interface.
88-
89-
```
90-
composer require initphp/http
91-
```
65+
<?php
66+
require_once 'vendor/autoload.php';
9267

93-
_**See the Wiki for detailed documentation.**_
94-
95-
```php
96-
require_once "vendor/autoload.php";
97-
use \InitPHP\HTTP\Message\{Request, Response, Stream};
98-
use \InitPHP\HTTP\Emitter\Emitter;
99-
use \InitPHP\Router\Router;
68+
use InitPHP\HTTP\Message\{Request, Response};
69+
use InitPHP\HTTP\Emitter\Emitter;
70+
use InitPHP\Router\Router;
10071

101-
$request = Request::createFromGlobals();
72+
$request = Request::createFromGlobals();
10273
$response = new Response();
10374

104-
// Create the router object.
105-
$router = new Router($request, $response, []);
75+
$router = new Router($request, $response);
10676

107-
// ... Create routes.
10877
$router->get('/', function () {
10978
return 'Hello World!';
11079
});
11180

11281
$router->post('/login', function (Request $request, Response $response) {
113-
return $response->json([
114-
'status' => 0,
115-
'message' => 'Unauthorized',
116-
], 401);
82+
return $response->withStatus(401);
11783
});
11884

119-
// If you do not make a definition for 404 errors; An exception is thrown if there is no match with the request.
120-
$router->error_404(function () {
121-
echo 'Page Not Found';
85+
// Optional: customise the 404 response (otherwise a PageNotFoundException is thrown).
86+
$router->setNotFoundHandler(function () {
87+
return 'Page Not Found';
12288
});
12389

124-
// Resolve the current route and get the generated HTTP response object.
90+
// Resolve the current request and build the response.
12591
$response = $router->dispatch();
12692

127-
// Publish the HTTP response object.
128-
$emitter = new Emitter;
129-
$emitter->emit($response);
93+
// Emit it.
94+
(new Emitter())->emit($response);
13095
```
13196

132-
## Getting Help
97+
## Documentation
13398

134-
If you have questions, concerns, bug reports, etc, please file an issue in this repository's Issue Tracker.
99+
Full, example-driven documentation lives in [`docs/`](./docs/README.md):
135100

136-
## Getting Involved
101+
- [Installation](./docs/installation.md) · [Getting started](./docs/getting-started.md) · [Configuration](./docs/configuration.md)
102+
- [Routing](./docs/routing.md) · [Route parameters & patterns](./docs/route-parameters.md) · [Route groups](./docs/groups.md)
103+
- [Controllers](./docs/controllers.md) · [Dependency injection](./docs/dependency-injection.md) · [Middleware](./docs/middleware.md)
104+
- [Named routes & URLs](./docs/named-routes.md) · [Static file links](./docs/static-file-links.md)
105+
- [Error handling](./docs/error-handling.md) · [Caching](./docs/caching.md)
106+
- [Upgrading from 1.x to 2.0](./docs/upgrading-1.x-to-2.0.md)
137107

138-
> All contributions to this project will be published under the MIT License. By submitting a pull request or filing a bug, issue, or feature request, you are agreeing to comply with this waiver of copyright interest.
108+
## Testing
139109

140-
There are two primary ways to help:
141-
142-
- Using the issue tracker, and
143-
- Changing the code-base.
144-
145-
### Using the issue tracker
146-
147-
Use the issue tracker to suggest feature requests, report bugs, and ask questions. This is also a great way to connect with the developers of the project as well as others who are interested in this solution.
148-
149-
Use the issue tracker to find ways to contribute. Find a bug or a feature, mention in the issue that you will take on that effort, then follow the Changing the code-base guidance below.
110+
```bash
111+
composer test # PHPUnit
112+
composer phpstan # static analysis (level max)
113+
composer cs:check # coding standards (PSR-12)
114+
composer coverage:check # line coverage with an enforced floor
115+
```
150116

151-
### Changing the code-base
117+
## Contributing
152118

153-
Generally speaking, you should fork this repository, make changes in your own fork, and then submit a pull request. All new code should have associated unit tests that validate implemented features and the presence or lack of defects. Additionally, the code should follow any stylistic and architectural guidelines prescribed by the project. In the absence of such guidelines, mimic the styles and patterns in the existing code-base.
119+
Contributions are welcome. Please read the org-wide
120+
[Contributing guide](https://github.com/InitPHP/.github/blob/main/CONTRIBUTING.md)
121+
and the [Code of Conduct](https://github.com/InitPHP/.github/blob/main/CODE_OF_CONDUCT.md)
122+
before opening a pull request.
154123

155124
## Credits
156125

157126
- [Muhammet ŞAFAK](https://www.muhammetsafak.com.tr) <<info@muhammetsafak.com.tr>>
158127

159128
## License
160129

161-
Copyright &copy; 2022 [MIT Licence](./LICENSE)
130+
Released under the [MIT License](./LICENSE).

docs/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# InitPHP Router — Documentation
2+
3+
Welcome to the InitPHP Router documentation. Every page is self-contained and
4+
built around runnable examples.
5+
6+
## Getting around
7+
8+
1. [Installation](./installation.md)
9+
2. [Getting started](./getting-started.md)
10+
3. [Configuration](./configuration.md)
11+
12+
## Routing
13+
14+
4. [Routing](./routing.md) — HTTP verbs, `register()`, `add()`, `any()`
15+
5. [Route parameters & patterns](./route-parameters.md)`:type` vs `{name}`, custom patterns, optional segments
16+
6. [Route groups](./groups.md) — prefix, domain, port and client-IP grouping
17+
7. [Controllers](./controllers.md) — handler formats and resource controllers
18+
8. [Named routes & URLs](./named-routes.md) — naming routes and generating URLs
19+
20+
## Handling requests
21+
22+
9. [Dependency injection](./dependency-injection.md) — handler arguments and PSR-11 containers
23+
10. [Middleware](./middleware.md) — before/after filters
24+
11. [Static file links](./static-file-links.md) — serving files and directories
25+
12. [Error handling](./error-handling.md) — 404 handling and exceptions
26+
13. [Caching](./caching.md) — caching the compiled route table
27+
28+
## Migrating
29+
30+
14. [Upgrading from 1.x to 2.0](./upgrading-1.x-to-2.0.md)
31+
32+
---
33+
34+
Throughout the docs, `$router` is an instance built like this:
35+
36+
```php
37+
use InitPHP\HTTP\Message\{Request, Response};
38+
use InitPHP\Router\Router;
39+
40+
$router = new Router(Request::createFromGlobals(), new Response());
41+
```
42+
43+
Any PSR-7 request/response pair works; [InitPHP HTTP](https://github.com/InitPHP/HTTP)
44+
is used here for concreteness.

docs/caching.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Caching
2+
3+
Building the route table on every request is cheap, but for large applications
4+
you can cache the compiled table to a file and skip re-registration.
5+
6+
## Enabling the cache
7+
8+
```php
9+
$router = new Router($request, $response, [
10+
'cache' => [
11+
'enable' => true,
12+
'path' => __DIR__ . '/var/cache/routes.cache',
13+
'ttl' => 86400, // seconds (default: 1 day)
14+
],
15+
]);
16+
```
17+
18+
## How it works
19+
20+
- On construction, if a fresh (non-expired) cache file exists, the route table is
21+
loaded from it and the router is marked as **cached**.
22+
- While the router is cached, registration calls (`get()`, `group()`, …) are
23+
**no-ops** — the routes already came from the cache.
24+
- When the router is destroyed and it was **not** loaded from cache, the current
25+
route table is written to the cache file.
26+
27+
A typical front controller registers routes unconditionally; on the first
28+
request they are compiled and cached, and on subsequent requests (until the TTL
29+
expires) they are loaded from the cache:
30+
31+
```php
32+
$router = new Router($request, $response, [
33+
'cache' => ['enable' => true, 'path' => $cacheFile],
34+
]);
35+
36+
// These run normally the first time, and are skipped once cached.
37+
$router->get('/', 'HomeController@index');
38+
$router->resource('photos', 'PhotoController');
39+
40+
$response = $router->dispatch();
41+
```
42+
43+
## Limitations
44+
45+
- **Handlers must be cacheable.** String (`'Controller@method'`) and array
46+
handlers cache fine. **Closures cannot be serialized**, so don't enable caching
47+
if your routes use closure handlers.
48+
- Delete the cache file (or wait for the TTL) after changing your routes.
49+
- A write failure is reported as a warning and never interrupts the response.
50+
51+
Next: [Upgrading from 1.x to 2.0](./upgrading-1.x-to-2.0.md).

0 commit comments

Comments
 (0)