Test Failed
Pull Request — master (#16)
by Divine Niiquaye
14:00
created

GroupingTrait::processRouteMaps()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
c 4
b 0
f 0
nc 4
nop 3
dl 0
loc 18
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Flight Routing.
7
 *
8
 * PHP version 7.1 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Flight\Routing\Traits;
19
20
use Biurad\Annotations\LoaderInterface;
21
use Flight\Routing\{DebugRoute, Route};
22
use Flight\Routing\Interfaces\RouteCompilerInterface;
23
use Psr\Cache\CacheItemPoolInterface;
24
25
trait GroupingTrait
26
{
27
    /** @var array */
28
    private $routesMap = [];
29
30
    /** @var Route[]|\SplFixedArray<int,Route> */
31
    private $routes = [];
32
33
    /** @var CacheItemPoolInterface|string|bool */
34
    private $cacheData = '';
35
36
    /** @var bool */
37
    private $hasGroups = false;
38
39
    /** @var self|null */
40
    private $parent = null;
41
42
    /** @var array<string,mixed[]>|null */
43
    private $stack = null;
44
45
    /** @var int */
46
    private $countRoutes = 0;
47
48
    /** @var DebugRoute|null */
49
    private $profiler = null;
50
51
    /** @var RouteCompilerInterface */
52
    private $compiler;
53
54
    /**
55
     * If routes was debugged, return the profiler.
56
     */
57
    public function getDebugRoute(): ?DebugRoute
58
    {
59
        return $this->profiler;
60
    }
61
62
    /**
63
     * If routes has been cached or not.
64
     */
65
    public function isCached(): bool
66
    {
67
        return true === $this->cacheData;
68
    }
69
70
    /**
71
     * Load routes from annotation.
72
     */
73
    public function loadAnnotation(LoaderInterface $loader): void
74
    {
75
        $annotations = $loader->load();
76
77
        foreach ($annotations as $annotation) {
78
            if ($annotation instanceof self) {
79
                $this->hasGroups = true;
80
                $this->routes[] = $annotation;
81
            }
82
        }
83
    }
84
85
    /**
86
     * Bind route with collection.
87
     */
88
    private function resolveWith(Route $route): Route
89
    {
90
        if (null !== $stack = $this->stack) {
91
            foreach ($stack as $routeMethod => $arguments) {
92
                if (empty($arguments)) {
93
                    continue;
94
                }
95
96
                \call_user_func_array([$route, $routeMethod], 'prefix' === $routeMethod ? [\implode('', $arguments)] : $arguments);
97
            }
98
        }
99
100
        if (null !== $this->parent) {
101
            $route->end($this);
102
        }
103
104
        ++$this->countRoutes;
105
106
        return $route;
107
    }
108
109
    /**
110
     * @param \ArrayIterator<int,Route> $routes
111
     *
112
     * @return \SplFixedArray<int,Route>
113
     */
114
    private function doMerge(string $prefix, \ArrayIterator $routes, bool $merge = true): \SplFixedArray
115
    {
116
        $unnamedRoutes = [];
117
118
        /** @var Route|RouteCollection $route */
119
        foreach ($this->routes as $namePrefix => $route) {
120
            if ($route instanceof self) {
121
                $route->doMerge($prefix . (\is_string($namePrefix) ? $namePrefix : ''), $routes, false);
0 ignored issues
show
Bug introduced by
The method doMerge() does not exist on Flight\Routing\Route. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
                $route->/** @scrutinizer ignore-call */ 
122
                        doMerge($prefix . (\is_string($namePrefix) ? $namePrefix : ''), $routes, false);
Loading history...
122
123
                continue;
124
            }
125
126
            $routes['map'][] = $route->bind($name = $prefix . $this->generateRouteName($route, $unnamedRoutes));
127
128
            if (isset($routes['profile']) || null !== $this->profiler) {
129
                $routes['profile'][$name] = new DebugRoute($route);
130
            }
131
132
            $this->processRouteMaps($route, $this->compiler, $routes);
133
        }
134
135
        if ($merge) {
136
            $this->countRoutes = $routes['countRoutes'] ?? $this->countRoutes;
137
            $this->routesMap = [$routes['staticRouteMap'] ?? [], 2 => $routes['variableRouteData'] ?? []];
138
139
            if (isset($routes['profile'])) {
140
                $this->profiler->populateProfiler($routes['profile']);
0 ignored issues
show
Bug introduced by
The method populateProfiler() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
                $this->profiler->/** @scrutinizer ignore-call */ 
141
                                 populateProfiler($routes['profile']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
141
            }
142
143
            // Split to prevent a too large regex error ...
144
            if (isset($routes['dynamicRoutesMap'])) {
145
                foreach (\array_chunk($routes['dynamicRoutesMap'], 150, true) as $dynamicRoute) {
146
                    $this->routesMap[1][] = '~^(?|' . \implode('|', $dynamicRoute) . ')$~Ju';
147
                }
148
            }
149
150
            $this->hasGroups = false;
151
        }
152
153
        return \SplFixedArray::fromArray($routes['map']);
154
    }
155
156
    private function processRouteMaps(Route $route, RouteCompilerInterface $compiler, \ArrayIterator $routes): void
157
    {
158
        $methods = \array_unique($route->get('methods'));
159
        $routeId = $routes['countRoutes'] ?? $routes['countRoutes'] = 0;
160
161
        [$pathRegex, $hostsRegex, $variables] = $compiler->compile($route);
162
        $routes['variableRouteData'][] = $variables;
163
164
        if ('\\' === $pathRegex[0]) {
165
            $hostsRegex = empty($hostsRegex) ? '?(?:\\/{2}[^\/]+)?' : '\\/{2}(?i:(?|' . \implode('|', $hostsRegex) . '))';
166
            $regex = \preg_replace('/\?(?|P<\w+>|<\w+>|\'\w+\')/', '', $hostsRegex . $pathRegex);
167
168
            $routes['dynamicRoutesMap'][] = '(?|' . \implode('|', $methods) . '|([A-Z]+))' . $regex . '(*:' . $routeId . ')';
169
        } else {
170
            $routes['staticRouteMap'][$pathRegex] = [$routeId, \array_flip($methods), !empty($hostsRegex) ? '#^(?|' . \implode('|', $hostsRegex) . ')$#i' : null];
171
        }
172
173
        ++$routes['countRoutes'];
174
    }
175
176
    private function generateRouteName(Route $route, array $unnamedRoutes): string
177
    {
178
        if (null === $name = $route->get('name')) {
179
            $name = $route->generateRouteName('');
180
181
            if (isset($unnamedRoutes[$name])) {
182
                $name .= ('_' !== $name[-1] ? '_' : '') . ++$unnamedRoutes[$name];
183
            } else {
184
                $unnamedRoutes[$name] = 0;
185
            }
186
        }
187
188
        return $name;
189
    }
190
191
    /**
192
     * @param CacheItemPoolInterface|string $cache
193
     */
194
    private function getCachedData($cache): ?array
195
    {
196
        if ($cache instanceof CacheItemPoolInterface) {
197
            return $cache->getItem(__FILE__)->get();
198
        }
199
200
        return (\file_exists($cache) && 1 !== $cachedData = include $cache) ? $cachedData : null;
201
    }
202
}
203