Dispatcher::__invoke()   C
last analyzed

Complexity

Conditions 12
Paths 17

Size

Total Lines 39
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 12.0053

Importance

Changes 0
Metric Value
cc 12
eloc 31
nc 17
nop 3
dl 0
loc 39
ccs 29
cts 30
cp 0.9667
crap 12.0053
rs 5.1612
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
/**
4
 * Minotaur
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
 * use this file except in compliance with the License. You may obtain a copy of
8
 * the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
 * License for the specific language governing permissions and limitations under
16
 * the License.
17
 *
18
 * @copyright 2015-2017 Appertly
19
 * @license   Apache-2.0
20
 */
21
namespace Minotaur\Route;
22
23
use Psr\Http\Message\ServerRequestInterface as Request;
24
use Psr\Http\Message\ResponseInterface as Response;
25
use Aura\Router\Matcher;
26
use Caridea\Container\Container;
27
28
/**
29
 * The final, innermost layer of the request–response dispatch queue.
30
 *
31
 * This class supports the following handlers on `Aura\Router\Route` objects:
32
 *
33
 * - Anonymous functions (closures and lambdas)
34
 * - An array containing a class name and a function name; this object will be
35
 *   retrieved from the container.
36
 * - A string; the object with this name in the container will be invoked.
37
 */
38
class Dispatcher implements Plugin
39
{
40
    /**
41
     * @var \Aura\Router\Matcher
42
     */
43
    private $matcher;
44
    /**
45
     * @var \Caridea\Container\Container
46
     */
47
    private $container;
48
    /**
49
     * @var \Psr\Http\Message\ServerRequestInterface the request, or `null`
50
     */
51
    private $lastDispatchedRequest;
52
53
    /**
54
     * Creates a new Dispatcher plugin
55
     *
56
     * @param \Aura\Router\Matcher $matcher The route matcher
57
     * @param \Caridea\Container\Container $container The dependency injection container
58
     */
59 9
    public function __construct(Matcher $matcher, Container $container)
60
    {
61 9
        $this->matcher = $matcher;
62 9
        $this->container = $container;
63 9
    }
64
65
    /**
66
     * Gets the plugin priority; larger means first.
67
     *
68
     * @return int The plugin priority
69
     */
70
    public function getPriority(): int
71
    {
72
        return PHP_INT_MIN;
73
    }
74
75
    /**
76
     * Gets the last request that was passed to the `__invoke` method.
77
     *
78
     * @return \Psr\Http\Message\ServerRequestInterface The last request or `null`
79
     */
80 1
    public function getLastDispatchedRequest(): ?Request
81
    {
82 1
        return $this->lastDispatchedRequest;
83
    }
84
85
    /**
86
     * Perform the actual routing and dispatch, returning the Response
87
     *
88
     * @param \Psr\Http\Message\ServerRequestInterface $request The server request
89
     * @param \Psr\Http\Message\ResponseInterface $response The response
90
     * @param callable $next - The next layer, (function (Request,Response): Response)
91
     * @return \Psr\Http\Message\ResponseInterface The new response
92
     * @throws Exception\Unroutable if route matching fails
93
     * @throws Exception\Uncallable if a controller method can't be invoked
94
     */
95 9
    public function __invoke(Request $request, Response $response, callable $next): Response
96
    {
97 9
        $route = $this->matcher->match($request);
98 9
        if (!$route) {
99 1
            $failedRoute = $this->matcher->getFailedRoute();
100 1
            throw Exception\Unroutable::fromRoute($failedRoute);
101
        }
102 8
        foreach ($route->attributes as $k => $v) {
103
            $request = $request->withAttribute($k, $v);
104
        }
105 8
        $request = $request->withAttribute('_route', $route);
106 8
        $this->lastDispatchedRequest = $request;
107 8
        $handler = $route->handler;
108 8
        if ($handler instanceof \Closure) {
109 1
            $response = $handler($request, $response);
110 7
        } elseif (is_array($handler) && count($handler) == 2) {
111 3
            list($className, $methodName) = $handler;
112 3
            $controller = $this->container->getFirst($className);
113 3
            if ($controller === null) {
114 1
                throw new Exception\Uncallable("Controller instance not found: '$className'");
115 2
            } elseif (!method_exists($controller, $methodName) && !method_exists($controller, '__call')) {
116 1
                throw new Exception\Uncallable("Controller class '$className' doesn't have method '$methodName'");
117
            }
118 1
            $toInvoke = [$controller, $methodName];
119 1
            $response = $toInvoke($request, $response);
120 4
        } elseif (is_string($handler)) {
121 3
            if (!$this->container->contains($handler)) {
122 1
                throw new Exception\Uncallable("The container has no object with the name '$handler'");
123
            }
124 2
            $toInvoke = $this->container->get($handler);
125 2
            if (!method_exists($toInvoke, '__invoke')) {
126 1
                throw new Exception\Uncallable("The object '$handler' cannot be invoked as a function");
127
            }
128 1
            $response = $toInvoke($request, $response);
129
        } else {
130 1
            throw new Exception\Uncallable("Could not invoke the handler: " . print_r($handler, true));
131
        }
132 3
        return $response; // forget $next, we don't care.
133
    }
134
}
135