Test Failed
Push — master ( 899e31...286460 )
by Agel_Nash
02:52
created

DLTemplate::setTemplateData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 3
c 2
b 0
f 0
dl 0
loc 7
ccs 0
cts 5
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3 1
include_once(MODX_BASE_PATH . 'assets/lib/APIHelpers.class.php');
4
5
/**
6
 * Class DLTemplate
7
 */
8
class DLTemplate
9
{
10
    /**
11
     * Объект DocumentParser - основной класс MODX
12
     * @var DocumentParser $modx
13
     * @access protected
14
     */
15
    protected $modx = null;
16
17
    /**
18
     * @var DLTemplate cached reference to singleton instance
19
     */
20
    protected static $instance;
21
22
    protected $templatePath = 'assets/templates/';
23
24
    protected $templateExtension = 'html';
25
26
    /**
27
     * @var null|Twig_Environment twig object
0 ignored issues
show
Bug introduced by
The type Twig_Environment was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
     */
29
    protected $twig;
30
31
    protected $twigEnabled = false;
32
    protected $bladeEnabled = false;
33
34
    protected $templateData = array();
35
36
    public $phx;
37
38
    /**
39
     * gets the instance via lazy initialization (created on first usage)
40
     *
41
     * @return self
42
     */
43
    public static function getInstance(DocumentParser $modx)
44
    {
45
46
        if (null === self::$instance) {
47
            self::$instance = new self($modx);
48
        }
49
50
        return self::$instance;
51
    }
52
53
    /**
54
     * is not allowed to call from outside: private!
55
     *
56
     */
57
    private function __construct(DocumentParser $modx)
58
    {
59
        $this->modx = $modx;
60
    }
61
62
    /**
63
     * prevent the instance from being cloned
64
     *
65
     * @return void
66
     */
67
    private function __clone()
68
    {
69
    }
70
71
    /**
72
     * prevent from being unserialized
73
     *
74
     * @return void
75
     */
76
    private function __wakeup()
77
    {
78
    }
79
80
    public function getTemplatePath()
81
    {
82
        return $this->templatePath;
83
    }
84
85
    /**
86
     * Задает относительный путь к папке с шаблонами
87
     *
88
     * @param string $path
89
     * @param bool $supRoot
90
     * @return $this
91
     */
92
    public function setTemplatePath($path, $supRoot = false)
93
    {
94
        $path = trim($path);
95
        if ($supRoot === false) {
96
            $path = $this->cleanPath($path);
97
        }
98
99
        if (!empty($path)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
100
            $this->templatePath = $path;
101
        }
102
103
        return $this;
104
    }
105
106
    /**
107
     * @param string $path
108
     * @return string
109
     */
110
    protected function cleanPath($path)
111
    {
112
        return preg_replace(array('/\.*[\/|\\\]/i', '/[\/|\\\]+/i'), array('/', '/'), $path);
113
    }
114
115
    public function getTemplateExtension()
116
    {
117
        return $this->templateExtension;
118
    }
119
120
    /**
121
     * Задает расширение файла с шаблоном
122
     *
123
     * @param $ext
124
     * @return $this
125
     */
126
    public function setTemplateExtension($ext)
127
    {
128
        $ext = $this->cleanPath(trim($ext, ". \t\n\r\0\x0B"));
129
130
        if (!empty($ext)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
131
            $this->templateExtension = $ext;
132
        }
133
134
        return $this;
135
    }
136
137
    /**
138
     * Additional data for external templates
139
     *
140
     * @param array $data
141
     * @return $this
142
     */
143
    public function setTemplateData($data = array())
144
    {
145
        if (is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
146
            $this->templateData = $data;
147
        }
148
149
        return $this;
150
    }
151
152
    /**
153
     * @param array $data
154
     * @return array
155
     */
156
    public function getTemplateData($data = array())
157
    {
158
        $plh = $this->templateData;
159
        $plh['data'] = $data;
160
        $plh['modx'] = $this->modx;
161
        return $plh;
162
    }
163
164
    /**
165
     * Сохранение данных в массив плейсхолдеров
166
     *
167
     * @param mixed $data данные
168
     * @param int $set устанавливать ли глобальнй плейсхолдер MODX
169
     * @param string $key ключ локального плейсхолдера
170
     * @param string $prefix префикс для ключей массива
171
     * @return string
172
     */
173
    public function toPlaceholders($data, $set = 0, $key = 'contentPlaceholder', $prefix = '')
174
    {
175
        $out = '';
176
        if ($set != 0) {
177
            $this->modx->toPlaceholder($key, $data, $prefix);
178
        } else {
179
            $out = $data;
180
        }
181
182
        return $out;
183
    }
184
185
    /**
186
     * refactor $modx->getChunk();
187
     *
188
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
189
     * @return string html template with placeholders without data
190
     */
191
    public function getChunk($name)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (5) exceeds 3; consider refactoring the function
Loading history...
192
    {
193
        $tpl = '';
194
        $ext = null;
195
        $this->twigEnabled = substr($name, 0, 3) == '@T_';
196
        $this->bladeEnabled = substr($name, 0, 3) == '@B_';//(0 === strpos($name, '@B_'));
197
        if ($name != '' && !isset($this->modx->chunkCache[$name])) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
198
            $mode = (preg_match(
199
                '/^((@[A-Z_]+)[:]{0,1})(.*)/Asu',
200
                trim($name),
201
                $tmp
202
            ) && isset($tmp[2], $tmp[3])) ? $tmp[2] : false;
203
            $subTmp = (isset($tmp[3])) ? trim($tmp[3]) : null;
204
            if ($this->twigEnabled) {
205
                $mode = '@' . substr($mode, 3);
0 ignored issues
show
Bug introduced by
It seems like $mode can also be of type false; however, parameter $string of substr() 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

205
                $mode = '@' . substr(/** @scrutinizer ignore-type */ $mode, 3);
Loading history...
206
            } elseif ($this->bladeEnabled) {
207
                $mode = '@' . substr($mode, 3);
208
                $ext = $this->getTemplateExtension();
209
                $this->setTemplateExtension('blade.php');
210
            }
211
            switch ($mode) {
212
                case '@FILE':
213
                    if ($subTmp != '') {
214
                        $real = realpath(MODX_BASE_PATH . $this->templatePath);
215
                        $path = realpath(MODX_BASE_PATH . $this->templatePath . $this->cleanPath($subTmp) . '.' . $this->templateExtension);
216
                        if (basename($path, '.' . $this->templateExtension) !== '' &&
217
                            0 === strpos($path, $real) &&
218
                            file_exists($path)
219
                        ) {
220
                            $tpl = file_get_contents($path);
221
                        }
222
                    }
223
                    break;
224
                case '@INLINE':
225
                case '@TPL':
226
                case '@CODE':
227
                    $tpl = $tmp[3]; //without trim
228
                    break;
229
                case '@DOCUMENT':
230
                case '@DOC':
231
                    switch (true) {
232
                        case ((int)$subTmp > 0):
233
                            $tpl = $this->modx->getPageInfo((int)$subTmp, 0, "content");
234
                            $tpl = isset($tpl['content']) ? $tpl['content'] : '';
235
                            break;
236
                        case ((int)$subTmp == 0):
237
                            $tpl = $this->modx->documentObject['content'];
238
                            break;
239
                    }
240
                    break;
241
                case '@PLH':
242
                case '@PLACEHOLDER':
243
                    if ($subTmp != '') {
244
                        $tpl = $this->modx->getPlaceholder($subTmp);
245
                    }
246
                    break;
247
                case '@CFG':
248
                case '@CONFIG':
249
                case '@OPTIONS':
250
                    if ($subTmp != '') {
251
                        $tpl = $this->modx->getConfig($subTmp);
252
                    }
253
                    break;
254
                case '@SNIPPET':
255
                    if ($subTmp != '') {
256
                        $tpl = $this->modx->runSnippet($subTmp, $this->modx->event->params);
257
                    }
258
                    break;
259
                case '@RENDERPAGE':
260
                    $tpl = $this->renderDoc($subTmp, false);
0 ignored issues
show
Bug introduced by
It seems like $subTmp can also be of type string; however, parameter $id of DLTemplate::renderDoc() does only seem to accept integer, 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

260
                    $tpl = $this->renderDoc(/** @scrutinizer ignore-type */ $subTmp, false);
Loading history...
261
                    break;
262
                case '@LOADPAGE':
263
                    $tpl = $this->renderDoc($subTmp, true);
264
                    break;
265
                case '@TEMPLATE':
266
                    $tpl = $this->getTemplate($subTmp);
0 ignored issues
show
Bug introduced by
It seems like $subTmp can also be of type string; however, parameter $id of DLTemplate::getTemplate() does only seem to accept integer, 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

266
                    $tpl = $this->getTemplate(/** @scrutinizer ignore-type */ $subTmp);
Loading history...
267
                    break;
268
                case '@CHUNK':
269
                    $tpl = $this->getBaseChunk($subTmp);
270
                    break;
271
                default:
272
                    $tpl = $this->getBaseChunk($name);
273
            }
274
            $this->modx->chunkCache[$name] = $tpl;
275
        } else {
276
            $tpl = $this->getBaseChunk($name);
277
        }
278
279
        if($ext !== null) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
280
            $this->setTemplateExtension($ext);
281
        }
282
        return $tpl;
283
    }
284
285
    protected function getBaseChunk($name)
286
    {
287
        if (empty($name)) {
288
            return null;
289
        }
290
291
        if (isset ($this->modx->chunkCache[$name])) {
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
292
            $tpl = $this->modx->chunkCache[$name];
293
        } else {
294
            $table = $this->modx->getFullTableName('site_htmlsnippets');
295
            $query = $this->modx->db->query(
296
                "SELECT `snippet` FROM " . $table . " WHERE `name`='" . $this->modx->db->escape($name) . "' AND `disabled`=0"
297
            );
298
            if ($this->modx->db->getRecordCount($query) == 1) {
299
                $row = $this->modx->db->getRow($query);
300
                $tpl = $row['snippet'];
301
            } else {
302
                $tpl = null;
303
            }
304
        }
305
306
        return $tpl;
307
    }
308
309
    /**
310
     * Рендер документа с подстановкой плейсхолдеров и выполнением сниппетов
311
     *
312
     * @param int $id ID документа
313
     * @param bool $events Во время рендера документа стоит ли вызывать события OnLoadWebDocument и OnLoadDocumentObject (внутри метода getDocumentObject).
314
     * @param mixed $tpl Шаблон с которым необходимо отрендерить документ. Возможные значения:
315
     *                       null - Использовать шаблон который назначен документу
316
     *                       int(0-n) - Получить шаблон из базы данных с указанным ID и применить его к документу
317
     *                       string - Применить шаблон указанный в строке к документу
318
     * @return string
319
     *
320
     * Событие OnLoadWebDocument дополнительно передает параметры:
321
     *       - с источиком от куда произошел вызов события
322
     *       - оригинальный экземпляр класса DocumentParser
323
     */
324
    public function renderDoc($id, $events = false, $tpl = null)
325
    {
326
        $id = (int)$id;
327
        if ($id <= 0) {
328
            return '';
329
        }
330
331
        $m = clone $this->modx; //Чтобы была возможность вызывать события
332
        $m->documentIdentifier = $id;
333
        $m->documentObject = $m->getDocumentObject('id', (int)$id, $events ? 'prepareResponse' : null);
334
        if ($m->documentObject['type'] == "reference") {
335
            if (is_numeric($m->documentObject['content']) && $m->documentObject['content'] > 0) {
336
                $m->documentObject['content'] = $this->renderDoc($m->documentObject['content'], $events);
337
            }
338
        }
339
        switch (true) {
340
            case is_integer($tpl):
341
                $tpl = $this->getTemplate($tpl);
342
                break;
343
            case is_string($tpl):
344
                break;
345
            case is_null($tpl):
346
            default:
347
                $tpl = $this->getTemplate($m->documentObject['template']);
348
        }
349
        $m->documentContent = $tpl;
350
        if ($events) {
351
            $m->invokeEvent("OnLoadWebDocument", array(
352
                'source'   => 'DLTemplate',
353
                'mainModx' => $this->modx,
354
            ));
355
        }
356
357
        return $this->parseDocumentSource($m->documentContent, $m);
358
    }
359
360
    /**
361
     * Получить содержимое шаблона с определенным номером
362
     * @param int $id Номер шаблона
363
     * @return string HTML код шаблона
364
     */
365
    public function getTemplate($id)
366
    {
367
        $tpl = null;
368
        $id = (int)$id;
369
        if ($id > 0) {
370
            $tpl = $this->modx->db->getValue("SELECT `content` FROM {$this->modx->getFullTableName("site_templates")} WHERE `id` = {$id}");
371
        }
372
        if (is_null($tpl)) {
373
            $tpl = '[*content*]';
374
        }
375
376
        return $tpl;
377
    }
378
379
    /**
380
     * refactor $modx->parseChunk();
381
     *
382
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
383
     * @param array $data paceholder
384
     * @param bool $parseDocumentSource render html template via DocumentParser::parseDocumentSource()
385
     * @return string html template with data without placeholders
386
     */
387
    public function parseChunk($name, $data = array(), $parseDocumentSource = false, $disablePHx = false)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
388
    {
389
        $out = $this->getChunk($name);
390
        switch (true) {
391
            case $this->twigEnabled && $out !== '' && ($twig = $this->getTwig($name, $out)):
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $twig is correct as $this->getTwig($name, $out) targeting DLTemplate::getTwig() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
392
                $out = $twig->render(md5($name), $this->getTemplateData($data));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $twig does not seem to be defined for all execution paths leading up to this point.
Loading history...
393
                break;
394
            case $this->bladeEnabled && $out !== '' && ($blade = $this->getBlade($name, $out)):
395
                $out = $blade->with($this->getTemplateData($data))->render();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $blade does not seem to be defined for all execution paths leading up to this point.
Loading history...
396
                break;
397
            case is_array($data) && ($out != ''):
398
                if (preg_match("/\[\+[A-Z0-9\.\_\-]+\+\]/is", $out)) {
399
                    $item = $this->renameKeyArr($data, '[', ']', '+');
400
                    $out = str_replace(array_keys($item), array_values($item), $out);
401
                }
402
                if (!$disablePHx && preg_match("/:([^:=]+)(?:=`(.*?)`(?=:[^:=]+|$))?/is", $out)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
403
                    if (is_null($this->phx) || !($this->phx instanceof DLphx)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
404
                        $this->phx = $this->createPHx(0, 1000);
405
                    }
406
                    $this->phx->placeholders = array();
407
                    $this->setPHxPlaceholders($data);
408
                    $out = $this->phx->Parse($out);
409
                }
410
                break;
411
        }
412
        if ($parseDocumentSource) {
413
            $out = $this->parseDocumentSource($out);
414
        }
415
416
        return $out;
417
    }
418
419
    /**
420
     *
421
     * @param string|array $value
422
     * @param string $key
423
     * @param string $path
424
     */
425
    public function setPHxPlaceholders($value = '', $key = '', $path = '')
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
426
    {
427
        $keypath = !empty($path) ? $path . "." . $key : $key;
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
428
        $this->phx->curPass = 0;
429
        if (is_array($value)) {
430
            foreach ($value as $subkey => $subval) {
431
                $this->setPHxPlaceholders($subval, $subkey, $keypath);
432
            }
433
        } else {
434
            $this->phx->setPHxVariable($keypath, $value);
435
        }
436
    }
437
438
    /**
439
     * Return clone of twig
440
     *
441
     * @param string $name
442
     * @param string $tpl
443
     * @return null
444
     */
445
    protected function getTwig($name, $tpl)
446
    {
447
        if (is_null($this->twig) && isset($this->modx->twig)) {
448
            $twig = clone $this->modx->twig;
449
            $this->twig = $twig;
450
        } else {
451
            $twig = $this->twig;
452
        }
453
        if ($twig && class_exists('Twig_Loader_Array')) {
454
            $twig->getLoader()->addLoader(
455
                new Twig_Loader_Array(array(
0 ignored issues
show
Bug introduced by
The type Twig_Loader_Array was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
456
                    md5($name) => $tpl
457
                ))
458
            );
459
        }
460
461
        return $twig;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $twig also could return the type Twig_Environment|object which is incompatible with the documented return type null.
Loading history...
462
    }
463
464
    /**
465
     * Return clone of blade
466
     *
467
     * @param string $name
468
     * @param string $tpl
469
     * @return Illuminate\View\Factory
0 ignored issues
show
Bug introduced by
The type Illuminate\View\Factory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
470
     */
471
    protected function getBlade($name, $tpl)
472
    {
473
        $out = null;
474
        try {
475
            /**
476
             * Illuminate\View\Factory $blade
477
             */
478
            $blade = $this->modx->blade;
479
            $cache = md5($name). '-'. sha1($tpl);
0 ignored issues
show
Coding Style introduced by
Concat operator must be surrounded by a single space
Loading history...
480
            $path = MODX_BASE_PATH . '/assets/cache/blade/' . $cache . '.blade.php';
481
            if (! file_exists($path)) {
482
                file_put_contents($path, $tpl);
483
            }
484
            $out = $blade->make('cache::' . $cache);
485
        } catch (\Exception $exception) {
486
            $this->modx->messageQuit($exception->getMessage());
487
        }
488
489
        return $out;
490
    }
491
492
    /**
493
     *
494
     * @param string $string
495
     * @return string
496
     */
497
    public function cleanPHx($string)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
498
    {
499
        preg_match_all('~\[(\+|\*|\()([^:\+\[\]]+)([^\[\]]*?)(\1|\))\]~s', $string, $matches);
500
        if ($matches[0]) {
501
            $string = str_replace($matches[0], '', $string);
502
        }
503
504
        return $string;
505
    }
506
507
    /**
508
     * @param int $debug
509
     * @param int $maxpass
510
     * @return DLphx
511
     */
512
    public function createPHx($debug = 0, $maxpass = 50)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
513
    {
514
        if (!class_exists('DLphx')) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
515
            include_once(__DIR__ . '/DLphx.class.php');
516
        }
517
518
        return new DLphx($this->modx, $debug, $maxpass);
519
    }
520
521
    /**
522
     * Переменовывание элементов массива
523
     *
524
     * @param array $data массив с данными
525
     * @param string $prefix префикс ключей
526
     * @param string $suffix суффикс ключей
527
     * @param string $sep разделитель суффиксов, префиксов и ключей массива
528
     * @return array массив с переименованными ключами
529
     */
530
    public function renameKeyArr($data, $prefix = '', $suffix = '', $sep = '.')
531
    {
532
        return APIhelpers::renameKeyArr($data, $prefix, $suffix, $sep);
533
    }
534
535
    /**
536
     * @param $out
537
     * @param DocumentParser|null $modx
538
     * @return mixed|string
539
     */
540
    public function parseDocumentSource($out, $modx = null)
541
    {
542
        if (!is_object($modx)) {
0 ignored issues
show
Coding Style introduced by
There must be a single space after a NOT operator; 0 found
Loading history...
543
            $modx = $this->modx;
544
        }
545
        $minPasses = empty($modx->minParserPasses) ? 2 : $modx->minParserPasses;
546
        $maxPasses = empty($modx->maxParserPasses) ? 10 : $modx->maxParserPasses;
547
        $site_status = $modx->getConfig('site_status');
548
        $modx->config['site_status'] = 0;
549
        for ($i = 1; $i <= $maxPasses; $i++) {
550
            $html = $out;
551
            if (preg_match('/\[\!(.*)\!\]/us', $out)) {
552
                $out = str_replace(array('[!', '!]'), array('[[', ']]'), $out);
553
            }
554
            if ($i <= $minPasses || $out != $html) {
555
                $out = $modx->parseDocumentSource($out);
556
            } else {
557
                break;
558
            }
559
        }
560
        $out = $modx->rewriteUrls($out);
561
        $out = $this->cleanPHx($out);
562
        $modx->config['site_status'] = $site_status;
563
564
        return $out;
565
    }
566
}
567