RouterMiddleware::dispatch()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 6
nop 2
dl 0
loc 19
ccs 10
cts 10
cp 1
crap 4
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace Simply\Application\Middleware;
4
5
use Psr\Container\ContainerInterface;
6
use Psr\Http\Message\ResponseFactoryInterface;
7
use Psr\Http\Message\ResponseInterface;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Message\StreamFactoryInterface;
10
use Psr\Http\Server\MiddlewareInterface;
11
use Psr\Http\Server\RequestHandlerInterface;
12
use Simply\Application\Handler\MiddlewareHandler;
13
use Simply\Router\Exception\MethodNotAllowedException;
14
use Simply\Router\Exception\RouteNotFoundException;
15
use Simply\Router\Route;
16
use Simply\Router\Router;
17
18
/**
19
 * Routing middleware that uses router to point requests to appropriate handlers.
20
 * @author Riikka Kalliomäki <[email protected]>
21
 * @copyright Copyright (c) 2018 Riikka Kalliomäki
22
 * @license http://opensource.org/licenses/mit-license.php MIT License
23
 */
24
class RouterMiddleware implements MiddlewareInterface
25
{
26
    /** @var ContainerInterface Container used to load handlers and additional middleware */
27
    private $container;
28
29
    /** @var Router The router used to map requests to handlers */
30
    private $router;
31
32
    /** @var ResponseFactoryInterface The http factory used to generate error responses */
33
    private $responseFactory;
34
35
    /** @var StreamFactoryInterface The http factory used to generate error bodies */
36
    private $streamFactory;
37
38
    /**
39
     * RouterMiddleware constructor.
40
     * @param Router $router The router used to map requests to handlers
41
     * @param ContainerInterface $container Container used to load handlers and additional middleware
42
     * @param ResponseFactoryInterface $responseFactory The http factory used to generate error responses
43
     * @param StreamFactoryInterface $streamFactory The http factory used to generate error bodies
44
     */
45 12
    public function __construct(
46
        Router $router,
47
        ContainerInterface $container,
48
        ResponseFactoryInterface $responseFactory,
49
        StreamFactoryInterface $streamFactory
50
    ) {
51 12
        $this->router = $router;
52 12
        $this->container = $container;
53 12
        $this->responseFactory = $responseFactory;
54 12
        $this->streamFactory = $streamFactory;
55 12
    }
56
57
    /** {@inheritdoc} */
58 12
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
59
    {
60 12
        $uri = $request->getUri();
61 12
        $path = urldecode($uri->getPath());
62
63
        try {
64 12
            $route = $this->router->route($request->getMethod(), $path);
65 2
        } catch (MethodNotAllowedException $exception) {
66 1
            return $this->responseFactory->createResponse(405, 'Method Not Allowed')
67 1
                ->withHeader('Allow', implode(', ', $exception->getAllowedMethods()))
68 1
                ->withHeader('Content-Type', 'text/plain; charset=utf-8')
69 1
                ->withBody($this->streamFactory->createStream('Method Not Allowed'));
70 1
        } catch (RouteNotFoundException $exception) {
71 1
            return $handler->handle($request);
72
        }
73
74 10
        if ($request->getMethod() === 'GET' && $route->getPath() !== $path) {
75 1
            return $this->responseFactory->createResponse(302, 'Found')
76 1
                ->withHeader('Location', (string) $uri->withPath($route->getUrl()));
77
        }
78
79 9
        return $this->dispatch($route, $request);
80
    }
81
82
    /**
83
     * Dispatches the given route for the given request.
84
     * @param Route $route The route to dispatch
85
     * @param ServerRequestInterface $request The request to provide for the dispatched route
86
     * @return ResponseInterface The response result from the route
87
     */
88 9
    private function dispatch(Route $route, ServerRequestInterface $request): ResponseInterface
89
    {
90 9
        foreach ($route->getParameters() as $name => $value) {
91 1
            $request = $request->withAttribute($name, $value);
92
        }
93
94 9
        $handler = $route->getHandler();
95
96 9
        if (\is_string($handler)) {
97 8
            return $this->getHandler($handler)->handle($request);
98
        }
99
100 1
        $stack = new MiddlewareHandler($this->getHandler($handler['handler']));
101
102 1
        foreach ($handler['middleware'] ?? [] as $name) {
103 1
            $stack->push($this->getMiddleware($name));
104
        }
105
106 1
        return $stack->handle($request);
107
    }
108
109
    /**
110
     * Loads the request handler from the container.
111
     * @param string $name The name of the dependency to load
112
     * @return RequestHandlerInterface The request handler loaded from the container
113
     */
114 9
    private function getHandler(string $name): RequestHandlerInterface
115
    {
116 9
        return $this->container->get($name);
117
    }
118
119
    /**
120
     * Loads a middleware from the container.
121
     * @param string $name The name of the dependency
122
     * @return MiddlewareInterface The middleware loaded from the container
123
     */
124 1
    private function getMiddleware(string $name): MiddlewareInterface
125
    {
126 1
        return $this->container->get($name);
127
    }
128
}
129