Completed
Push — master ( 42ada8...66ea0c )
by Maxim
03:24
created

DLTemplate::getTemplateExtension()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 1
nc 1
nop 0
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 = null;
30
31
    protected $twigEnabled = false;
32
33
    protected $twigTemplateVars = array();
34
35
    public $phx = null;
36
37
    /**
38
     * gets the instance via lazy initialization (created on first usage)
39
     *
40
     * @return self
41
     */
42
    public static function getInstance(DocumentParser $modx)
43
    {
44
45
        if (null === self::$instance) {
46
            self::$instance = new self($modx);
47
        }
48
49
        return self::$instance;
50
    }
51
52
    /**
53
     * is not allowed to call from outside: private!
54
     *
55
     */
56
    private function __construct(DocumentParser $modx)
57
    {
58
        $this->modx = $modx;
59
    }
60
61
    /**
62
     * prevent the instance from being cloned
63
     *
64
     * @return void
65
     */
66
    private function __clone()
67
    {
68
    }
69
70
    /**
71
     * prevent from being unserialized
72
     *
73
     * @return void
74
     */
75
    private function __wakeup()
76
    {
77
    }
78
79
    /**
80
     * Задает относительный путь к папке с шаблонами
81
     *
82
     * @param $path
83
     * @return $this
84
     */
85
    public function setTemplatePath($path)
86
    {
87
        $path = trim($path);
88
        $path = preg_replace(array(
89
            '/\.*[\/|\\\]/i',
90
            '/[\/|\\\]+/i'
91
        ), array('/', '/'), $path);
92
93
        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...
94
            $this->templatePath = $path;
95
        }
96
97
        return $this;
98
    }
99
100
    /**
101
     * Задает расширение файла с шаблоном
102
     *
103
     * @param $ext
104
     * @return $this
105
     */
106
    public function setTemplateExtension($ext)
107
    {
108
        $ext = trim($ext, ". \t\n\r\0\x0B");
109
        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...
110
            $this->templateExtension = $ext;
111
        }
112
113
        return $this;
114
    }
115
116
    /**
117
     * Additional data for twig templates
118
     *
119
     * @param array $data
120
     * @return $this
121
     */
122
    public function setTwigTemplateVars($data = array())
123
    {
124
        if (is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
125
            $this->twigTemplateVars = $data;
126
        }
127
128
        return $this;
129
    }
130
131
    /**
132
     * Сохранение данных в массив плейсхолдеров
133
     *
134
     * @param mixed $data данные
135
     * @param int $set устанавливать ли глобальнй плейсхолдер MODX
136
     * @param string $key ключ локального плейсхолдера
137
     * @param string $prefix префикс для ключей массива
138
     * @return string
139
     */
140
    public function toPlaceholders($data, $set = 0, $key = 'contentPlaceholder', $prefix = '')
141
    {
142
        $out = '';
143
        if ($set != 0) {
144
            $this->modx->toPlaceholder($key, $data, $prefix);
145
        } else {
146
            $out = $data;
147
        }
148
149
        return $out;
150
    }
151
152
    /**
153
     * refactor $modx->getChunk();
154
     *
155
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
156
     * @return string html template with placeholders without data
157
     */
158
    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...
159
    {
160
        $tpl = '';
161
        $this->twigEnabled = substr($name, 0, 3) == '@T_';
162
        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...
163
            $mode = (preg_match(
164
                '/^((@[A-Z_]+)[:]{0,1})(.*)/Asu',
165
                trim($name),
166
                $tmp
167
            ) && isset($tmp[2], $tmp[3])) ? $tmp[2] : false;
168
            $subTmp = (isset($tmp[3])) ? trim($tmp[3]) : null;
169
            if ($this->twigEnabled) {
170
                $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

170
                $mode = '@' . substr(/** @scrutinizer ignore-type */ $mode, 3);
Loading history...
171
            }
172
            switch ($mode) {
173
                case '@FILE':
174
                    if ($subTmp != '') {
175
                        $real = realpath(MODX_BASE_PATH . $this->templatePath);
176
                        $path = realpath(MODX_BASE_PATH . $this->templatePath . preg_replace(array(
177
                                '/\.*[\/|\\\]/i',
178
                                '/[\/|\\\]+/i'
179
                            ), array('/', '/'), $subTmp) . '.' . $this->templateExtension);
180
                        $fname = explode(".", $path);
181
                        if ($real == substr(
182
                            $path,
183
                            0,
184
                            strlen($real)
185
                        ) && end($fname) == $this->templateExtension && file_exists($path)
186
                        ) {
187
                            $tpl = file_get_contents($path);
188
                        }
189
                    }
190
                    break;
191
                case '@CHUNK':
192
                    if ($subTmp != '') {
193
                        $tpl = $this->modx->getChunk($subTmp);
194
                    }
195
                    break;
196
                case '@INLINE':
197
                case '@TPL':
198
                case '@CODE':
199
                    $tpl = $tmp[3]; //without trim
200
                    break;
201
                case '@DOCUMENT':
202
                case '@DOC':
203
                    switch (true) {
204
                        case ((int)$subTmp > 0):
205
                            $tpl = $this->modx->getPageInfo((int)$subTmp, 0, "content");
206
                            $tpl = isset($tpl['content']) ? $tpl['content'] : '';
207
                            break;
208
                        case ((int)$subTmp == 0):
209
                            $tpl = $this->modx->documentObject['content'];
210
                            break;
211
                    }
212
                    break;
213
                case '@PLH':
214
                case '@PLACEHOLDER':
215
                    if ($subTmp != '') {
216
                        $tpl = $this->modx->getPlaceholder($subTmp);
217
                    }
218
                    break;
219
                case '@CFG':
220
                case '@CONFIG':
221
                case '@OPTIONS':
222
                    if ($subTmp != '') {
223
                        $tpl = $this->modx->getConfig($subTmp);
224
                    }
225
                    break;
226
                case '@SNIPPET':
227
                    if ($subTmp != '') {
228
                        $tpl = $this->modx->runSnippet($subTmp, $this->modx->event->params);
229
                    }
230
                    break;
231
                case '@RENDERPAGE':
232
                    $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

232
                    $tpl = $this->renderDoc(/** @scrutinizer ignore-type */ $subTmp, false);
Loading history...
233
                    break;
234
                case '@LOADPAGE':
235
                    $tpl = $this->renderDoc($subTmp, true);
236
                    break;
237
                case '@TEMPLATE':
238
                    $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

238
                    $tpl = $this->getTemplate(/** @scrutinizer ignore-type */ $subTmp);
Loading history...
239
                    break;
240
                default:
241
                    $tpl = $this->modx->getChunk($name);
242
            }
243
            $this->modx->chunkCache[$name] = $tpl;
244
        } else {
245
            if ($name != '') {
246
                $tpl = $this->modx->getChunk($name);
247
            }
248
        }
249
250
        return $tpl;
251
    }
252
253
    /**
254
     * Рендер документа с подстановкой плейсхолдеров и выполнением сниппетов
255
     *
256
     * @param int $id ID документа
257
     * @param bool $events Во время рендера документа стоит ли вызывать события OnLoadWebDocument и OnLoadDocumentObject (внутри метода getDocumentObject).
258
     * @param mixed $tpl Шаблон с которым необходимо отрендерить документ. Возможные значения:
259
     *                       null - Использовать шаблон который назначен документу
260
     *                       int(0-n) - Получить шаблон из базы данных с указанным ID и применить его к документу
261
     *                       string - Применить шаблон указанный в строке к документу
262
     * @return string
263
     *
264
     * Событие OnLoadWebDocument дополнительно передает параметры:
265
     *       - с источиком от куда произошел вызов события
266
     *       - оригинальный экземпляр класса DocumentParser
267
     */
268
    public function renderDoc($id, $events = false, $tpl = null)
269
    {
270
        $id = (int)$id;
271
        if ($id <= 0) {
272
            return '';
273
        }
274
275
        $m = clone $this->modx; //Чтобы была возможность вызывать события
276
        $m->documentIdentifier = $id;
277
        $m->documentObject = $m->getDocumentObject('id', (int)$id, $events ? 'prepareResponse' : null);
278
        if ($m->documentObject['type'] == "reference") {
279
            if (is_numeric($m->documentObject['content']) && $m->documentObject['content'] > 0) {
280
                $m->documentObject['content'] = $this->renderDoc($m->documentObject['content'], $events);
281
            }
282
        }
283
        switch (true) {
284
            case is_integer($tpl):
285
                $tpl = $this->getTemplate($tpl);
286
                break;
287
            case is_string($tpl):
288
                break;
289
            case is_null($tpl):
290
            default:
291
                $tpl = $this->getTemplate($m->documentObject['template']);
292
        }
293
        $m->documentContent = $tpl;
294
        if ($events) {
295
            $m->invokeEvent("OnLoadWebDocument", array(
296
                'source'   => 'DLTemplate',
297
                'mainModx' => $this->modx,
298
            ));
299
        }
300
301
        return $this->parseDocumentSource($m->documentContent, $m);
302
    }
303
304
    /**
305
     * Получить содержимое шаблона с определенным номером
306
     * @param int $id Номер шаблона
307
     * @return string HTML код шаблона
308
     */
309
    public function getTemplate($id)
310
    {
311
        $tpl = null;
312
        $id = (int)$id;
313
        if ($id > 0) {
314
            $tpl = $this->modx->db->getValue("SELECT `content` FROM {$this->modx->getFullTableName("site_templates")} WHERE `id` = {$id}");
315
        }
316
        if (is_null($tpl)) {
317
            $tpl = '[*content*]';
318
        }
319
320
        return $tpl;
321
    }
322
323
    /**
324
     * refactor $modx->parseChunk();
325
     *
326
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
327
     * @param array $data paceholder
328
     * @param bool $parseDocumentSource render html template via DocumentParser::parseDocumentSource()
329
     * @return string html template with data without placeholders
330
     */
331
    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...
332
    {
333
        $out = $this->getChunk($name);
334
        if ($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...
335
            $plh = $this->twigTemplateVars;
336
            $plh['data'] = $data;
337
            $plh['modx'] = $this->modx;
338
            $out = $twig->render(md5($name), $plh);
339
        } else {
340
            if (is_array($data) && ($out != '')) {
341
                if (preg_match("/\[\+[A-Z0-9\.\_\-]+\+\]/is", $out)) {
342
                    $item = $this->renameKeyArr($data, '[', ']', '+');
343
                    $out = str_replace(array_keys($item), array_values($item), $out);
344
                }
345
                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...
346
                    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...
347
                        $this->phx = $this->createPHx(0, 1000);
348
                    }
349
                    $this->phx->placeholders = array();
350
                    $this->setPHxPlaceholders($data);
351
                    $out = $this->phx->Parse($out);
352
                    $out = $this->cleanPHx($out);
0 ignored issues
show
Bug introduced by
It seems like $out can also be of type mixed; however, parameter $string of DLTemplate::cleanPHx() 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

352
                    $out = $this->cleanPHx(/** @scrutinizer ignore-type */ $out);
Loading history...
353
                }
354
            }
355
        }
356
        if ($parseDocumentSource) {
357
            $out = $this->parseDocumentSource($out);
358
        }
359
360
        return $out;
361
    }
362
363
    /**
364
     *
365
     * @param string|array $value
366
     * @param string $key
367
     * @param string $path
368
     */
369
    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...
370
    {
371
        $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...
372
        $this->phx->curPass = 0;
373
        if (is_array($value)) {
374
            foreach ($value as $subkey => $subval) {
375
                $this->setPHxPlaceholders($subval, $subkey, $keypath);
376
            }
377
        } else {
378
            $this->phx->setPHxVariable($keypath, $value);
379
        }
380
    }
381
382
    /**
383
     * Return clone of twig
384
     *
385
     * @param string $name
386
     * @param string $tpl
387
     * @return null
388
     */
389
    protected function getTwig($name, $tpl)
390
    {
391
        if (is_null($this->twig) && isset($this->modx->twig)) {
392
            $twig = clone $this->modx->twig;
393
            $this->twig = $twig;
394
        } else {
395
            $twig = $this->twig;
396
        }
397
        if ($twig && class_exists('Twig_Loader_Array')) {
398
            $twig->getLoader()->addLoader(
399
                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...
400
                    md5($name) => $tpl
401
                ))
402
            );
403
        }
404
405
        return $twig;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $twig also could return the type object|Twig_Environment which is incompatible with the documented return type null.
Loading history...
406
    }
407
408
    /**
409
     *
410
     * @param string $string
411
     * @return string
412
     */
413
    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...
414
    {
415
        preg_match_all('~\[(\+|\*|\()([^:\+\[\]]+)([^\[\]]*?)(\1|\))\]~s', $string, $matches);
416
        if ($matches[0]) {
417
            $string = str_replace($matches[0], '', $string);
418
        }
419
420
        return $string;
421
    }
422
423
    /**
424
     * @param int $debug
425
     * @param int $maxpass
426
     * @return DLphx
427
     */
428
    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...
429
    {
430
        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...
431
            include_once(__DIR__ . '/DLphx.class.php');
432
        }
433
434
        return new DLphx($this->modx, $debug, $maxpass);
435
    }
436
437
    /**
438
     * Переменовывание элементов массива
439
     *
440
     * @param array $data массив с данными
441
     * @param string $prefix префикс ключей
442
     * @param string $suffix суффикс ключей
443
     * @param string $sep разделитель суффиксов, префиксов и ключей массива
444
     * @return array массив с переименованными ключами
445
     */
446
    public function renameKeyArr($data, $prefix = '', $suffix = '', $sep = '.')
447
    {
448
        return APIhelpers::renameKeyArr($data, $prefix, $suffix, $sep);
449
    }
450
451
    /**
452
     * @param $out
453
     * @param DocumentParser|null $modx
454
     * @return mixed|string
455
     */
456
    public function parseDocumentSource($out, $modx = null)
457
    {
458
        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...
459
            $modx = $this->modx;
460
        }
461
        $minPasses = empty($modx->minParserPasses) ? 2 : $modx->minParserPasses;
462
        $maxPasses = empty($modx->maxParserPasses) ? 10 : $modx->maxParserPasses;
463
        $site_status = $modx->getConfig('site_status');
464
        $modx->config['site_status'] = 0;
465
        for ($i = 1; $i <= $maxPasses; $i++) {
466
            $html = $out;
467
            if (preg_match('/\[\!(.*)\!\]/us', $out)) {
468
                $out = str_replace(array('[!', '!]'), array('[[', ']]'), $out);
469
            }
470
            if ($i <= $minPasses || $out != $html) {
471
                $out = $modx->parseDocumentSource($out);
472
            } else {
473
                break;
474
            }
475
        }
476
        $out = $modx->rewriteUrls($out);
477
        $out = $this->cleanPHx($out);
478
        $modx->config['site_status'] = $site_status;
479
480
        return $out;
481
    }
482
}
483