Completed
Push — master ( f2f784...429167 )
by Бабичев
01:47
created

Lexeme::loader()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 8
cts 8
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 6
nc 4
nop 1
crap 4
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
Unused Code Comprehensibility introduced by Babichev Maxim
43% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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