GenerateInvokeMiddleware::__invoke()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 54
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 11
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 54
ccs 9
cts 9
cp 1
crap 2
rs 9.9

How to fix   Long Method   

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
3
declare(strict_types=1);
4
5
namespace Jasny\SwitchRoute\Generator;
6
7
use Jasny\SwitchRoute\InvalidRouteException;
8
use Jasny\SwitchRoute\Invoker;
9
use Jasny\SwitchRoute\InvokerInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use ReflectionException;
12
13
/**
14
 * Generate a script that invokes .
15
 */
16
class GenerateInvokeMiddleware extends AbstractGenerate
17
{
18
    protected InvokerInterface $invoker;
19
20
    /**
21
     * GenerateScript constructor.
22
     */
23 5
    public function __construct(InvokerInterface $invoker = null)
24
    {
25 5
        $this->invoker = $invoker ?? new Invoker();
26
    }
27
28
    /**
29
     * Invoke code generation.
30
     *
31
     * @param string $name      Class name
32
     * @param array  $routes    Ignored
33
     * @param array  $structure
34
     * @return string
35
     */
36 4
    public function __invoke(string $name, array $routes, array $structure): string
37
    {
38 4
        $invokeCode = self::indent($this->generateSwitchFromRoutes($routes), 8)
39 4
            . (!isset($structure["\e"]) ? "\n\n" . '$this->notFound($request);' : '');
40
41 2
        [$namespace, $class] = $this->generateNs($name);
42
43 2
        return <<<CODE
44 2
<?php
45
46
declare(strict_types=1);
47 2
{$namespace}
48
use Jasny\SwitchRoute\NotFoundException;
49
use Psr\Http\Message\ResponseInterface;
50
use Psr\Http\Message\ServerRequestInterface;
51
use Psr\Http\Server\MiddlewareInterface;
52
use Psr\Http\Server\RequestHandlerInterface;
53
use Psr\Http\Message\ResponseFactoryInterface;
54
55
/**
56
 * PSR-15 compatible middleware that invokes an action based on the route attributes of the server request.
57
 *
58
 * This file is generated by SwitchRoute.
59
 * Do not modify it manually. Any changes will be overwritten.
60
 */
61 2
class {$class} implements MiddlewareInterface
62
{
63
    /**
64
     * @var callable|null
65
     */
66
    protected \$instantiate;
67
68
    /**
69
     * @param callable \$instantiate  Instantiate controller / action classes.
70
     */
71
    public function __construct(?callable \$instantiate = null)
72
    {
73
        \$this->instantiate = \$instantiate;
74
    }
75
76
    /**
77
     * Process an incoming server request.
78
     */
79
    public function process(ServerRequestInterface \$request, RequestHandlerInterface \$handler): ResponseInterface
80
    {
81
        \$include = \$request->getAttribute('route:include', null);
82
        if (\$include !== null) {
83
            return require \$include;
84
        }
85
86
        \$controller = \$request->getAttribute('route:controller', '');
87
        \$action = \$request->getAttribute('route:action', '');
88
89 2
{$invokeCode}
90
91
        throw new NotFoundException("No default route specified");
92
    }
93
}
94 2
CODE;
95
    }
96
97
    /**
98
     * Generate the PHP script with a switch for routing.
99
     */
100 5
    protected function generateSwitchFromRoutes(array $routes): string
101
    {
102 5
        $grouped = $this->groupRoutes($routes);
103 4
        $grouped[''][''] = null;
104
105 4
        $code = [];
106 4
        $code[] = "switch (\$controller) {";
107
108 4
        foreach ($grouped as $controller => $controllerRoutes) {
109 4
            $code[] = "    case '" . addslashes($controller) . "':";
110 4
            $code[] = "        switch (\$action) {";
111
112 4
            foreach ($controllerRoutes as $action => $key) {
113 4
                $route = [
114 4
                    'controller' => $controller !== '' ? $controller : null,
115 4
                    'action' => $action !== '' ? $action : null,
116 4
                ];
117
118 4
                if ($key !== null) {
119 3
                    $code[] = self::indent("case '" . addslashes($action) . "':", 12);
120 3
                    $code[] = self::indent($this->generateRoute($key, $route, []), 16);
121
                }
122
            }
123 3
            $code[] = "        }";
124 3
            $code[] = "        break;";
125
        }
126
127 3
        $code[] = "}";
128
129 3
        return join("\n", $code);
130
    }
131
132
    /**
133
     * Group routes by controller name.
134
     */
135 5
    protected function groupRoutes(array $routes): array
136
    {
137 5
        $grouped = [];
138
139 5
        foreach ($routes as $key => $route) {
140 4
            if (isset($route['include'])) {
141 2
                continue;
142
            }
143
144 4
            if (!isset($route['controller']) && !isset($route['action'])) {
145 1
                $key = preg_replace('/{.*?}/', '*', $key);
146 1
                throw new InvalidRouteException("Route for '$key' should specify 'include', 'controller', " .
147 1
                    "or 'action'");
148
            }
149
150 3
            $controller = $route['controller'] ?? '';
151 3
            $action = $route['action'] ?? '';
152
153 3
            if (!isset($grouped[$controller][$action])) {
154 3
                $grouped[$controller][$action] = $key;
155
            }
156
        }
157
158 4
        return $grouped;
159
    }
160
161
    /**
162
     * Generate routing code for an endpoint.
163
     *
164
     * @throws InvalidRouteException
165
     */
166 4
    protected function generateRoute(string $key, array $route, array $vars): string
167
    {
168
        try {
169 4
            $invocation = $this->invoker->generateInvocation(
170 4
                $route,
171 4
                function ($name, $type, $default) {
172 2
                    if ($type == ServerRequestInterface::class || is_a($type, ServerRequestInterface::class, true)) {
173 2
                        return '$request';
174
                    }
175
176 2
                    return "\$request->getAttribute('route:{" . addslashes($name) . "}', "
177 2
                        . var_export($default, true) . ")";
178 4
                },
179 4
                '(isset($this->instantiate) ? ($this->instantiate)(\'%1$s\') : new \\%1$s)'
180 4
            );
181 2
        } catch (ReflectionException $exception) {
182 2
            $key = preg_replace('/{.*?}/', '*', $key);
183 2
            throw new InvalidRouteException("Invalid route for '$key'. " . $exception->getMessage(), 0, $exception);
184
        }
185
186 2
        return "return $invocation;";
187
    }
188
}
189