Skip to content

Commit 11cd0e7

Browse files
committed
-
Signed-off-by: azjezz <azjezz@protonmail.com>
1 parent 0c1ff34 commit 11cd0e7

21 files changed

Lines changed: 353 additions & 192 deletions

.phpcs.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
<file>src</file>
77
<file>tests</file>
8-
<file>examples</file>
9-
<file>bin</file>
108

119
<arg name="basepath" value="."/>
1210
<arg name="colors"/>

README.md

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
Hack-Routing
2-
===========
1+
# Hack-Routing
32

43
![Unit tests status](https://github.com/azjezz/hack-routing/workflows/unit%20tests/badge.svg)
54
![Static analysis status](https://github.com/azjezz/hack-routing/workflows/static%20analysis/badge.svg)
@@ -13,34 +12,74 @@ Fast, type-safe request routing, parameter retrieval, and link generation.
1312

1413
It's a port of [hack-router](https://github.com/hhvm/hack-router) By Facebook, Inc.
1514

16-
Components
17-
==========
15+
## Components
16+
17+
### HTTP Exceptions
1818

19-
HTTP Exceptions
20-
---------------
2119

2220
Exception classes representing common situations in HTTP applications:
2321

2422
- `HackRouting\HttpException\InternalServerErrorException`
2523
- `HackRouting\HttpException\MethodNotAllowedException`
2624
- `HackRouting\HttpException\NotFoundException`
2725

28-
BaseRouter
29-
----------
26+
### Router
3027

3128
A simple typed request router. Example:
3229

3330
```php
3431
<?php
3532

3633
use Psl\Str;
37-
use HackRouting\AbstractMatcher;
34+
use HackRouting\Cache;
35+
use HackRouting\Router;
36+
use HackRouting\HttpMethod;
37+
use HackRouting\HttpException;
38+
39+
$cache = new Cache\ApcuCache();
40+
$router = new Router($cache);
41+
42+
$router->route(HttpMethod::GET, '/', function(): string {
43+
return 'Hello, World!';
44+
});
45+
46+
$router->route(HttpMethod::GET, '/user/{username}/', function(array $parameters): string {
47+
return Str\format('Hello, %s!', $parameters['username']);
48+
});
49+
50+
$router->route(HttpMethod::POST, '/', function(): string {
51+
return 'Hello, POST world';
52+
});
53+
54+
try {
55+
[$responder, $parameters] = $router->match('GET', '/hello/azjezz');
56+
57+
$responder($parameters); // Hello, azjezz!
58+
} catch (HttpException\MethodNotAllowedException $e) {
59+
$allowed_methods = $e->getAllowedMethods();
60+
// Handle 403.
61+
} catch (HttpException\NotFoundException) {
62+
// Handle 404.
63+
} catch (HttpException\InternalServerErrorException) {
64+
// Handle 500.
65+
}
66+
```
67+
68+
### AbstractRouter
69+
70+
A more low-level router, which allows you to load routes using other means ( e.g. from configuration files ).
71+
72+
```php
73+
<?php
74+
75+
use Psl\Str;
76+
use HackRouting\AbstractRouter;
3877
use HackRouting\HttpMethod;
3978

4079
/**
4180
* @extends BaseRouter<(function(array<string, string>):string)>
4281
*/
43-
final class Matcher extends AbstractMatcher {
82+
final class Matcher extends AbstractRouter {
4483
/**
4584
* @return array<non-empty-string, array<string, (function(array<string, string>):string)>>
4685
*/
@@ -52,18 +91,17 @@ final class Matcher extends AbstractMatcher {
5291
],
5392

5493
HttpMethod::POST => [
55-
'/' => static fn(arrray $parameters): string => 'Hello, POST world',
94+
'/' => static fn(array $parameters): string => 'Hello, POST world',
5695
],
5796
];
5897
}
5998
}
6099
```
61100

62-
Simplified for conciseness - see [`examples/MatcherExample.php`](examples/MatcherExample.php) for full executable
101+
Simplified for conciseness - see [`examples/AbstractRouterExample.php`](examples/AbstractRouterExample.php) for full executable
63102
example.
64103

65-
UriPatterns
66-
-----------
104+
### UriPatterns
67105

68106
Generate route fragments, URIs (for linking), and retrieve URI parameters in a consistent and type-safe way:
69107

@@ -102,12 +140,26 @@ $link = UserPageController::getUriBuilder()
102140
These examples are simplified for conciseness - see [`examples/UriPatternsExample.php`](examples/UriPatternsExample.php)
103141
for full executable example.
104142

105-
Contributing
106-
============
143+
### Caching
144+
145+
HackRouting comes with 4 caching strategies.
146+
147+
- `HackRouting\Cache\ApcuCache`
148+
- `HackRouting\Cache\FileCache`
149+
- `HackRouting\Cache\MemoryCache`
150+
- `HackRouting\Cache\NullCache`
151+
152+
By default, the router will use `NullCache` strategy, however, in production, it's extremely recommended using another strategy that fits your need.
153+
154+
If your application is running behind a traditional web-server ( i.e: fpm/fast-cgi ), we recommend using `ApcuCache` strategy if possible, falling back to `FileCache`.
155+
156+
If your application is used with a long-running process server such as Amphp, ReactPHP, RoadRunner ... etc,
157+
it's recommended to use `MemoryCache` to avoid additional I/O operations, and maximize performance.
158+
159+
## Contributing
107160

108161
We welcome GitHub issues and pull requests - please see CONTRIBUTING.md for details.
109162

110-
License
111-
=======
163+
## License
112164

113165
hack-routing is MIT-licensed.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
require_once(__DIR__ . '/../vendor/autoload.php');
1010

11-
use HackRouting\AbstractMatcher;
11+
use HackRouting\AbstractRouter;
1212
use HackRouting\Cache\MemoryCache;
1313
use HackRouting\HttpMethod;
1414
use Psl\IO;
@@ -17,7 +17,7 @@
1717
/**
1818
* @extends BaseRouter<(function(array<string, string>):string)>
1919
*/
20-
final class MatcherExample extends AbstractMatcher
20+
final class AbstractRouterExample extends AbstractRouter
2121
{
2222
/**
2323
* @return array<non-empty-string, array<string, (function(array<string, string>):string)>>
@@ -51,7 +51,7 @@ function get_example_inputs(): iterable
5151

5252
(static function (): void {
5353
$output = IO\output_handle();
54-
$router = new MatcherExample(new MemoryCache());
54+
$router = new AbstractRouterExample(new MemoryCache());
5555
foreach (get_example_inputs() as $input) {
5656
[$method, $path] = $input;
5757

examples/UriPatternsExample.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
require_once(__DIR__ . '/../vendor/autoload.php');
1010

11-
use HackRouting\AbstractMatcher;
11+
use HackRouting\AbstractRouter;
1212
use HackRouting\Cache\FileCache;
1313
use HackRouting\Cache\MemoryCache;
1414
use HackRouting\HttpMethod;
@@ -102,7 +102,7 @@ public function getResponse(): string
102102
/**
103103
* @extends BaseRouter<class-string<WebController>>
104104
*/
105-
final class UriPatternsExample extends AbstractMatcher
105+
final class UriPatternsExample extends AbstractRouter
106106
{
107107
/**
108108
* @return list<class-string<WebController>>

examples/ops.php

Lines changed: 0 additions & 19 deletions
This file was deleted.

examples/simple.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
/***********
4+
* IF YOU EDIT THIS FILE also update the snippet in README.md
5+
***********/
6+
7+
namespace HackRouting\Examples\BaseRouterExample;
8+
9+
use HackRouting\AbstractRouter;
10+
use HackRouting\Cache;
11+
use HackRouting\HttpException;
12+
use HackRouting\HttpMethod;
13+
use HackRouting\Router;
14+
use Psl\IO;
15+
use Psl\Str;
16+
17+
require_once(__DIR__ . '/../vendor/autoload.php');
18+
19+
/**
20+
* @return iterable<array{0: non-empty-string, 1: string}>
21+
*/
22+
function get_example_inputs(): iterable
23+
{
24+
yield array(HttpMethod::GET, '/');
25+
yield array(HttpMethod::GET, '/user/foo');
26+
yield array(HttpMethod::GET, '/user/bar');
27+
yield array(HttpMethod::POST, '/');
28+
}
29+
30+
(static function (): void {
31+
$output = IO\output_handle();
32+
33+
$cache = new Cache\ApcuCache();
34+
$router = new Router($cache);
35+
36+
$router->route(HttpMethod::GET, '/', function (): string {
37+
return 'Hello, World!';
38+
});
39+
40+
$router->route(HttpMethod::GET, '/user/{username}', function (array $parameters): string {
41+
return Str\format('Hello, %s!', $parameters['username']);
42+
});
43+
44+
$router->route(HttpMethod::POST, '/', function (): string {
45+
return 'Hello, POST world';
46+
});
47+
48+
foreach (get_example_inputs() as [$method, $path]) {
49+
try {
50+
[$responder, $parameters] = $router->match($method, $path);
51+
52+
$response = $responder($parameters);
53+
54+
$method = Str\pad_right(Str\format('[%s]', $method), 8);
55+
$request = Str\pad_right(Str\format('%s %s', $method, $path), 25);
56+
57+
$output->write(Str\format("%s -> %s\n", $request, $response));
58+
} catch (HttpException\MethodNotAllowedException $e) {
59+
$allowed_methods = $e->getAllowedMethods();
60+
61+
// Handle 403.
62+
} catch (HttpException\NotFoundException) {
63+
// Handle 404.
64+
} catch (HttpException\InternalServerErrorException) {
65+
// Handle 500.
66+
}
67+
}
68+
69+
exit(0);
70+
})();

0 commit comments

Comments
 (0)