Passed
Push — main ( 2e52e3...625462 )
by Dimitri
03:32
created

NativeAdapter   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 531
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 174
c 0
b 0
f 0
dl 0
loc 531
rs 2.8
wmc 70

26 Methods

Rating   Name   Duplication   Size   Complexity  
A addCss() 0 9 3
A endSection() 0 3 1
A scriptsBundle() 0 26 5
A begin() 0 3 1
A extend() 0 3 1
A getData() 0 3 1
A stylesBundle() 0 26 5
A renderString() 0 18 2
A addData() 0 10 2
A stop() 0 16 3
B insert() 0 21 7
A prepareTemplateData() 0 6 2
A include() 0 3 1
A renderView() 0 3 1
A setData() 0 9 2
A end() 0 3 1
A setVar() 0 10 2
A addLibCss() 0 9 3
A addJs() 0 9 3
A __construct() 0 5 1
B render() 0 82 11
A show() 0 26 5
A excerpt() 0 3 2
A start() 0 5 1
A section() 0 3 1
A addLibJs() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like NativeAdapter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use NativeAdapter, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\View\Adapters;
13
14
use RuntimeException;
15
16
/**
17
 * Class View
18
 */
19
class NativeAdapter extends AbstractAdapter
20
{
21
    /**
22
     * Fusionner les données enregistrées et les données utilisateur
23
     */
24
    protected $tempData;
25
26
    /**
27
     * Indique si les données doivent être enregistrées entre les rendus.
28
     *
29
     * @var bool
30
     */
31
    protected $saveData;
32
33
    /**
34
     * Nombre de vues chargées
35
     *
36
     * @var int
37
     */
38
    protected $viewsCount = 0;
39
40
    /**
41
     * Contient les sections et leurs données.
42
     *
43
     * @var array
44
     */
45
    protected $sections = [];
46
47
    /**
48
     * Le nom de la section actuelle en cours de rendu, le cas échéant.
49
     *
50
     * @var string[]
51
     */
52
    protected $sectionStack = [];
53
54
    /**
55
     * Contient les css charges a partie de la section actuelle en cours de rendu
56
     *
57
     * @var array
58
     */
59
    protected $_styles = [];
60
61
    /**
62
     * Contient les librairies css charges a partie de la section actuelle en cours de rendu
63
     *
64
     * @var array
65
     */
66
    protected $_lib_styles = [];
67
68
    /**
69
     * Contient les scripts js charges a partie de la section actuelle en cours de rendu
70
     *
71
     * @var array
72
     */
73
    protected $_scripts = [];
74
75
    /**
76
     * Contient les scripts js des librairies charges a partie de la section actuelle en cours de rendu
77
     *
78
     * @var array
79
     */
80
    protected $_lib_scripts = [];
81
82
    /**
83
     * {@inheritDoc}
84
     */
85
    public function __construct(protected array $config, $viewPathLocator = null, protected bool $debug = BLITZ_DEBUG)
86
    {
87
        parent::__construct($config, $viewPathLocator, $debug);
88
89
        $this->saveData = (bool) ($config['save_data'] ?? true);
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function render(string $view, ?array $options = null, ?bool $saveData = null): string
96
    {
97
        $view = str_replace([$this->viewPath, ' '], '', $view);
98
        if (empty(pathinfo($view, PATHINFO_EXTENSION))) {
99
            $view .= '.' . str_replace('.', '', $this->config['extension'] ?? 'php');
100
        }
101
102
        $this->renderVars['start']   = microtime(true);
103
        $this->renderVars['view']    = $view;
104
        $this->renderVars['options'] = $options ?? [];
105
106
        // Stocke les résultats ici donc même si
107
        // plusieurs vues sont appelées dans une vue, ce ne sera pas le cas
108
        // nettoyez-le sauf si nous le voulons.
109
        $saveData ??= $this->saveData;
110
111
        // A-t-il été mis en cache ?
112
        if (isset($this->renderVars['options']['cache'])) {
113
            $cacheName = $this->renderVars['options']['cache_name'] ?? str_replace('.php', '', $this->renderVars['view']);
114
            $cacheName = str_replace(['\\', '/'], '', $cacheName);
115
116
            $this->renderVars['cacheName'] = $cacheName;
117
118
            if ($output = cache($this->renderVars['cacheName'])) {
119
                $this->logPerformance($this->renderVars['start'], microtime(true), $this->renderVars['view']);
0 ignored issues
show
Bug introduced by
It seems like $this->renderVars['start'] can also be of type string; however, parameter $start of BlitzPHP\View\Adapters\A...apter::logPerformance() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

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

119
                $this->logPerformance(/** @scrutinizer ignore-type */ $this->renderVars['start'], microtime(true), $this->renderVars['view']);
Loading history...
Bug introduced by
It seems like microtime(true) can also be of type string; however, parameter $end of BlitzPHP\View\Adapters\A...apter::logPerformance() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

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

119
                $this->logPerformance($this->renderVars['start'], /** @scrutinizer ignore-type */ microtime(true), $this->renderVars['view']);
Loading history...
120
121
                return $output;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $output returns the type BlitzPHP\Cache\Cache|true which is incompatible with the type-hinted return string.
Loading history...
122
            }
123
        }
124
125
        $this->renderVars['file'] = $this->getRenderedFile($options, $this->renderVars['view'], 'php');
126
127
        // Rendre nos données de vue disponibles pour la vue.
128
        $this->prepareTemplateData($saveData);
129
130
        // Enregistrer les variables actuelles
131
        $renderVars = $this->renderVars;
132
133
        $output = (function (): string {
134
            extract($this->tempData);
135
            ob_start();
136
            include $this->renderVars['file'];
137
138
            return ob_get_clean() ?: '';
139
        })();
140
141
        // Récupère les variables actuelles
142
        $this->renderVars = $renderVars;
143
144
        // Lors de l'utilisation de mises en page, les données ont déjà été stockées
145
        // dans $this->sections, et aucune autre sortie valide
146
        // est autorisé dans $output donc nous allons l'écraser.
147
        if ($this->layout !== null && $this->sectionStack === []) {
148
            $layoutView   = $this->layout;
149
            $this->layout = null;
150
            // Enregistrer les variables actuelles
151
            $renderVars = $this->renderVars;
152
            $output     = $this->render($layoutView, array_merge($options ?? [], ['viewPath' => LAYOUT_PATH]), $saveData);
153
            // Récupère les variables actuelles
154
            $this->renderVars = $renderVars;
155
        }
156
157
        $this->logPerformance($this->renderVars['start'], microtime(true), $this->renderVars['view']);
158
159
        if (($this->debug && (! isset($options['debug']) || $options['debug'] === true))) {
160
            // Nettoyer nos noms de chemins pour les rendre un peu plus propres
161
            $this->renderVars['file'] = clean_path($this->renderVars['file']);
162
            $this->renderVars['file'] = ++$this->viewsCount . ' ' . $this->renderVars['file'];
163
164
            $output = '<!-- DEBUG-VIEW START ' . $this->renderVars['file'] . ' -->' . PHP_EOL
165
                . $output . PHP_EOL
166
                . '<!-- DEBUG-VIEW ENDED ' . $this->renderVars['file'] . ' -->' . PHP_EOL;
167
        }
168
169
        // Faut-il mettre en cache ?
170
        if (isset($this->renderVars['options']['cache'])) {
171
            cache()->write($this->renderVars['cacheName'], $output, (int) $this->renderVars['options']['cache']);
172
        }
173
174
        $this->tempData = null;
175
176
        return $output;
177
    }
178
179
    /**
180
     * {@inheritDoc}
181
     */
182
    public function renderString(string $view, ?array $options = null, ?bool $saveData = null): string
183
    {
184
        $start = microtime(true);
185
        $saveData ??= $this->saveData;
186
        $this->prepareTemplateData($saveData);
187
188
        $output = (function (string $view): string {
189
            extract($this->tempData);
190
            ob_start();
191
            eval('?>' . $view);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
192
193
            return ob_get_clean() ?: '';
194
        })($view);
195
196
        $this->logPerformance($start, microtime(true), $this->excerpt($view));
0 ignored issues
show
Bug introduced by
It seems like microtime(true) can also be of type string; however, parameter $end of BlitzPHP\View\Adapters\A...apter::logPerformance() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

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

196
        $this->logPerformance($start, /** @scrutinizer ignore-type */ microtime(true), $this->excerpt($view));
Loading history...
Bug introduced by
It seems like $start can also be of type string; however, parameter $start of BlitzPHP\View\Adapters\A...apter::logPerformance() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

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

196
        $this->logPerformance(/** @scrutinizer ignore-type */ $start, microtime(true), $this->excerpt($view));
Loading history...
197
        $this->tempData = null;
198
199
        return $output;
200
    }
201
202
    /**
203
     * Extraire le premier bit d'une longue chaîne et ajouter des points de suspension
204
     */
205
    public function excerpt(string $string, int $length = 20): string
206
    {
207
        return (strlen($string) > $length) ? substr($string, 0, $length - 3) . '...' : $string;
208
    }
209
210
    /**
211
     * {@inheritDoc}
212
     */
213
    public function setData(array $data = [], ?string $context = null): self
214
    {
215
        if ($context) {
216
            $data = esc($data, $context);
217
        }
218
219
        $this->tempData = $data;
220
221
        return $this;
222
    }
223
224
    /**
225
     * {@inheritDoc}
226
     */
227
    public function addData(array $data = [], ?string $context = null): self
228
    {
229
        if ($context) {
230
            $data = esc($data, $context);
231
        }
232
233
        $this->tempData ??= $this->data;
234
        $this->tempData = array_merge($this->tempData, $data);
235
236
        return $this;
237
    }
238
239
    /**
240
     * {@inheritDoc}
241
     */
242
    public function setVar(string $name, $value = null, ?string $context = null): self
243
    {
244
        if ($context) {
245
            $value = esc($value, $context);
246
        }
247
248
        $this->tempData ??= $this->data;
249
        $this->tempData[$name] = $value;
250
251
        return $this;
252
    }
253
254
    /**
255
     * {@inheritDoc}
256
     */
257
    public function getData(): array
258
    {
259
        return $this->tempData ?? $this->data;
260
    }
261
262
    /**
263
     * Spécifie que la vue actuelle doit étendre une mise en page existante.
264
     */
265
    public function extend(string $layout)
266
    {
267
        $this->layout = $layout;
268
    }
269
270
    /**
271
     * Commence contient le contenu d'une section dans la mise en page.
272
     */
273
    public function start(string $name)
274
    {
275
        $this->sectionStack[] = $name;
276
277
        ob_start();
278
    }
279
280
    /**
281
     * Commence contient le contenu d'une section dans la mise en page.
282
     *
283
     * @alias self::start()
284
     */
285
    public function section(string $name): void
286
    {
287
        $this->start($name);
288
    }
289
290
    /**
291
     * Commence contient le contenu d'une section dans la mise en page.
292
     *
293
     * @alias self::start()
294
     */
295
    public function begin(string $name): void
296
    {
297
        $this->start($name);
298
    }
299
300
    /**
301
     * Capture la dernière section
302
     *
303
     * @throws RuntimeException
304
     */
305
    public function stop()
306
    {
307
        $contents = ob_get_clean();
308
309
        if ($this->sectionStack === []) {
310
            throw new RuntimeException('View themes, no current section.');
311
        }
312
313
        $section = array_pop($this->sectionStack);
314
315
        // Assurez-vous qu'un tableau existe afin que nous puissions stocker plusieurs entrées pour cela.
316
        if (! array_key_exists($section, $this->sections)) {
317
            $this->sections[$section] = [];
318
        }
319
320
        $this->sections[$section][] = $contents;
321
    }
322
323
    /**
324
     * Capture la dernière section
325
     *
326
     * @throws RuntimeException
327
     *
328
     * @alias self::stop()
329
     */
330
    public function endSection(): void
331
    {
332
        $this->stop();
333
    }
334
335
    /**
336
     * Capture la dernière section
337
     *
338
     * @throws RuntimeException
339
     *
340
     * @alias self::stop()
341
     */
342
    public function end(): void
343
    {
344
        $this->stop();
345
    }
346
347
    /**
348
     * Restitue le contenu d'une section.
349
     */
350
    public function show(string $sectionName)
351
    {
352
        if (! isset($this->sections[$sectionName])) {
353
            echo '';
354
355
            return;
356
        }
357
358
        $start = $end = '';
359
        if ($sectionName === 'css') {
360
            $start = "<style type=\"text/css\">\n";
361
            $end   = "</style>\n";
362
        }
363
        if ($sectionName === 'js') {
364
            $start = "<script type=\"text/javascript\">\n";
365
            $end   = "</script>\n";
366
        }
367
368
        echo $start;
369
370
        foreach ($this->sections[$sectionName] as $key => $contents) {
371
            echo $contents;
372
            unset($this->sections[$sectionName][$key]);
373
        }
374
375
        echo $end;
376
    }
377
378
    /**
379
     * Affichage rapide du contenu principal
380
     */
381
    public function renderView(): void
382
    {
383
        $this->show('content');
384
    }
385
386
    /**
387
     * Utilisé dans les vues de mise en page pour inclure des vues supplémentaires.
388
     *
389
     * @param mixed $saveData
390
     */
391
    public function insert(string $view, ?array $data = [], ?array $options = null, $saveData = true): string
392
    {
393
        $view = preg_replace('#\.php$#i', '', $view) . '.php';
394
        $view = str_replace(' ', '', $view);
395
396
        if ($view[0] !== '/') {
397
            $current_dir = pathinfo($this->renderVars['file'] ?? '', PATHINFO_DIRNAME);
398
            if (file_exists(rtrim($current_dir, DS) . DS . $view)) {
0 ignored issues
show
Bug introduced by
It seems like $current_dir can also be of type array; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

398
            if (file_exists(rtrim(/** @scrutinizer ignore-type */ $current_dir, DS) . DS . $view)) {
Loading history...
399
                $view = rtrim($current_dir, DS) . DS . $view;
400
            } elseif (file_exists($this->viewPath . 'partials' . DS . $view)) {
401
                $view = $this->viewPath . 'partials' . DS . $view;
402
            } elseif (file_exists($this->viewPath . trim(dirname($current_dir), '/\\') . DS . $view)) {
0 ignored issues
show
Bug introduced by
It seems like $current_dir can also be of type array; however, parameter $path of dirname() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

402
            } elseif (file_exists($this->viewPath . trim(dirname(/** @scrutinizer ignore-type */ $current_dir), '/\\') . DS . $view)) {
Loading history...
403
                $view = $this->viewPath . trim(dirname($current_dir), '/\\') . DS . $view;
404
            } elseif (file_exists(VIEW_PATH . 'partials' . DS . $view)) {
405
                $view = VIEW_PATH . 'partials' . DS . $view;
406
            } elseif (file_exists(VIEW_PATH . trim(dirname($current_dir), '/\\') . DS . $view)) {
407
                $view = VIEW_PATH . trim(dirname($current_dir), '/\\') . DS . $view;
408
            }
409
        }
410
411
        return $this->addData($data)->render($view, $options, $saveData);
412
    }
413
414
    /**
415
     * Utilisé dans les vues de mise en page pour inclure des vues supplémentaires.
416
     *
417
     * @alias self::insert()
418
     *
419
     * @param mixed $saveData
420
     */
421
    public function include(string $view, ?array $data = [], ?array $options = null, $saveData = true): string
422
    {
423
        return $this->insert($view, $data, $options, $saveData);
424
    }
425
426
    /**
427
     * Ajoute un fichier css de librairie a la vue
428
     */
429
    public function addLibCss(string ...$src): self
430
    {
431
        foreach ($src as $var) {
432
            if (! in_array($var, $this->_lib_styles, true)) {
433
                $this->_lib_styles[] = $var;
434
            }
435
        }
436
437
        return $this;
438
    }
439
440
    /**
441
     * Ajoute un fichier css a la vue
442
     */
443
    public function addCss(string ...$src): self
444
    {
445
        foreach ($src as $var) {
446
            if (! in_array($var, $this->_styles, true)) {
447
                $this->_styles[] = $var;
448
            }
449
        }
450
451
        return $this;
452
    }
453
454
    /**
455
     * Compile les fichiers de style de l'instance et genere les link:href vers ceux-ci
456
     */
457
    public function stylesBundle(string ...$groups): void
458
    {
459
        $groups     = (array) (empty($groups) ? $this->layout ?? 'default' : $groups);
460
        $lib_styles = $styles = [];
461
462
        foreach ($groups as $group) {
463
            $lib_styles = array_merge(
464
                $lib_styles,
465
                // (array) config('layout.'.$group.'.lib_styles'),
466
                $this->_lib_styles ?? []
467
            );
468
            $styles = array_merge(
469
                $styles,
470
                // (array) config('layout.'.$group.'.styles'),
471
                $this->_styles ?? []
472
            );
473
        }
474
475
        if (! empty($lib_styles)) {
476
            lib_styles(array_unique($lib_styles));
477
        }
478
        if (! empty($styles)) {
479
            styles(array_unique($styles));
480
        }
481
482
        $this->show('css');
483
    }
484
485
    /**
486
     * Ajoute un fichier js de librairie a la vue
487
     */
488
    public function addLibJs(string ...$src): self
489
    {
490
        foreach ($src as $var) {
491
            if (! in_array($var, $this->_lib_scripts, true)) {
492
                $this->_lib_scripts[] = $var;
493
            }
494
        }
495
496
        return $this;
497
    }
498
499
    /**
500
     * Ajoute un fichier js a la vue
501
     */
502
    public function addJs(string ...$src): self
503
    {
504
        foreach ($src as $var) {
505
            if (! in_array($var, $this->_scripts, true)) {
506
                $this->_scripts[] = $var;
507
            }
508
        }
509
510
        return $this;
511
    }
512
513
    /**
514
     * Compile les fichiers de script de l'instance et genere les link:href vers ceux-ci
515
     */
516
    public function scriptsBundle(string ...$groups): void
517
    {
518
        $groups      = (array) (empty($groups) ? $this->layout ?? 'default' : $groups);
519
        $lib_scripts = $scripts = [];
520
521
        foreach ($groups as $group) {
522
            $lib_scripts = array_merge(
523
                $lib_scripts,
524
                // (array) config('layout.'.$group.'.lib_scripts'),
525
                $this->_lib_scripts ?? []
526
            );
527
            $scripts = array_merge(
528
                $scripts,
529
                // (array) config('layout.'.$group.'.scripts'),
530
                $this->_scripts ?? []
531
            );
532
        }
533
534
        if (! empty($lib_scripts)) {
535
            lib_scripts(array_unique($lib_scripts));
536
        }
537
        if (! empty($scripts)) {
538
            scripts(array_unique($scripts));
539
        }
540
541
        $this->show('js');
542
    }
543
544
    protected function prepareTemplateData(bool $saveData): void
545
    {
546
        $this->tempData ??= $this->data;
547
548
        if ($saveData) {
549
            $this->data = $this->tempData;
550
        }
551
    }
552
}
553