Test Failed
Push — master ( d750d9...412c5f )
by Divine Niiquaye
03:10
created

DumperTrait::exportRoute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 15
nc 2
nop 1
dl 0
loc 22
rs 9.7666
c 1
b 0
f 0
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 Closure;
21
use Flight\Routing\Interfaces\RouteInterface;
22
use Flight\Routing\Matchers\SimpleRouteMatcher;
23
use Flight\Routing\Route;
24
use Flight\Routing\RouteList;
25
26
/**
27
 * @codeCoverageIgnore
28
 */
29
trait DumperTrait
30
{
31
    /** @var array<string,RouteInterface> */
32
    private $cachedRoutes = [];
33
34
    /** @var mixed */
35
    private $compiledRoutes;
36
37
    /**
38
     * Get the compiled routes after warmpRoutes
39
     *
40
     * @return mixed
41
     */
42
    public function getCompiledRoutes()
43
    {
44
        return $this->compiledRoutes;
45
    }
46
47
    /**
48
     * Warm up routes to speed up routes handling.
49
     *
50
     * @param string $cacheFile
51
     * @param bool   $dump
52
     */
53
    public function warmRoutes(string $cacheFile, bool $dump = true): void
54
    {
55
        $cachedRoutes = \is_file($cacheFile) ? require $cacheFile : [[], []];
56
57
        if (!$dump || !empty(\current($cachedRoutes))) {
58
            list($this->compiledRoutes, $this->cachedRoutes) = $cachedRoutes;
59
60
            return;
61
        }
62
63
        $generatedCode = <<<EOF
64
<?php
65
66
/**
67
 * This file has been auto-generated
68
 * by the Flight Routing.
69
 */
70
71
return [
72
{$this->generateCompiledRoutes()}];
73
74
EOF;
75
76
        \file_put_contents($cacheFile, $generatedCode);
77
    }
78
79
    /**
80
     * @internal
81
     *
82
     * @param mixed $value
83
     */
84
    protected static function export($value): string
85
    {
86
        if (null === $value) {
87
            return 'null';
88
        }
89
90
        if (!\is_array($value)) {
91
            if ($value instanceof RouteInterface) {
92
                return self::exportRoute($value);
93
            }
94
95
            return \str_replace("\n", '\'."\n".\'', \var_export($value, true));
96
        }
97
98
        if (!$value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
99
            return '[]';
100
        }
101
102
        $i      = 0;
103
        $export = '[';
104
105
        foreach ($value as $k => $v) {
106
            if ($i === $k) {
107
                ++$i;
108
            } else {
109
                $export .= self::export($k) . ' => ';
110
111
                if (\is_int($k) && $i < $k) {
112
                    $i = 1 + $k;
113
                }
114
            }
115
116
            if (\is_string($v) && 0 === \strpos($v, 'unserialize')) {
117
                $v = '\\' . $v . ', ';
118
            } elseif ($v instanceof RouteInterface) {
119
                $v .= self::exportRoute($v);
120
            } else {
121
                $v = self::export($v) . ', ';
122
            }
123
124
            $export .= $v;
125
        }
126
127
        return \substr_replace($export, ']', -2);
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr_replace($export, ']', -2) could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
128
    }
129
130
    /**
131
     * @return string
132
     */
133
    protected function generateCompiledRoutes(): string
134
    {
135
        $collection = new RouteList();
136
        $collection->addForeach(...$this->getRoutes());
0 ignored issues
show
Bug introduced by
It seems like getRoutes() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

136
        $collection->addForeach(...$this->/** @scrutinizer ignore-call */ getRoutes());
Loading history...
137
138
        $compiledRoutes = $this->matcher->warmCompiler($collection);
139
140
        $code = '[ // $compiledRoutes' . "\n";
141
142
        if ($this->matcher instanceof SimpleRouteMatcher) {
143
            $code .= $this->simpleRouteCompilerCode($compiledRoutes);
144
        } elseif (null !== $compiledRoutes || false !== $compiledRoutes) {
145
            $code .= self::export($compiledRoutes);
146
        }
147
        $code .= "],\n";
148
149
        $code .= '[ // $routes' . "\n";
150
151
        foreach ($collection->getRoutes() as $route) {
152
            if ($route->getController() instanceof Closure) {
153
                continue;
154
            }
155
156
            $code .= \sprintf('    %s => ', self::export($route->getName()));
157
            $code .= self::export($route);
158
            $code .= ", \n";
159
        }
160
        $code .= "],\n";
161
162
        return (string) \preg_replace('/^./m', \str_repeat('    ', 1) . '$0', $code);
163
    }
164
165
    /**
166
     * @param RouteInterface $route
167
     *
168
     * @return string
169
     */
170
    private static function exportRoute(RouteInterface $route): string
171
    {
172
        $controller = $route->getController();
173
174
        if (!\is_string($controller)) {
175
            $controller = \sprintf('unserialize(\'%s\')', \serialize($controller));
176
        }
177
178
        $exported = self::export([
179
            $route->getName(),
180
            $route->getMethods(),
181
            $route->getPath(),
182
            $route->getSchemes(),
183
            $route->getDomain(),
184
            $controller,
185
            $route->getMiddlewares(),
186
            $route->getPatterns(),
187
            $route->getDefaults(),
188
            $route->getArguments(),
189
        ]);
190
191
        return \sprintf('%s::__set_state(%s)', Route::class, $exported);
192
    }
193
194
    /**
195
     * @param mixed[] $compiledRoutes
196
     *
197
     * @return string
198
     */
199
    private function simpleRouteCompilerCode(array $compiledRoutes): string
200
    {
201
        [$staticRoutes, $dynamicRoutes] = $compiledRoutes;
202
203
        $code = '';
204
        $code .= '    [ // $staticRoutes' . "\n";
205
206
        foreach ($staticRoutes as $path => $route) {
207
            $code .= \sprintf('        %s => ', self::export($path));
208
209
            if (\is_array($route)) {
210
                $code .= \sprintf(
211
                    "[\n            %s,\n            %s\n        ]",
212
                    self::export(\current($route)),
213
                    \sprintf('\unserialize(\'%s\')', \serialize(\end($route)))
214
                );
215
            } else {
216
                $code .= self::export($route);
217
            }
218
            $code .= ", \n";
219
        }
220
        $code .= "    ],\n";
221
222
        $code .= '    [ // $dynamicRoutes' . "\n";
223
224
        foreach ($dynamicRoutes as $name => $route) {
225
            $code .= \sprintf('        %s =>', self::export($name));
226
            $code .= \sprintf(' \unserialize(\'%s\'),' . "\n", \serialize($route));
227
        }
228
        $code .= "    ],\n";
229
230
        return $code;
231
    }
232
}
233