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) { |
|
|
|
|
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); |
|
|
|
|
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @return string |
132
|
|
|
*/ |
133
|
|
|
protected function generateCompiledRoutes(): string |
134
|
|
|
{ |
135
|
|
|
$collection = new RouteList(); |
136
|
|
|
$collection->addForeach(...$this->getRoutes()); |
|
|
|
|
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
|
|
|
|
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.