1 | <?php |
||
2 | |||
3 | namespace Bavix\Flow; |
||
4 | |||
5 | use Bavix\Helpers\Arr; |
||
6 | use Bavix\Lexer\Lexer; |
||
7 | use Bavix\SDK\FileLoader; |
||
8 | |||
9 | /** |
||
10 | * Class Lexeme |
||
11 | * |
||
12 | * @package Bavix\Flow |
||
13 | */ |
||
14 | class Lexeme |
||
15 | { |
||
16 | |||
17 | /** |
||
18 | * @var array |
||
19 | */ |
||
20 | protected $types; |
||
21 | |||
22 | /** |
||
23 | * @var array |
||
24 | */ |
||
25 | protected $default = [ |
||
26 | 'types' => [ |
||
27 | 'variable' |
||
28 | ] |
||
29 | ]; |
||
30 | |||
31 | /** |
||
32 | * @var array |
||
33 | */ |
||
34 | protected $props = []; |
||
35 | |||
36 | /** |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $data = []; |
||
40 | |||
41 | /** |
||
42 | * @var array |
||
43 | */ |
||
44 | protected $closed = []; |
||
45 | |||
46 | /** |
||
47 | * @var array |
||
48 | */ |
||
49 | protected $items = []; |
||
50 | |||
51 | /** |
||
52 | * @var string[] |
||
53 | */ |
||
54 | protected $folders = []; |
||
55 | |||
56 | /** |
||
57 | * @var Lexer |
||
58 | */ |
||
59 | protected $lexer; |
||
60 | |||
61 | /** |
||
62 | * @var Flow |
||
63 | */ |
||
64 | protected $flow; |
||
65 | |||
66 | /** |
||
67 | * Lexeme constructor. |
||
68 | * |
||
69 | * @param Flow $flow |
||
70 | */ |
||
71 | 11 | public function __construct(Flow $flow) |
|
72 | { |
||
73 | 11 | $this->types = Property::get('types'); |
|
74 | 11 | $this->addFolder(\dirname(__DIR__, 2) . '/lexemes'); |
|
75 | 11 | $this->flow = $flow; |
|
76 | 11 | } |
|
77 | |||
78 | /** |
||
79 | * @param string $path |
||
80 | * |
||
81 | * @return self |
||
82 | */ |
||
83 | 11 | public function addFolder(string $path): self |
|
84 | { |
||
85 | 11 | Arr::unShift($this->folders, $path); |
|
86 | |||
87 | 11 | return $this; |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * @return Lexer |
||
92 | */ |
||
93 | 11 | protected function lexer(): Lexer |
|
94 | { |
||
95 | 11 | if (!$this->lexer) |
|
96 | { |
||
97 | 11 | $this->lexer = new Lexer(); |
|
98 | } |
||
99 | |||
100 | 11 | return $this->lexer; |
|
101 | } |
||
102 | |||
103 | /** |
||
104 | * @param $file |
||
105 | * |
||
106 | * @return FileLoader\DataInterface|null |
||
107 | */ |
||
108 | 11 | protected function loader($file) |
|
109 | { |
||
110 | 11 | foreach ($this->folders as $folder) |
|
111 | { |
||
112 | 11 | foreach (FileLoader::extensions() as $ext) |
|
113 | { |
||
114 | try |
||
115 | { |
||
116 | 11 | return FileLoader::load($folder . '/' . $file . '.' . $ext); |
|
117 | // $data = FileLoader::load($folder . '/' . $file . '.' . $ext); |
||
0 ignored issues
–
show
|
|||
118 | 11 | // |
|
119 | 11 | // if ($ext === 'yml') |
|
120 | // { |
||
121 | // (new FileLoader\PHPLoader($folder . '/' . $file . '.php' )) |
||
122 | // ->save($data->asArray()); |
||
123 | // } |
||
124 | // |
||
125 | 7 | // return $data; |
|
126 | } |
||
127 | catch (\Throwable $throwable) |
||
128 | { |
||
129 | // skip... |
||
130 | } |
||
131 | } |
||
132 | } |
||
133 | |||
134 | 10 | return null; |
|
135 | } |
||
136 | 10 | ||
137 | 10 | /** |
|
138 | * @param array $props |
||
139 | 10 | * @param string $key |
|
140 | * |
||
141 | 10 | * @return array |
|
142 | */ |
||
143 | public function property(array $props, string $key): array |
||
144 | { |
||
145 | $self = $this; |
||
146 | $prop = &$props[$key]; |
||
147 | |||
148 | if (empty($this->props[$key])) |
||
149 | { |
||
150 | if (isset($prop['extends'])) |
||
151 | { |
||
152 | $extends = Arr::map((array)$prop['extends'], function ($extend) use ($self, &$props) { |
||
153 | 10 | return $self->property($props, $extend); |
|
154 | }); |
||
155 | |||
156 | 10 | $prop = \array_merge_recursive( |
|
157 | $prop, |
||
158 | ...$extends |
||
159 | ); |
||
160 | } |
||
161 | |||
162 | $this->props[$key] = $prop; |
||
163 | } |
||
164 | 11 | ||
165 | return $this->props[$key]; |
||
166 | 11 | } |
|
167 | |||
168 | 10 | /** |
|
169 | * @param array $props |
||
170 | * |
||
171 | 11 | * @return array |
|
172 | */ |
||
173 | protected function properties(array $props): array |
||
174 | { |
||
175 | foreach ($props as $key => $prop) |
||
176 | { |
||
177 | $this->property($props, $key); |
||
178 | } |
||
179 | 11 | ||
180 | return $this->props; |
||
181 | 11 | } |
|
182 | 11 | ||
183 | /** |
||
184 | 11 | * @param array $types |
|
185 | * |
||
186 | * @return array |
||
187 | 11 | */ |
|
188 | protected function types(array $types): array |
||
189 | { |
||
190 | $results = []; |
||
191 | foreach ($types as $type) |
||
192 | { |
||
193 | $results[] = $this->types[$type]; |
||
194 | } |
||
195 | 11 | ||
196 | return $results; |
||
197 | 11 | } |
|
198 | |||
199 | 11 | /** |
|
200 | 11 | * @param string $key |
|
201 | 11 | * |
|
202 | * @return string |
||
203 | */ |
||
204 | protected function fragment(string $key): string |
||
205 | { |
||
206 | $property = $this->props[$key] ?? $this->default; |
||
207 | |||
208 | return '(?<' . $key . '>(' . |
||
209 | implode('|', $this->types($property['types'])) . |
||
210 | 11 | '))'; |
|
211 | } |
||
212 | 11 | ||
213 | 11 | /** |
|
214 | * @param string $key |
||
215 | 11 | * @param array $syntax |
|
216 | * |
||
217 | 11 | * @return array |
|
218 | 11 | */ |
|
219 | 11 | public function syntax2Array(string $key, array $syntax): array |
|
220 | { |
||
221 | 11 | $props = $this->props[$key] ?? null; |
|
222 | $closed = $this->closed[$key] ?? false; |
||
223 | |||
224 | 11 | return Cache::get(__CLASS__ . $key, function () use ($syntax, $props, $closed) { |
|
225 | return [ |
||
226 | 11 | 'syntax' => $syntax, |
|
227 | 'props' => $props, |
||
228 | 11 | 'closed' => $closed, |
|
229 | ]; |
||
230 | 11 | }); |
|
231 | } |
||
232 | |||
233 | protected function tryLoad(string $key) |
||
234 | { |
||
235 | if (empty($this->data[$key])) |
||
236 | { |
||
237 | $item = Cache::getItem(__CLASS__ . $key); |
||
238 | |||
239 | if ($item && $item->isHit()) |
||
240 | { |
||
241 | $_cache = $item->get(); |
||
242 | 11 | ||
243 | $this->data[$key] = $_cache['syntax']; |
||
244 | $this->props[$key] = $_cache['props']; |
||
245 | $this->closed[$key] = $_cache['closed']; |
||
246 | |||
247 | return $this->data[$key]; |
||
248 | } |
||
249 | } |
||
250 | |||
251 | 11 | return null; |
|
252 | } |
||
253 | 11 | ||
254 | /** |
||
255 | 11 | * @param string $key |
|
256 | * @param array $data |
||
257 | 11 | * |
|
258 | 11 | * @return array|mixed |
|
259 | */ |
||
260 | protected function getLexemes(string $key, array $data) |
||
261 | 11 | { |
|
262 | $syntax = $this->tryLoad($key); |
||
263 | |||
264 | if (empty($this->data[$key])) |
||
265 | { |
||
266 | $syntax = $this->syntax($key, $data); |
||
267 | $this->syntax2Array($key, $syntax); |
||
268 | } |
||
269 | |||
270 | 11 | return $syntax; |
|
271 | } |
||
272 | 11 | ||
273 | /** |
||
274 | 6 | * @param string $key |
|
275 | * @param array $data |
||
276 | * |
||
277 | 11 | * @return array |
|
278 | 11 | */ |
|
279 | protected function syntax(string $key, array $data) |
||
280 | 11 | { |
|
281 | foreach ($data['directives'] ?? [] as $_key => $directive) |
||
282 | 11 | { |
|
283 | $this->data($_key, $directive ?: []); |
||
284 | 11 | } |
|
285 | 11 | ||
286 | 11 | $this->closed[$key] = $data['closed'] ?? false; |
|
287 | 11 | $this->properties($data['properties'] ?? []); |
|
288 | 11 | ||
289 | 11 | $syntax = []; |
|
290 | |||
291 | foreach ($data['syntax'] ?? [] as $text) |
||
292 | 11 | { |
|
293 | $tokens = $this->lexer()->tokens($text); |
||
294 | 11 | $vars = $tokens[Lexer::PRINTER] ?? []; |
|
295 | 11 | $code = \str_replace( |
|
296 | 11 | ['\\(', '\\)', ','], |
|
297 | 11 | ['\\( ', ' \\)', ' ,'], |
|
298 | $text |
||
299 | ); |
||
300 | |||
301 | 11 | foreach ($vars as $var) |
|
302 | 11 | { |
|
303 | 11 | $code = \str_replace( |
|
304 | $var['code'], |
||
305 | $this->fragment($var['fragment']), |
||
306 | $code |
||
307 | 11 | ); |
|
308 | } |
||
309 | |||
310 | $syntax[] = [ |
||
311 | 'vars' => $vars, |
||
312 | 'regexp' => '~^' . $key . ' ' . $code . '$~ui' |
||
313 | ]; |
||
314 | } |
||
315 | |||
316 | 11 | return $syntax; |
|
317 | } |
||
318 | |||
319 | /** |
||
320 | * @param string $key |
||
321 | 11 | * @param array|null $data |
|
322 | * |
||
323 | 11 | * @return array|bool|mixed |
|
324 | */ |
||
325 | 6 | protected function get(string $key, array $data = null) |
|
326 | { |
||
327 | /** |
||
328 | 11 | * @var $loader mixed |
|
329 | */ |
||
330 | 11 | $loader = $this->loader($key) ?: $data; |
|
331 | |||
332 | 11 | if (null === $loader) |
|
333 | { |
||
334 | 11 | return true; |
|
335 | } |
||
336 | 11 | ||
337 | $mixed = []; |
||
338 | |||
339 | if ($loader) |
||
340 | 11 | { |
|
341 | $mixed = $loader; |
||
342 | |||
343 | if (\is_object($mixed)) |
||
344 | { |
||
345 | $mixed = $loader->asArray(); |
||
346 | } |
||
347 | } |
||
348 | 11 | ||
349 | return $this->getLexemes($key, $mixed); |
||
350 | 11 | } |
|
351 | |||
352 | 11 | /** |
|
353 | * @param string $key |
||
354 | * |
||
355 | * @return bool |
||
356 | */ |
||
357 | public function closed(string $key): bool |
||
358 | { |
||
359 | $this->data($key); |
||
360 | |||
361 | 11 | return $this->closed[$key] ?? false; |
|
362 | } |
||
363 | 11 | ||
364 | /** |
||
365 | 11 | * @param string $key |
|
366 | * @param array $data |
||
367 | 11 | * |
|
368 | * @return array|bool |
||
369 | */ |
||
370 | 11 | public function data(string $key, array $data = null) |
|
371 | { |
||
372 | $this->tryLoad($key); |
||
373 | |||
374 | if (!\array_key_exists($key, $this->data)) |
||
375 | { |
||
376 | $this->data[$key] = $this->get($key, $data); |
||
377 | } |
||
378 | 11 | ||
379 | return $this->data[$key]; |
||
380 | 11 | } |
|
381 | 11 | ||
382 | /** |
||
383 | 11 | * @param string $value |
|
384 | * |
||
385 | * @return array |
||
386 | */ |
||
387 | public function lexerApply(string $value): array |
||
388 | 11 | { |
|
389 | 11 | $name = __FUNCTION__ . $value; |
|
390 | 11 | $item = Cache::getItem($name); |
|
391 | |||
392 | if ($item && $item->isHit()) |
||
393 | 11 | { |
|
394 | 11 | return $item->get(); |
|
395 | } |
||
396 | |||
397 | 11 | $value = '{{ ' . $value . ' }}'; |
|
398 | 11 | $tokens = $this->flow->lexer()->tokens($value); |
|
399 | 11 | $_lexer = \current($tokens[Lexer::PRINTER]); |
|
400 | |||
401 | $store = [ |
||
402 | 'lexer' => $_lexer, |
||
403 | 'code' => $this->flow->build($_lexer) |
||
404 | ]; |
||
405 | |||
406 | return Cache::get($name, function () use ($store) { |
||
407 | return $store; |
||
408 | 11 | }); |
|
409 | } |
||
410 | 11 | ||
411 | 11 | /** |
|
412 | * @param string $key |
||
413 | 11 | * @param string $tpl |
|
414 | * |
||
415 | * @return array|null |
||
416 | */ |
||
417 | public function apply(string $key, string $tpl) |
||
418 | 11 | { |
|
419 | $lexData = $this->data($key); |
||
420 | 11 | $data = null; |
|
421 | |||
422 | 11 | if (true === $lexData) |
|
423 | 11 | { |
|
424 | 11 | return $data; |
|
425 | } |
||
426 | 11 | ||
427 | 11 | foreach ($lexData as $datum) |
|
428 | { |
||
429 | if (\preg_match($datum['regexp'], $tpl, $outs)) |
||
430 | { |
||
431 | 11 | $data = Arr::filter($outs, function (...$args) { |
|
432 | return \is_string(\end($args)); |
||
433 | }); |
||
434 | |||
435 | $data = Arr::map($data, [$this, 'lexerApply']); |
||
436 | break; |
||
437 | } |
||
438 | } |
||
439 | |||
440 | return $data; |
||
441 | } |
||
442 | |||
443 | } |
||
444 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.