Skip to content

Commit e1642a2

Browse files
authored
Merge pull request #70 from sunrise-php/release/v2.8.0
v2.8.0
2 parents 74361b9 + f44e979 commit e1642a2

File tree

6 files changed

+269
-4
lines changed

6 files changed

+269
-4
lines changed

README.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
## Installation
1313

1414
```bash
15-
composer require 'sunrise/http-router:^2.6'
15+
composer require 'sunrise/http-router:^2.8'
1616
```
1717

1818
## QuickStart
@@ -81,13 +81,26 @@ $loader->attachArray([
8181
$router = new Router();
8282
$router->load($loader);
8383

84+
// if the router matching should be isolated for top middlewares...
85+
// for example for error handling...
86+
// [!] available from version 2.8
87+
$response = $router->run($request);
88+
8489
// if the router is used as a request handler
8590
$response = $router->handle($request);
8691

8792
// if the router is used as middleware
8893
$response = $router->process($request, $handler);
8994
```
9095

96+
```php
97+
/** @var Sunrise\Http\Router\RouteCollector $this */
98+
99+
$this->get('home', '/', new CallableRequestHandler(function ($request) {
100+
return (new ResponseFactory)->createJsonResponse(200);
101+
}));
102+
```
103+
91104
#### Strategy loading routes from descriptors (annotations or attributes)
92105

93106
```php
@@ -111,6 +124,11 @@ $loader->attachArray([
111124
$router = new Router();
112125
$router->load($loader);
113126

127+
// if the router matching should be isolated for top middlewares...
128+
// for example for error handling...
129+
// [!] available from version 2.8
130+
$response = $router->run($request);
131+
114132
// if the router is used as a request handler
115133
$response = $router->handle($request);
116134

@@ -131,13 +149,56 @@ $collector->get('home', '/', new HomeController());
131149
$router = new Router();
132150
$router->addRoute(...$collector->getCollection()->all());
133151

152+
// if the router matching should be isolated for top middlewares...
153+
// for example for error handling...
154+
// [!] available from version 2.8
155+
$response = $router->run($request);
156+
134157
// if the router is used as a request handler
135158
$response = $router->handle($request);
136159

137160
// if the router is used as middleware
138161
$response = $router->process($request, $handler);
139162
```
140163

164+
#### Error handling example
165+
166+
```php
167+
use Sunrise\Http\Message\ResponseFactory;
168+
use Sunrise\Http\Router\Exception\MethodNotAllowedException;
169+
use Sunrise\Http\Router\Exception\RouteNotFoundException;
170+
use Sunrise\Http\Router\Middleware\CallableMiddleware;
171+
use Sunrise\Http\Router\RequestHandler\CallableRequestHandler;
172+
use Sunrise\Http\Router\RouteCollector;
173+
use Sunrise\Http\Router\Router;
174+
use Sunrise\Http\ServerRequest\ServerRequestFactory;
175+
176+
use function Sunrise\Http\Router\emit;
177+
178+
$collector = new RouteCollector();
179+
180+
$collector->get('home', '/', new CallableRequestHandler(function ($request) {
181+
return (new ResponseFactory)->createJsonResponse(200);
182+
}));
183+
184+
$router = new Router();
185+
$router->addRoute(...$collector->getCollection()->all());
186+
187+
$router->addMiddleware(new CallableMiddleware(function ($request, $handler) {
188+
try {
189+
return $handler->handle($request);
190+
} catch (MethodNotAllowedException $e) {
191+
return (new ResponseFactory)->createResponse(405);
192+
} catch (RouteNotFoundException $e) {
193+
return (new ResponseFactory)->createResponse(404);
194+
} catch (Throwable $e) {
195+
return (new ResponseFactory)->createResponse(500);
196+
}
197+
}));
198+
199+
emit($router->run(ServerRequestFactory::fromGlobals()));
200+
```
201+
141202
#### Route Annotation Example
142203

143204
##### Minimal annotation view

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@
6060
},
6161
"scripts": {
6262
"test": [
63-
"phpunit --colors=always --coverage-text",
64-
"phpcs"
63+
"phpcs",
64+
"XDEBUG_MODE=coverage phpunit --colors=always --coverage-text"
6565
],
6666
"bench": [
6767
"phpbench run --report='generator: \"table\", cols: [\"groups\", \"subject\", \"mean\", \"diff\"], sort: {mean: \"asc\"}'"
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php declare(strict_types=1);
2+
3+
/**
4+
* It's free open-source software released under the MIT License.
5+
*
6+
* @author Anatoly Fenric <[email protected]>
7+
* @copyright Copyright (c) 2018, Anatoly Fenric
8+
* @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9+
* @link https://github.com/sunrise-php/http-router
10+
*/
11+
12+
namespace Sunrise\Http\Router\Middleware;
13+
14+
/**
15+
* Import classes
16+
*/
17+
use Psr\Http\Message\ResponseInterface;
18+
use Psr\Http\Message\ServerRequestInterface;
19+
use Psr\Http\Server\MiddlewareInterface;
20+
use Psr\Http\Server\RequestHandlerInterface;
21+
22+
/**
23+
* CallableMiddleware
24+
*
25+
* @since 2.8.0
26+
*/
27+
class CallableMiddleware implements MiddlewareInterface
28+
{
29+
30+
/**
31+
* The middleware callback
32+
*
33+
* @var callable
34+
*/
35+
private $callback;
36+
37+
/**
38+
* Constructor of the class
39+
*
40+
* @param callable $callback
41+
*/
42+
public function __construct(callable $callback)
43+
{
44+
$this->callback = $callback;
45+
}
46+
47+
/**
48+
* {@inheritDoc}
49+
*/
50+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
51+
{
52+
return ($this->callback)($request, $handler);
53+
}
54+
}

src/Router.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Sunrise\Http\Router\Exception\RouteAlreadyExistsException;
2525
use Sunrise\Http\Router\Exception\RouteNotFoundException;
2626
use Sunrise\Http\Router\Loader\LoaderInterface;
27+
use Sunrise\Http\Router\RequestHandler\CallableRequestHandler;
2728
use Sunrise\Http\Router\RequestHandler\QueueableRequestHandler;
2829

2930
/**
@@ -275,6 +276,28 @@ public function match(ServerRequestInterface $request) : RouteInterface
275276
throw new RouteNotFoundException('Route Not Found');
276277
}
277278

279+
/**
280+
* Runs the router
281+
*
282+
* @param ServerRequestInterface $request
283+
*
284+
* @return ResponseInterface
285+
*
286+
* @since 2.8.0
287+
*/
288+
public function run(ServerRequestInterface $request) : ResponseInterface
289+
{
290+
// lazy resolving of the given request...
291+
$routing = new CallableRequestHandler(function (ServerRequestInterface $request) : ResponseInterface {
292+
return $this->match($request)->handle($request);
293+
});
294+
295+
$handler = new QueueableRequestHandler($routing);
296+
$handler->add(...$this->getMiddlewares());
297+
298+
return $handler->handle($request);
299+
}
300+
278301
/**
279302
* {@inheritDoc}
280303
*/
@@ -295,7 +318,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
295318
{
296319
try {
297320
return $this->handle($request);
298-
} catch (MethodNotAllowedException | RouteNotFoundException $e) {
321+
} catch (MethodNotAllowedException|RouteNotFoundException $e) {
299322
$request = $request->withAttribute(self::ATTR_NAME_FOR_ROUTING_ERROR, $e);
300323

301324
return $handler->handle($request);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Sunrise\Http\Router\Tests\Middleware;
4+
5+
/**
6+
* Import classes
7+
*/
8+
use PHPUnit\Framework\TestCase;
9+
use Psr\Http\Server\MiddlewareInterface;
10+
use Sunrise\Http\Router\Middleware\CallableMiddleware;
11+
use Sunrise\Http\Router\Tests\Fixture;
12+
use Sunrise\Http\ServerRequest\ServerRequestFactory;
13+
14+
/**
15+
* CallableMiddlewareTest
16+
*/
17+
class CallableMiddlewareTest extends TestCase
18+
{
19+
20+
/**
21+
* @return void
22+
*/
23+
public function testContracts() : void
24+
{
25+
$middleware = new CallableMiddleware(function ($request, $handler) {
26+
return $handler->handle($request);
27+
});
28+
29+
$this->assertInstanceOf(MiddlewareInterface::class, $middleware);
30+
}
31+
32+
/**
33+
* @return void
34+
*/
35+
public function testRun() : void
36+
{
37+
$middleware = new CallableMiddleware(function ($request, $handler) {
38+
return $handler->handle($request);
39+
});
40+
41+
$request = (new ServerRequestFactory)->createServerRequest('GET', '/');
42+
$requestHandler = new Fixture\BlankRequestHandler();
43+
$middleware->process($request, $requestHandler);
44+
45+
$this->assertTrue($requestHandler->isRunned());
46+
}
47+
}

tests/RouterTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
use Sunrise\Http\Router\Exception\MiddlewareAlreadyExistsException;
1313
use Sunrise\Http\Router\Exception\RouteAlreadyExistsException;
1414
use Sunrise\Http\Router\Exception\RouteNotFoundException;
15+
use Sunrise\Http\Router\Middleware\CallableMiddleware;
1516
use Sunrise\Http\Router\Loader\LoaderInterface;
1617
use Sunrise\Http\Router\Route;
1718
use Sunrise\Http\Router\RouteCollection;
1819
use Sunrise\Http\Router\Router;
20+
use Sunrise\Http\Message\ResponseFactory;
1921
use Sunrise\Http\ServerRequest\ServerRequestFactory;
2022

2123
/**
@@ -299,6 +301,84 @@ public function testMatchForUndefinedRoute() : void
299301
}
300302
}
301303

304+
/**
305+
* @return void
306+
*/
307+
public function testRun() : void
308+
{
309+
$routes = [
310+
new Fixture\TestRoute(),
311+
new Fixture\TestRoute(),
312+
new Fixture\TestRoute(),
313+
];
314+
315+
$router = new Router();
316+
$router->addRoute(...$routes);
317+
$router->run((new ServerRequestFactory)
318+
->createServerRequest(
319+
$routes[1]->getMethods()[1],
320+
$routes[1]->getPath()
321+
));
322+
323+
$this->assertTrue($routes[1]->getRequestHandler()->isRunned());
324+
}
325+
326+
/**
327+
* @return void
328+
*/
329+
public function testRunWithNotAllowedMethod() : void
330+
{
331+
$routes = [
332+
new Fixture\TestRoute(),
333+
new Fixture\TestRoute(),
334+
new Fixture\TestRoute(),
335+
];
336+
337+
$router = new Router();
338+
$router->addRoute(...$routes);
339+
340+
$router->addMiddleware(new CallableMiddleware(function ($request, $handler) {
341+
try {
342+
return $handler->handle($request);
343+
} catch (MethodNotAllowedException $e) {
344+
return (new ResponseFactory)->createResponse(405);
345+
}
346+
}));
347+
348+
$response = $router->run((new ServerRequestFactory)
349+
->createServerRequest('UNKNOWN', $routes[1]->getPath()));
350+
351+
$this->assertSame(405, $response->getStatusCode());
352+
}
353+
354+
/**
355+
* @return void
356+
*/
357+
public function testRunWithNotFoundRoute() : void
358+
{
359+
$routes = [
360+
new Fixture\TestRoute(),
361+
new Fixture\TestRoute(),
362+
new Fixture\TestRoute(),
363+
];
364+
365+
$router = new Router();
366+
$router->addRoute(...$routes);
367+
368+
$router->addMiddleware(new CallableMiddleware(function ($request, $handler) {
369+
try {
370+
return $handler->handle($request);
371+
} catch (RouteNotFoundException $e) {
372+
return (new ResponseFactory)->createResponse(404);
373+
}
374+
}));
375+
376+
$response = $router->run((new ServerRequestFactory)
377+
->createServerRequest($routes[1]->getMethods()[1], '/unknown'));
378+
379+
$this->assertSame(404, $response->getStatusCode());
380+
}
381+
302382
/**
303383
* @return void
304384
*/

0 commit comments

Comments
 (0)