Completed
Branch master (1f607b)
by Agel_Nash
08:24 queued 22s
created

DLTemplate::getTwig()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 4
nop 2
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 8 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
include_once(MODX_BASE_PATH . 'assets/lib/APIHelpers.class.php');
4
5
/**
6
 * Class DLTemplate
7
 */
8
class DLTemplate
1 ignored issue
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9
{
10
    /**
11
     * Объект DocumentParser - основной класс MODX
12
     * @var \DocumentParser
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
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
    /**
72
     * prevent from being unserialized
73
     *
74
     * @return void
75
     */
76
    private function __wakeup()
77
    {
78
79
    }
80
81
    /**
82
     * Задает относительный путь к папке с шаблонами
83
     *
84
     * @param $path
85
     * @return $this
86
     */
87
    public function setTemplatePath($path)
88
    {
89
        $path = trim($path);
90
        $path = preg_replace(array(
91
            '/\.*[\/|\\\]/i',
92
            '/[\/|\\\]+/i'
93
        ), array('/', '/'), $path);
94
95
        if (!empty($path)) {
96
            $this->templatePath = $path;
97
        }
98
99
        return $this;
100
    }
101
102
    /**
103
     * Задает расширение файла с шаблоном
104
     *
105
     * @param $ext
106
     * @return $this
107
     */
108
    public function setTemplateExtension($ext)
109
    {
110
        $ext = trim($ext, ". \t\n\r\0\x0B");
111
        if (!empty($ext)) {
112
            $this->templateExtension = $ext;
113
        }
114
115
        return $this;
116
    }
117
118
    /**
119
     * Additional data for twig templates
120
     *
121
     * @param array $data
122
     * @return $this
123
     */
124
    public function setTwigTemplateVars($data = array()) {
125
        if (is_array($data)) $this->twigTemplateVars = $data;
126
        return $this;
127
    }
128
129
    /**
130
     * Сохранение данных в массив плейсхолдеров
131
     *
132
     * @param mixed $data данные
133
     * @param int $set устанавливать ли глобальнй плейсхолдер MODX
134
     * @param string $key ключ локального плейсхолдера
135
     * @param string $prefix префикс для ключей массива
136
     * @return string
137
     */
138
    public function toPlaceholders($data, $set = 0, $key = 'contentPlaceholder', $prefix = '')
139
    {
140
        $out = '';
141
        if ($set != 0) {
142
            $this->modx->toPlaceholder($key, $data, $prefix);
143
        } else {
144
            $out = $data;
145
        }
146
147
        return $out;
148
    }
149
150
    /**
151
     * refactor $modx->getChunk();
152
     *
153
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
154
     * @return string html template with placeholders without data
155
     */
156
    public function getChunk($name)
157
    {
158
        $tpl = '';
159
        $this->twigEnabled = substr($name,0,3) == '@T_';
160
        if ($name != '' && !isset($this->modx->chunkCache[$name])) {
161
            $mode = (preg_match('/^((@[A-Z_]+)[:]{0,1})(.*)/Asu', trim($name),
162
                    $tmp) && isset($tmp[2], $tmp[3])) ? $tmp[2] : false;
163
            $subTmp = (isset($tmp[3])) ? trim($tmp[3]) : null;
164
            if ($this->twigEnabled) $mode = '@'.substr($mode,3);
165
            switch ($mode) {
166
                case '@FILE':
167
                    if ($subTmp != '') {
168
                        $real = realpath(MODX_BASE_PATH . $this->templatePath);
169
                        $path = realpath(MODX_BASE_PATH . $this->templatePath . preg_replace(array(
170
                                '/\.*[\/|\\\]/i',
171
                                '/[\/|\\\]+/i'
172
                            ), array('/', '/'), $subTmp) . '.' . $this->templateExtension);
173
                        $fname = explode(".", $path);
174
                        if ($real == substr($path, 0,
175
                                strlen($real)) && end($fname) == $this->templateExtension && file_exists($path)
176
                        ) {
177
                            $tpl = file_get_contents($path);
178
                        }
179
                    }
180
                    break;
181
                case '@CHUNK':
182
                    if ($subTmp != '') {
183
                        $tpl = $this->modx->getChunk($subTmp);
184
                    }
185
                    break;
186
                case '@INLINE':
187
                case '@TPL':
188
                case '@CODE':
189
                    $tpl = $tmp[3]; //without trim
190
                    break;
191
                case '@DOCUMENT':
192
                case '@DOC':
193
                    switch (true) {
194
                        case ((int)$subTmp > 0):
195
                            $tpl = $this->modx->getPageInfo((int)$subTmp, 0, "content");
196
                            $tpl = isset($tpl['content']) ? $tpl['content'] : '';
197
                            break;
198
                        case ((int)$subTmp == 0):
199
                            $tpl = $this->modx->documentObject['content'];
200
                            break;
201
                    }
202
                    break;
203
                case '@PLH':
204
                case '@PLACEHOLDER':
205
                    if ($subTmp != '') {
206
                        $tpl = $this->modx->getPlaceholder($subTmp);
207
                    }
208
                    break;
209
                case '@CFG':
210
                case '@CONFIG':
211
                case '@OPTIONS':
212
                    if ($subTmp != '') {
213
                        $tpl = $this->modx->getConfig($subTmp);
214
                    }
215
                    break;
216
                case '@SNIPPET':
217
                    if ($subTmp != '') {
218
                        $tpl = $this->modx->runSnippet($subTmp, $this->modx->event->params);
219
                    }
220
                    break;
221
                case '@RENDERPAGE':
222
                    $tpl = $this->renderDoc($subTmp, false);
223
                    break;
224
                case '@LOADPAGE':
225
                    $tpl = $this->renderDoc($subTmp, true);
226
                    break;
227
                case '@TEMPLATE':
228
                    $tpl = $this->getTemplate($subTmp);
229
                    break;
230
                default:
231
                    $tpl = $this->modx->getChunk($name);
232
            }
233
            $this->modx->chunkCache[$name] = $tpl;
234
        } else {
235
            if ($name != '') {
236
                $tpl = $this->modx->getChunk($name);
237
            }
238
        }
239
240
        return $tpl;
241
    }
242
243
    /**
244
     * Рендер документа с подстановкой плейсхолдеров и выполнением сниппетов
245
     *
246
     * @param int $id ID документа
247
     * @param bool $events Во время рендера документа стоит ли вызывать события OnLoadWebDocument и OnLoadDocumentObject (внутри метода getDocumentObject).
248
     * @param mixed $tpl Шаблон с которым необходимо отрендерить документ. Возможные значения:
249
     *                       null - Использовать шаблон который назначен документу
250
     *                       int(0-n) - Получить шаблон из базы данных с указанным ID и применить его к документу
251
     *                       string - Применить шаблон указанный в строке к документу
252
     * @return string
253
     *
254
     * Событие OnLoadWebDocument дополнительно передает параметры:
255
     *       - с источиком от куда произошел вызов события
256
     *       - оригинальный экземпляр класса DocumentParser
257
     */
258
    public function renderDoc($id, $events = false, $tpl = null)
259
    {
260
        if ((int)$id <= 0) {
261
            return '';
262
        }
263
264
        $m = clone $this->modx; //Чтобы была возможность вызывать события
265
        $m->documentObject = $m->getDocumentObject('id', (int)$id, $events ? 'prepareResponse' : null);
266
        if ($m->documentObject['type'] == "reference") {
267
            if (is_numeric($m->documentObject['content']) && $m->documentObject['content'] > 0) {
268
                $m->documentObject['content'] = $this->renderDoc($m->documentObject['content'], $events);
269
            }
270
        }
271
        switch (true) {
272
            case is_integer($tpl):
273
                $tpl = $this->getTemplate($tpl);
274
                break;
275
            case is_string($tpl):
276
                break;
277
            case is_null($tpl):
278
            default:
279
                $tpl = $this->getTemplate($m->documentObject['template']);
280
        }
281
        $m->documentContent = $tpl;
282
        if ($events) {
283
            $m->invokeEvent("OnLoadWebDocument", array(
284
                'source'   => 'DLTemplate',
285
                'mainModx' => $this->modx,
286
            ));
287
        }
288
289
        return $this->parseDocumentSource($m->documentContent, $m);
290
    }
291
292
    /**
293
     * Получить содержимое шаблона с определенным номером
294
     * @param int $id Номер шаблона
295
     * @return string HTML код шаблона
296
     */
297
    public function getTemplate($id)
298
    {
299
        $tpl = null;
300
        if ($id > 0) {
301
            $tpl = $this->modx->db->getValue("SELECT `content` FROM {$this->modx->getFullTableName("site_templates")} WHERE `id` = '{$id}'");
302
        }
303
        if (is_null($tpl)) {
304
            $tpl = '[*content*]';
305
        }
306
307
        return $tpl;
308
    }
309
310
    /**
311
     * refactor $modx->parseChunk();
312
     *
313
     * @param string $name Template: chunk name || @CODE: template || @FILE: file with template
314
     * @param array $data paceholder
315
     * @param bool $parseDocumentSource render html template via DocumentParser::parseDocumentSource()
316
     * @return string html template with data without placeholders
317
     */
318
    public function parseChunk($name, $data = array(), $parseDocumentSource = false)
319
    {
320
        $out = $this->getChunk($name);
321
        if ($this->twigEnabled && ($out != '') && ($twig = $this->getTwig($name, $out))) {
322
            $plh = $this->twigTemplateVars;
323
            $plh['data'] = $data;
324
            $plh['modx'] = $this->modx;
325
            $out = $twig->render(md5($name),$plh);
326
        } else {
327
            if (is_array($data) && ($out != '')) {
328
                if (preg_match("/\[\+[A-Z0-9\.\_\-]+\+\]/is", $out)) {
329
                    $item = $this->renameKeyArr($data, '[', ']', '+');
330
                    $out = str_replace(array_keys($item), array_values($item), $out);
331
                }
332
                if (preg_match("/:([^:=]+)(?:=`(.*?)`(?=:[^:=]+|$))?/is", $out)) {
333
                    if (is_null($this->phx) || !($this->phx instanceof DLphx)) {
334
                        $this->phx = $this->createPHx(0, 1000);
335
                    }
336
                    $this->phx->placeholders = array();
337
                    $this->setPHxPlaceholders($data);
338
                    $out = $this->phx->Parse($out);
339
                    $out = $this->cleanPHx($out);
340
                }
341
            }
342
        }
343
        if ($parseDocumentSource) {
344
            $out = $this->parseDocumentSource($out);
345
        }
346
347
        return $out;
348
    }
349
350
    /**
351
     *
352
     * @param string|array $value
353
     * @param string $key
354
     * @param string $path
355
     */
356
    public function setPHxPlaceholders($value = '', $key = '', $path = '')
357
    {
358
        $keypath = !empty($path) ? $path . "." . $key : $key;
359
        $this->phx->curPass = 0;
360
        if (is_array($value)) {
361
            foreach ($value as $subkey => $subval) {
362
                $this->setPHxPlaceholders($subval, $subkey, $keypath);
363
            }
364
        } else {
365
            $this->phx->setPHxVariable($keypath, $value);
366
        }
367
    }
368
369
    /**
370
     * Return clone of twig
371
     *
372
     * @return null
373
     */
374
    protected function getTwig($name, $tpl) {
375
        if (is_null($this->twig) && isset($this->modx->twig)) {
376
            $twig = clone $this->modx->twig;
377
            $this->twig = $twig;
378
        } else {
379
            $twig = $this->twig;
380
        }
381
        if ($twig && class_exists('Twig_Loader_Array')) {
382
            $twig->getLoader()->addLoader(
383
                new Twig_Loader_Array(array(
384
                    md5($name) => $tpl
385
                ))
386
            );
387
        }
388
        return $twig;
389
    }
390
391
    /**
392
     *
393
     * @param string $string
394
     * @return string
395
     */
396
    public function cleanPHx($string)
397
    {
398
        preg_match_all('~\[(\+|\*|\()([^:\+\[\]]+)([^\[\]]*?)(\1|\))\]~s', $string, $matches);
399
        if ($matches[0]) {
400
            $string = str_replace($matches[0], '', $string);
401
        }
402
403
        return $string;
404
    }
405
406
    /**
407
     * @param int $debug
408
     * @param int $maxpass
409
     * @return DLphx
410
     */
411
    public function createPHx($debug = 0, $maxpass = 50)
412
    {
413
        if (!class_exists('DLphx', false)) {
414
            include_once(__DIR__ . '/DLphx.class.php');
415
        }
416
417
        return new DLphx($debug, $maxpass);
418
    }
419
420
    /**
421
     * Переменовывание элементов массива
422
     *
423
     * @param array $data массив с данными
424
     * @param string $prefix префикс ключей
425
     * @param string $suffix суффикс ключей
426
     * @param string $sep разделитель суффиксов, префиксов и ключей массива
427
     * @return array массив с переименованными ключами
428
     */
429
    public function renameKeyArr($data, $prefix = '', $suffix = '', $sep = '.')
430
    {
431
        return APIhelpers::renameKeyArr($data, $prefix, $suffix, $sep);
432
    }
433
434
    /**
435
     * @param $out
436
     * @param DocumentParser|null $modx
437
     * @return mixed|string
438
     */
439
    public function parseDocumentSource($out, $modx = null)
440
    {
441
        if (!is_object($modx)) {
442
            $modx = $this->modx;
443
        }
444
        $minPasses = empty ($modx->minParserPasses) ? 2 : $modx->minParserPasses;
445
        $maxPasses = empty ($modx->maxParserPasses) ? 10 : $modx->maxParserPasses;
446
        $site_status = $modx->getConfig('site_status');
447
        $modx->config['site_status'] = 0;
448
        for ($i = 1; $i <= $maxPasses; $i++) {
449
            $html = $out;
450
            if (preg_match('/\[\!(.*)\!\]/us', $out)) {
451
                $out = str_replace(array('[!', '!]'), array('[[', ']]'), $out);
452
            }
453
            if ($i <= $minPasses || $out != $html) {
454
                $out = $modx->parseDocumentSource($out);
455
            } else {
456
                break;
457
            }
458
        }
459
        $out = $modx->rewriteUrls($out);
460
        $modx->config['site_status'] = $site_status;
461
462
        return $out;
463
    }
464
}
465