getResponderForFailedRoute()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
crap 3
1
<?php
2
/**
3
 * Routeless Fail Responder
4
 *
5
 * PHP version 5
6
 *
7
 * Copyright (C) 2016 Jake Johns
8
 *
9
 * This software may be modified and distributed under the terms
10
 * of the MIT license.  See the LICENSE file for details.
11
 *
12
 * @category  Responder
13
 * @package   Jnjxp\Routeless
14
 * @author    Jake Johns <[email protected]>
15
 * @copyright 2016 Jake Johns
16
 * @license   http://jnj.mit-license.org/2016 MIT License
17
 * @link      http://github.com/jnjxp/jnjxp.routeless
18
 */
19
20
namespace Jnjxp\Routeless;
21
22
use Aura\Router\Route;
23
24
use Aura\Router\Rule\Allows;
25
use Aura\Router\Rule\Accepts;
26
use Aura\Router\Rule\Host;
27
use Aura\Router\Rule\Path;
28
29
use Psr\Http\Message\ServerRequestInterface as Request;
30
use Psr\Http\Message\ResponseInterface as Response;
31
32
/**
33
 * Routing failed responder
34
 *
35
 * @category Responder
36
 * @package  Jnjxp\Routeless
37
 * @author   Jake Johns <[email protected]>
38
 * @license  http://jnj.mit-license.org/ MIT License
39
 * @link     http://github.com/jnjxp/jnjxp.routeless
40
 */
41
class RoutingFailedResponder
42
{
43
    /**
44
     * Factories indexed by rule name
45
     *
46
     * @var array
47
     *
48
     * @access protected
49
     */
50
    protected $factories = [];
51
52
    /**
53
     * Default methods to respond with if no factory present
54
     *
55
     * @var array
56
     *
57
     * @access protected
58
     */
59
    protected $defaults = [
60
        Allows::class  => 'methodNotAllowed',
61
        Accepts::class => 'notAcceptable',
62
        Host::class    => 'notFound',
63
        Path::class    => 'notFound',
64
    ];
65
66
    /**
67
     * Create a routing failed responder
68
     *
69
     * @param array $factories array of rule name to responder factories
70
     *
71
     * @access public
72
     */
73 9
    public function __construct(array $factories = null)
74
    {
75 9
        if ($factories) {
76 9
            $this->factories = $factories;
77 9
        }
78 9
    }
79
80
    /**
81
     * Respond to route based on failed rule
82
     *
83
     * @param Request  $request  PSR7 Request
84
     * @param Response $response PSR7 Response
85
     * @param Route    $route    Failed rule
86
     *
87
     * @return Respone
88
     *
89
     * @access public
90
     */
91 7
    public function __invoke(
92
        Request $request,
93
        Response $response,
94
        Route $route
95
    ) {
96 7
        $responder = $this->getResponderForFailedRoute($route);
97 7
        return $responder($request, $response, $route);
98
    }
99
100
    /**
101
     * Does a responder exist for name?
102
     *
103
     * @param string $name Name of rule for which to check
104
     *
105
     * @return bool
106
     *
107
     * @access public
108
     */
109 9
    public function has($name)
110
    {
111 9
        return isset($this->factories[$name]);
112
    }
113
114
    /**
115
     * Set a factory for a name
116
     *
117
     * @param string   $name    Name of rule for responder
118
     * @param callable $factory Callable factory for responder
119
     *
120
     * @return $this
121
     *
122
     * @access public
123
     */
124 8
    public function set($name, callable $factory)
125
    {
126 8
        $this->factories[$name] = $factory;
127 8
        return $this;
128
    }
129
130
    /**
131
     * Get
132
     *
133
     * @param string $name Name of rule for which to get responder
134
     *
135
     * @return callable
136
     * @throws Exception if no factory available for rule
137
     *
138
     * @access public
139
     */
140 3
    public function get($name)
141
    {
142 3
        if (! $this->has($name)) {
143 1
            $message = sprintf('No responder for failed rule "%s"', $name);
144 1
            throw new Exception($message);
145
        }
146 2
        $factory = $this->factories[$name];
147 2
        return $factory();
148
    }
149
150
    /**
151
     * Get responder for failed route
152
     *
153
     * @param Route $route Failed Route
154
     *
155
     * @return callable
156
     *
157
     * @access protected
158
     */
159 7
    protected function getResponderForFailedRoute(Route $route)
160
    {
161 7
        $rule = $route->failedRule;
162 7
        if ($this->has($rule)) {
163 2
            return $this->get($rule);
164
        }
165
166 5
        if (isset($this->defaults[$rule])) {
167 4
            return [$this, $this->defaults[$rule]];
168
        }
169
170 1
        return [$this, 'other'];
171
    }
172
173
    /**
174
     * Method not allowed
175
     *
176
     * Builds the Response when the failed route method was not allowed.
177
     *
178
     * @param Request  $request  PSR7 Request
179
     * @param Response $response PSR7 Response
180
     * @param Route    $route    Failed Route
181
     *
182
     * @return Response
183
     *
184
     * @access protected
185
     */
186 1
    protected function methodNotAllowed(
187
        Request $request,
188
        Response $response,
189
        Route $route
190
    ) {
191
        $request;
192 1
        $response = $response->withStatus(405)
193 1
            ->withHeader('Allow', implode(', ', $route->allows))
194 1
            ->withHeader('Content-Type', 'application/json');
195 1
        $response->getBody()->write(json_encode($route->allows));
196 1
        return $response;
197
    }
198
199
    /**
200
     * Not Acceptable
201
     *
202
     * Builds the Response when the failed route could not accept the media type.
203
     *
204
     * @param Request  $request  PSR7 Request
205
     * @param Response $response PSR7 Response
206
     * @param Route    $route    Failed Route
207
     *
208
     * @return Response
209
     *
210
     * @access protected
211
     */
212 1
    protected function notAcceptable(
213
        Request $request,
214
        Response $response,
215
        Route $route
216
    ) {
217
        $request;
218
        $response = $response
219 1
            ->withStatus(406)
220 1
            ->withHeader('Content-Type', 'application/json');
221 1
        $response->getBody()->write(json_encode($route->accepts));
222 1
        return $response;
223
    }
224
225
    /**
226
     * Resource not found
227
     *
228
     * Builds the Response when the failed route host or path was not found.
229
     *
230
     * @param Request  $request  PSR7 Request
231
     * @param Response $response PSR7 Response
232
     * @param Route    $route    Failed Route
233
     *
234
     * @return Response
235
     *
236
     * @access protected
237
     */
238 2
    protected function notFound(Request $request, Response $response, Route $route)
239
    {
240
        $request; $route;
241 2
        $response = $response->withStatus(404);
242 2
        $response->getBody()->write('404 Not Found');
243 2
        return $response;
244
    }
245
246
    /**
247
     * Other rule failed
248
     *
249
     * Builds the Response when routing failed for some other reason.
250
     *
251
     * @param Request  $request  PSR7 Request
252
     * @param Response $response PSR7 Response
253
     * @param Route    $route    Failed Route
254
     *
255
     * @return Response
256
     *
257
     * @access protected
258
     */
259 1
    protected function other(Request $request, Response $response, Route $route)
260
    {
261
        $request;
262 1
        $response = $response->withStatus(500)
263 1
            ->withHeader('Content-Type', 'text/plain');
264 1
        $message = sprintf(
265 1
            'Route "%s" failed for rule "%s"',
266 1
            $route->name,
267 1
            $route->failedRule
268 1
        );
269 1
        $response->getBody()->write($message);
270 1
        return $response;
271
    }
272
}
273