Completed
Push — master ( c0f250...321e24 )
by Filipe
15:10
created

DispatcherMiddleware   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 121
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 8
lcom 1
cbo 8
dl 0
loc 121
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A process() 0 16 2
A route() 0 12 2
A merge() 0 5 1
A responseFrom() 0 13 2
1
<?php
2
3
/**
4
 * This file is part of slick/web_stack package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\WebStack\Http\Server;
11
12
use Aura\Router\Route;
13
use Interop\Http\Server\MiddlewareInterface;
14
use Interop\Http\Server\RequestHandlerInterface;
15
use Psr\Http\Message\ResponseInterface;
16
use Psr\Http\Message\ServerRequestInterface;
17
use Slick\WebStack\Controller\ContextCreator;
18
use Slick\WebStack\Controller\ControllerContextInterface;
19
use Slick\WebStack\Dispatcher\ControllerDispatchInflectorInterface;
20
use Slick\WebStack\Dispatcher\ControllerInvokerInterface;
21
use Slick\WebStack\Exception\BadHttpStackConfigurationException;
22
use Slick\WebStack\Exception\MissingResponseException;
23
24
/**
25
 * DispatcherMiddleware
26
 *
27
 * @package Slick\WebStack\Http\Server
28
 */
29
class DispatcherMiddleware implements MiddlewareInterface
30
{
31
    /**
32
     * @var ControllerDispatchInflectorInterface
33
     */
34
    private $inflector;
35
36
    /**
37
     * @var ControllerInvokerInterface
38
     */
39
    private $invoker;
40
41
    /**
42
     * @var ContextCreator
43
     */
44
    private $contextCreator;
45
46
    /**
47
     * DispatcherMiddleware constructor.
48
     * @param ControllerDispatchInflectorInterface $inflector
49
     * @param ControllerInvokerInterface $invoker
50
     * @param ContextCreator $contextCreator
51
     */
52
    public function __construct(
53
        ControllerDispatchInflectorInterface $inflector,
54
        ControllerInvokerInterface $invoker,
55
        ContextCreator $contextCreator
56
    ) {
57
        $this->inflector = $inflector;
58
        $this->invoker = $invoker;
59
        $this->contextCreator = $contextCreator;
60
    }
61
62
    /**
63
     * Process an incoming server request and return a response, optionally delegating
64
     * response creation to a handler.
65
     *
66
     * @param ServerRequestInterface $request
67
     * @param RequestHandlerInterface $handler
68
     *
69
     * @return ResponseInterface
70
     *
71
     * @throws BadHttpStackConfigurationException When the need route is missing from the request
72
     * @throws MissingResponseException When context has disabled the rendering and has no response object to return
73
     */
74
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler)
75
    {
76
        $route = $this->route($request);
77
        $controllerDispatch = $this->inflector->inflect($route);
78
        $context = $this->contextCreator->create($request, $route);
79
80
        $data = $this->invoker->invoke($context, $controllerDispatch);
81
82
        if ($context->handlesResponse()) {
83
            return $this->responseFrom($context);
84
        }
85
86
        $request = $request->withAttribute('viewData', $this->merge($request, $data));
87
88
        return $handler->handle($request);
89
    }
90
91
    /**
92
     * Get route attribute from request
93
     *
94
     * @param ServerRequestInterface $request
95
     *
96
     * @return Route
97
     *
98
     * @throws BadHttpStackConfigurationException When the need route is missing from the request
99
     */
100
    private function route(ServerRequestInterface $request)
101
    {
102
        $route = $request->getAttribute('route', false);
103
        if (! $route instanceof Route) {
104
            throw new BadHttpStackConfigurationException(
105
                "Dispatcher works with router middleware in order to process incoming requests. " .
106
                "Please check that the HTTP stack has the router middleware placed before the dispatcher. ".
107
                "Missing route in the request."
108
            );
109
        }
110
        return $route;
111
    }
112
113
    /**
114
     * Merges any existing view data in the request with the provided one
115
     *
116
     * @param ServerRequestInterface $request
117
     * @param array                  $data
118
     *
119
     * @return array
120
     */
121
    private function merge(ServerRequestInterface $request, array $data)
122
    {
123
        $existing = $request->getAttribute('viewData', []);
124
        return array_merge($existing, $data);
125
    }
126
127
    /**
128
     * Verify and return the response from the context
129
     *
130
     * @param ControllerContextInterface $context
131
     *
132
     * @return ResponseInterface
133
     *
134
     * @throws MissingResponseException When context has no response object to return
135
     */
136
    private function responseFrom(ControllerContextInterface $context)
137
    {
138
        $response = $context->response();
139
        if (! $response instanceof ResponseInterface) {
140
            throw new MissingResponseException(
141
                "Missing response object after handle the request. If you disabled rendering you need to ".
142
                "provide an HTTP response object to be returned to the client. Use Context::setResponse() ".
143
                "to provide a response to be returned."
144
            );
145
        }
146
147
        return $response;
148
    }
149
}
150