Passed
Push — master ( a46cb4...8ff499 )
by Michael
27:12 queued 15:45
created

compileConfigVariable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Smarty Internal Plugin Smarty Template Compiler Base
4
 * This file contains the basic classes and methods for compiling Smarty templates with lexer/parser
5
 *
6
 * @package    Smarty
7
 * @subpackage Compiler
8
 * @author     Uwe Tews
9
 */
10
11
/**
12
 * Main abstract compiler class
13
 *
14
 * @package    Smarty
15
 * @subpackage Compiler
16
 *
17
 * @property Smarty_Internal_SmartyTemplateCompiler $prefixCompiledCode  = ''
18
 * @property Smarty_Internal_SmartyTemplateCompiler $postfixCompiledCode = ''
19
 * @method   registerPostCompileCallback($callback, $parameter = array(), $key = null, $replace = false)
20
 * @method   unregisterPostCompileCallback($key)
21
 */
22
abstract class Smarty_Internal_TemplateCompilerBase
23
{
24
    /**
25
     * compile tag objects cache
26
     *
27
     * @var array
28
     */
29
    public static $_tag_objects = array();
30
31
    /**
32
     * counter for prefix variable number
33
     *
34
     * @var int
35
     */
36
    public static $prefixVariableNumber = 0;
37
38
    /**
39
     * Smarty object
40
     *
41
     * @var Smarty
42
     */
43
    public $smarty = null;
44
45
    /**
46
     * Parser object
47
     *
48
     * @var Smarty_Internal_Templateparser
49
     */
50
    public $parser = null;
51
52
    /**
53
     * hash for nocache sections
54
     *
55
     * @var mixed
56
     */
57
    public $nocache_hash = null;
58
59
    /**
60
     * suppress generation of nocache code
61
     *
62
     * @var bool
63
     */
64
    public $suppressNocacheProcessing = false;
65
66
    /**
67
     * caching enabled (copied from template object)
68
     *
69
     * @var int
70
     */
71
    public $caching = 0;
72
73
    /**
74
     * tag stack
75
     *
76
     * @var array
77
     */
78
    public $_tag_stack = array();
79
80
    /**
81
     * tag stack count
82
     *
83
     * @var array
84
     */
85
    public $_tag_stack_count = array();
86
87
    /**
88
     * Plugins used by template
89
     *
90
     * @var array
91
     */
92
    public $required_plugins = array('compiled' => array(), 'nocache' => array());
93
94
    /**
95
     * Required plugins stack
96
     *
97
     * @var array
98
     */
99
    public $required_plugins_stack = array();
100
101
    /**
102
     * current template
103
     *
104
     * @var Smarty_Internal_Template
105
     */
106
    public $template = null;
107
108
    /**
109
     * merged included sub template data
110
     *
111
     * @var array
112
     */
113
    public $mergedSubTemplatesData = array();
114
115
    /**
116
     * merged sub template code
117
     *
118
     * @var array
119
     */
120
    public $mergedSubTemplatesCode = array();
121
122
    /**
123
     * collected template properties during compilation
124
     *
125
     * @var array
126
     */
127
    public $templateProperties = array();
128
129
    /**
130
     * source line offset for error messages
131
     *
132
     * @var int
133
     */
134
    public $trace_line_offset = 0;
135
136
    /**
137
     * trace uid
138
     *
139
     * @var string
140
     */
141
    public $trace_uid = '';
142
143
    /**
144
     * trace file path
145
     *
146
     * @var string
147
     */
148
    public $trace_filepath = '';
149
150
    /**
151
     * stack for tracing file and line of nested {block} tags
152
     *
153
     * @var array
154
     */
155
    public $trace_stack = array();
156
157
    /**
158
     * plugins loaded by default plugin handler
159
     *
160
     * @var array
161
     */
162
    public $default_handler_plugins = array();
163
164
    /**
165
     * saved preprocessed modifier list
166
     *
167
     * @var mixed
168
     */
169
    public $default_modifier_list = null;
170
171
    /**
172
     * force compilation of complete template as nocache
173
     *
174
     * @var boolean
175
     */
176
    public $forceNocache = false;
177
178
    /**
179
     * flag if compiled template file shall we written
180
     *
181
     * @var bool
182
     */
183
    public $write_compiled_code = true;
184
185
    /**
186
     * Template functions
187
     *
188
     * @var array
189
     */
190
    public $tpl_function = array();
191
192
    /**
193
     * called sub functions from template function
194
     *
195
     * @var array
196
     */
197
    public $called_functions = array();
198
199
    /**
200
     * compiled template or block function code
201
     *
202
     * @var string
203
     */
204
    public $blockOrFunctionCode = '';
205
206
    /**
207
     * flags for used modifier plugins
208
     *
209
     * @var array
210
     */
211
    public $modifier_plugins = array();
212
213
    /**
214
     * type of already compiled modifier
215
     *
216
     * @var array
217
     */
218
    public $known_modifier_type = array();
219
220
    /**
221
     * parent compiler object for merged subtemplates and template functions
222
     *
223
     * @var Smarty_Internal_TemplateCompilerBase
224
     */
225
    public $parent_compiler = null;
226
227
    /**
228
     * Flag true when compiling nocache section
229
     *
230
     * @var bool
231
     */
232
    public $nocache = false;
233
234
    /**
235
     * Flag true when tag is compiled as nocache
236
     *
237
     * @var bool
238
     */
239
    public $tag_nocache = false;
240
241
    /**
242
     * Compiled tag prefix code
243
     *
244
     * @var array
245
     */
246
    public $prefix_code = array();
247
248
    /**
249
     * used prefix variables by current compiled tag
250
     *
251
     * @var array
252
     */
253
    public $usedPrefixVariables = array();
254
255
    /**
256
     * Prefix code  stack
257
     *
258
     * @var array
259
     */
260
    public $prefixCodeStack = array();
261
262
    /**
263
     * Tag has compiled code
264
     *
265
     * @var bool
266
     */
267
    public $has_code = false;
268
269
    /**
270
     * A variable string was compiled
271
     *
272
     * @var bool
273
     */
274
    public $has_variable_string = false;
275
276
    /**
277
     * Stack for {setfilter} {/setfilter}
278
     *
279
     * @var array
280
     */
281
    public $variable_filter_stack = array();
282
283
    /**
284
     * variable filters for {setfilter} {/setfilter}
285
     *
286
     * @var array
287
     */
288
    public $variable_filters = array();
289
290
    /**
291
     * Nesting count of looping tags like {foreach}, {for}, {section}, {while}
292
     *
293
     * @var int
294
     */
295
    public $loopNesting = 0;
296
297
    /**
298
     * Strip preg pattern
299
     *
300
     * @var string
301
     */
302
    public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!';
303
304
    /**
305
     * plugin search order
306
     *
307
     * @var array
308
     */
309
    public $plugin_search_order = array(
310
        'function',
311
        'block',
312
        'compiler',
313
        'class'
314
    );
315
316
    /**
317
     * General storage area for tag compiler plugins
318
     *
319
     * @var array
320
     */
321
    public $_cache = array();
322
323
    /**
324
     * Lexer preg pattern for left delimiter
325
     *
326
     * @var string
327
     */
328
    private $ldelPreg = '[{]';
329
330
    /**
331
     * Lexer preg pattern for right delimiter
332
     *
333
     * @var string
334
     */
335
    private $rdelPreg = '[}]';
336
337
    /**
338
     * Length of right delimiter
339
     *
340
     * @var int
341
     */
342
    private $rdelLength = 0;
343
344
    /**
345
     * Length of left delimiter
346
     *
347
     * @var int
348
     */
349
    private $ldelLength = 0;
350
351
    /**
352
     * Lexer preg pattern for user literals
353
     *
354
     * @var string
355
     */
356
    private $literalPreg = '';
357
358
    /**
359
     * Initialize compiler
360
     *
361
     * @param Smarty $smarty global instance
362
     */
363
    public function __construct(Smarty $smarty)
364
    {
365
        $this->smarty = $smarty;
366
        $this->nocache_hash = str_replace(
367
            array(
368
                '.',
369
                ','
370
            ),
371
            '_',
372
            uniqid(mt_rand(), true)
373
        );
374
    }
375
376
    /**
377
     * Method to compile a Smarty template
378
     *
379
     * @param Smarty_Internal_Template                  $template template object to compile
380
     * @param bool                                      $nocache  true is shall be compiled in nocache mode
381
     * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
382
     *
383
     * @return bool true if compiling succeeded, false if it failed
384
     * @throws \Exception
385
     */
386
    public function compileTemplate(
387
        Smarty_Internal_Template $template,
388
        $nocache = null,
389
        ?Smarty_Internal_TemplateCompilerBase $parent_compiler = null
390
    ) {
391
        // get code frame of compiled template
392
        $_compiled_code = $template->smarty->ext->_codeFrame->create(
393
            $template,
394
            $this->compileTemplateSource(
395
                $template,
396
                $nocache,
397
                $parent_compiler
398
            ),
399
            $this->postFilter($this->blockOrFunctionCode) .
400
            join('', $this->mergedSubTemplatesCode),
401
            false,
402
            $this
403
        );
404
        return $_compiled_code;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $_compiled_code returns the type string which is incompatible with the documented return type boolean.
Loading history...
405
    }
406
407
    /**
408
     * Compile template source and run optional post filter
409
     *
410
     * @param \Smarty_Internal_Template                  $template
411
     * @param null|bool                                  $nocache flag if template must be compiled in nocache mode
412
     * @param \Smarty_Internal_TemplateCompilerBase|null $parent_compiler
413
     *
414
     * @return string
415
     * @throws \Exception
416
     */
417
    public function compileTemplateSource(
418
        Smarty_Internal_Template $template,
419
        $nocache = null,
420
        ?Smarty_Internal_TemplateCompilerBase $parent_compiler = null
421
    ) {
422
        try {
423
            // save template object in compiler class
424
            $this->template = $template;
425
            if ($this->smarty->debugging) {
426
                if (!isset($this->smarty->_debug)) {
427
                    $this->smarty->_debug = new Smarty_Internal_Debug();
428
                }
429
                $this->smarty->_debug->start_compile($this->template);
430
            }
431
            $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
432
            $nocache = isset($nocache) ? $nocache : false;
433
            if (empty($template->compiled->nocache_hash)) {
434
                $template->compiled->nocache_hash = $this->nocache_hash;
435
            } else {
436
                $this->nocache_hash = $template->compiled->nocache_hash;
437
            }
438
            $this->caching = $template->caching;
439
            // flag for nocache sections
440
            $this->nocache = $nocache;
441
            $this->tag_nocache = false;
442
            // reset has nocache code flag
443
            $this->template->compiled->has_nocache_code = false;
444
            $this->has_variable_string = false;
445
            $this->prefix_code = array();
446
            // add file dependency
447
            if ($this->smarty->merge_compiled_includes || $this->template->source->handler->checkTimestamps()) {
448
                $this->parent_compiler->template->compiled->file_dependency[ $this->template->source->uid ] =
449
                    array(
450
                        $this->template->source->filepath,
451
                        $this->template->source->getTimeStamp(),
452
                        $this->template->source->type,
453
                    );
454
            }
455
            $this->smarty->_current_file = $this->template->source->filepath;
456
            // get template source
457
            if (!empty($this->template->source->components)) {
458
                $_compiled_code = '<?php $_smarty_tpl->_loadInheritance(); $_smarty_tpl->inheritance->init($_smarty_tpl, true); ?>';
459
460
                $i = 0;
461
                $reversed_components = array_reverse($this->template->getSource()->components);
0 ignored issues
show
Bug introduced by
The method getSource() does not exist on Smarty_Internal_Template. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

461
                $reversed_components = array_reverse($this->template->/** @scrutinizer ignore-call */ getSource()->components);
Loading history...
462
                foreach ($reversed_components as $source) {
463
                    $i++;
464
                    if ($i === count($reversed_components)) {
465
                        $_compiled_code .= '<?php $_smarty_tpl->inheritance->endChild($_smarty_tpl); ?>';
466
                    }
467
                    $_compiled_code .= $this->compileTag(
468
                        'include',
469
                        [
470
                            var_export($source->resource, true),
471
                            ['scope' => 'parent'],
472
                        ]
473
                    );
474
                }
475
                $_compiled_code = $this->postFilter($_compiled_code, $this->template);
0 ignored issues
show
Unused Code introduced by
The call to Smarty_Internal_TemplateCompilerBase::postFilter() has too many arguments starting with $this->template. ( Ignorable by Annotation )

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

475
                /** @scrutinizer ignore-call */ 
476
                $_compiled_code = $this->postFilter($_compiled_code, $this->template);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
476
            } else {
477
                // get template source
478
                $_content = $this->template->source->getContent();
479
                $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
480
            }
481
            if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) {
482
                $_compiled_code = '<?php ' . $this->compileRequiredPlugins() . "?>\n" . $_compiled_code;
483
            }
484
        } catch (Exception $e) {
485
            if ($this->smarty->debugging) {
486
                $this->smarty->_debug->end_compile($this->template);
487
            }
488
            $this->_tag_stack = array();
489
            // free memory
490
            $this->parent_compiler = null;
491
            $this->template = null;
492
            $this->parser = null;
493
            throw $e;
494
        }
495
        if ($this->smarty->debugging) {
496
            $this->smarty->_debug->end_compile($this->template);
497
        }
498
        $this->parent_compiler = null;
499
        $this->parser = null;
500
        return $_compiled_code;
501
    }
502
503
    /**
504
     * Optionally process compiled code by post filter
505
     *
506
     * @param string $code compiled code
507
     *
508
     * @return string
509
     * @throws \SmartyException
510
     */
511
    public function postFilter($code)
512
    {
513
        // run post filter if on code
514
        if (!empty($code)
515
            && (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
516
        ) {
517
            return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
518
        } else {
519
            return $code;
520
        }
521
    }
522
523
    /**
524
     * Run optional prefilter
525
     *
526
     * @param string $_content template source
527
     *
528
     * @return string
529
     * @throws \SmartyException
530
     */
531
    public function preFilter($_content)
532
    {
533
        // run pre filter if required
534
        if ($_content !== ''
535
            && ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
536
        ) {
537
            return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
538
        } else {
539
            return $_content;
540
        }
541
    }
542
543
    /**
544
     * Compile Tag
545
     * This is a call back from the lexer/parser
546
     *
547
     * Save current prefix code
548
     * Compile tag
549
     * Merge tag prefix code with saved one
550
     * (required nested tags in attributes)
551
     *
552
     * @param string $tag       tag name
553
     * @param array  $args      array with tag attributes
554
     * @param array  $parameter array with compilation parameter
555
     *
556
     * @throws SmartyCompilerException
557
     * @throws SmartyException
558
     * @return string compiled code
559
     */
560
    public function compileTag($tag, $args, $parameter = array())
561
    {
562
        $this->prefixCodeStack[] = $this->prefix_code;
563
        $this->prefix_code = array();
564
        $result = $this->compileTag2($tag, $args, $parameter);
565
        $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
566
        return $result;
567
    }
568
569
    /**
570
     * compile variable
571
     *
572
     * @param string $variable
573
     *
574
     * @return string
575
     */
576
    public function compileVariable($variable)
577
    {
578
        if (!strpos($variable, '(')) {
579
            // not a variable variable
580
            $var = trim($variable, '\'');
581
            $this->tag_nocache = $this->tag_nocache |
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
Documentation Bug introduced by
The property $tag_nocache was declared of type boolean, but $this->tag_nocache | $th..., true, false)->nocache is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
582
                                 $this->template->ext->getTemplateVars->_getVariable(
583
                                     $this->template,
584
                                     $var,
585
                                     null,
586
                                     true,
587
                                     false
588
                                 )->nocache;
589
            // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
590
        }
591
        return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
592
    }
593
594
    /**
595
     * compile config variable
596
     *
597
     * @param string $variable
598
     *
599
     * @return string
600
     */
601
    public function compileConfigVariable($variable)
602
    {
603
        // return '$_smarty_tpl->config_vars[' . $variable . ']';
604
        return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
605
    }
606
607
    /**
608
     * compile PHP function call
609
     *
610
     * @param string $name
611
     * @param array  $parameter
612
     *
613
     * @return string
614
     * @throws \SmartyCompilerException
615
     */
616
    public function compilePHPFunctionCall($name, $parameter)
617
    {
618
        if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
0 ignored issues
show
Deprecated Code introduced by
The function Smarty_Security::isTrustedPhpFunction() has been deprecated. ( Ignorable by Annotation )

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

618
        if (!$this->smarty->security_policy || /** @scrutinizer ignore-deprecated */ $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
Loading history...
619
            if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
620
                || strcasecmp($name, 'array') === 0
621
                || (is_callable($name) && !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name]))
622
            ) {
623
                $func_name = smarty_strtolower_ascii($name);
624
625
                if ($func_name === 'isset') {
626
                    if (count($parameter) === 0) {
627
                        $this->trigger_template_error('Illegal number of parameter in "isset()"');
628
                    }
629
630
                    $pa = array();
631
                    foreach ($parameter as $p) {
632
                        $pa[] = $this->syntaxMatchesVariable($p) ? 'isset(' . $p . ')' : '(' . $p . ' !== null )';
633
                    }
634
                    return '(' . implode(' && ', $pa) . ')';
635
636
                } elseif (in_array(
637
                    $func_name,
638
                    array(
639
                        'empty',
640
                        'reset',
641
                        'current',
642
                        'end',
643
                        'prev',
644
                        'next'
645
                    )
646
                )
647
                ) {
648
                    if (count($parameter) !== 1) {
649
                        $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'");
650
                    }
651
                    if ($func_name === 'empty') {
652
                        return $func_name . '(' .
653
                            str_replace("')->value", "',null,true,false)->value", $parameter[0]) . ')';
654
                    } else {
655
                        return $func_name . '(' . $parameter[0] . ')';
656
                    }
657
                } else {
658
659
                    if (
660
                        !$this->smarty->loadPlugin('smarty_modifiercompiler_' . $name)
661
                        && !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])
662
                        && !in_array($name, ['time', 'join', 'is_array', 'in_array', 'count'])
663
                    ) {
664
                        trigger_error('Using unregistered function "' . $name . '" in a template is deprecated and will be ' .
665
                            'removed in a future release. Use Smarty::registerPlugin to explicitly register ' .
666
                            'a custom modifier.', E_USER_DEPRECATED);
667
                    }
668
669
                    return $name . '(' . implode(',', $parameter) . ')';
670
                }
671
672
            }
673
        }
674
675
        if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])) {
676
            if ($name === $this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name][0]) {
677
                return $name . '(' . implode(',', $parameter) . ')';
678
            }
679
680
            return sprintf(
681
                'call_user_func_array($_smarty_tpl->registered_plugins[ \'%s\' ][ %s ][ 0 ], array( %s ))',
682
                Smarty::PLUGIN_MODIFIER,
683
                var_export($name, true),
684
                implode(',', $parameter)
685
            );
686
        }
687
688
        $this->trigger_template_error("unknown function '{$name}'");
689
    }
690
691
    /**
692
     * Determines whether the passed string represents a valid (PHP) variable.
693
     * This is important, because `isset()` only works on variables and `empty()` can only be passed
694
     * a variable prior to php5.5
695
     * @param $string
696
     * @return bool
697
     */
698
    private function syntaxMatchesVariable($string) {
699
        static $regex_pattern = '/^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*((->)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\[.*]*\])*$/';
700
        return 1 === preg_match($regex_pattern, trim($string));
701
    }
702
703
    /**
704
     * This method is called from parser to process a text content section if strip is enabled
705
     * - remove text from inheritance child templates as they may generate output
706
     *
707
     * @param string $text
708
     *
709
     * @return string
710
     */
711
    public function processText($text)
712
    {
713
714
        if (strpos($text, '<') === false) {
715
            return preg_replace($this->stripRegEx, '', $text);
716
        }
717
718
        $store = array();
719
        $_store = 0;
720
721
        // capture html elements not to be messed with
722
        $_offset = 0;
723
        if (preg_match_all(
724
            '#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
725
            $text,
726
            $matches,
727
            PREG_OFFSET_CAPTURE | PREG_SET_ORDER
728
        )
729
        ) {
730
            foreach ($matches as $match) {
731
                $store[] = $match[ 0 ][ 0 ];
732
                $_length = strlen($match[ 0 ][ 0 ]);
733
                $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
734
                $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
735
                $_offset += $_length - strlen($replace);
736
                $_store++;
737
            }
738
        }
739
        $expressions = array(// replace multiple spaces between tags by a single space
740
                             '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s'                            => '\1 \2',
741
                             // remove newline between tags
742
                             '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s'                     => '\1\2',
743
                             // remove multiple spaces between attributes (but not in attribute values!)
744
                             '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
745
                             '#>[\040\011]+$#Ss'                                                       => '> ',
746
                             '#>[\040\011]*[\n]\s*$#Ss'                                                => '>',
747
                             $this->stripRegEx                                                         => '',
748
        );
749
        $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
750
        $_offset = 0;
751
        if (preg_match_all(
752
            '#@!@SMARTY:([0-9]+):SMARTY@!@#is',
753
            $text,
754
            $matches,
755
            PREG_OFFSET_CAPTURE | PREG_SET_ORDER
756
        )
757
        ) {
758
            foreach ($matches as $match) {
759
                $_length = strlen($match[ 0 ][ 0 ]);
760
                $replace = $store[ $match[ 1 ][ 0 ] ];
761
                $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
762
                $_offset += strlen($replace) - $_length;
763
                $_store++;
764
            }
765
        }
766
        return $text;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $text also could return the type array which is incompatible with the documented return type string.
Loading history...
767
    }
768
769
    /**
770
     * lazy loads internal compile plugin for tag and calls the compile method
771
     * compile objects cached for reuse.
772
     * class name format:  Smarty_Internal_Compile_TagName
773
     * plugin filename format: Smarty_Internal_TagName.php
774
     *
775
     * @param string $tag    tag name
776
     * @param array  $args   list of tag attributes
777
     * @param mixed  $param1 optional parameter
778
     * @param mixed  $param2 optional parameter
779
     * @param mixed  $param3 optional parameter
780
     *
781
     * @return bool|string compiled code or false
782
     * @throws \SmartyCompilerException
783
     */
784
    public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
785
    {
786
        /* @var Smarty_Internal_CompileBase $tagCompiler */
787
        $tagCompiler = $this->getTagCompiler($tag);
788
        // compile this tag
789
        return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3);
0 ignored issues
show
Bug introduced by
The method compile() does not exist on Smarty_Internal_CompileBase. It seems like you code against a sub-type of said class. However, the method does not exist in Smarty_Internal_Compile_Shared_Inheritance or Smarty_Internal_Compile_Private_ForeachSection. Are you sure you never get one of those? ( Ignorable by Annotation )

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

789
        return $tagCompiler === false ? false : $tagCompiler->/** @scrutinizer ignore-call */ compile($args, $this, $param1, $param2, $param3);
Loading history...
introduced by
The condition $tagCompiler === false is always false.
Loading history...
790
    }
791
792
    /**
793
     * lazy loads internal compile plugin for tag compile objects cached for reuse.
794
     *
795
     * class name format:  Smarty_Internal_Compile_TagName
796
     * plugin filename format: Smarty_Internal_TagName.php
797
     *
798
     * @param string $tag tag name
799
     *
800
     * @return bool|\Smarty_Internal_CompileBase tag compiler object or false if not found
801
     */
802
    public function getTagCompiler($tag)
803
    {
804
        // re-use object if already exists
805
        if (!isset(self::$_tag_objects[ $tag ])) {
806
            // lazy load internal compiler plugin
807
            $_tag = explode('_', $tag);
808
            $_tag = array_map('smarty_ucfirst_ascii', $_tag);
809
            $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
810
            if (class_exists($class_name)
811
                && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
812
            ) {
813
                self::$_tag_objects[ $tag ] = new $class_name;
814
            } else {
815
                self::$_tag_objects[ $tag ] = false;
816
            }
817
        }
818
        return self::$_tag_objects[ $tag ];
819
    }
820
821
    /**
822
     * Check for plugins and return function name
823
     *
824
     * @param        $plugin_name
825
     * @param string $plugin_type type of plugin
826
     *
827
     * @return string call name of function
828
     * @throws \SmartyException
829
     */
830
    public function getPlugin($plugin_name, $plugin_type)
831
    {
832
        $function = null;
833
        if ($this->caching && ($this->nocache || $this->tag_nocache)) {
834
            if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
835
                $function =
836
                    $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
837
            } elseif (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
838
                $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] =
839
                    $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ];
840
                $function =
841
                    $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
842
            }
843
        } else {
844
            if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
845
                $function =
846
                    $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
847
            } elseif (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
848
                $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] =
849
                    $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ];
850
                $function =
851
                    $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
852
            }
853
        }
854
        if (isset($function)) {
855
            if ($plugin_type === 'modifier') {
856
                $this->modifier_plugins[ $plugin_name ] = true;
857
            }
858
            return $function;
859
        }
860
        // loop through plugin dirs and find the plugin
861
        $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
862
        $file = $this->smarty->loadPlugin($function, false);
863
        if (is_string($file)) {
864
            if ($this->caching && ($this->nocache || $this->tag_nocache)) {
865
                $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
866
                    $file;
867
                $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
868
                    $function;
869
            } else {
870
                $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
871
                    $file;
872
                $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
873
                    $function;
874
            }
875
            if ($plugin_type === 'modifier') {
876
                $this->modifier_plugins[ $plugin_name ] = true;
877
            }
878
            return $function;
879
        }
880
        if (is_callable($function)) {
881
            // plugin function is defined in the script
882
            return $function;
883
        }
884
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
885
    }
886
887
    /**
888
     * Check for plugins by default plugin handler
889
     *
890
     * @param string $tag         name of tag
891
     * @param string $plugin_type type of plugin
892
     *
893
     * @return bool true if found
894
     * @throws \SmartyCompilerException
895
     */
896
    public function getPluginFromDefaultHandler($tag, $plugin_type)
897
    {
898
        $callback = null;
899
        $script = null;
900
        $cacheable = true;
901
        $result = call_user_func_array(
902
            $this->smarty->default_plugin_handler_func,
903
            array(
904
                $tag,
905
                $plugin_type,
906
                $this->template,
907
                &$callback,
908
                &$script,
909
                &$cacheable,
910
            )
911
        );
912
        if ($result) {
913
            $this->tag_nocache = $this->tag_nocache || !$cacheable;
914
            if ($script !== null) {
0 ignored issues
show
introduced by
The condition $script !== null is always false.
Loading history...
915
                if (is_file($script)) {
916
                    if ($this->caching && ($this->nocache || $this->tag_nocache)) {
917
                        $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] =
918
                            $script;
919
                        $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] =
920
                            $callback;
921
                    } else {
922
                        $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] =
923
                            $script;
924
                        $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] =
925
                            $callback;
926
                    }
927
                    include_once $script;
928
                } else {
929
                    $this->trigger_template_error("Default plugin handler: Returned script file '{$script}' for '{$tag}' not found");
930
                }
931
            }
932
            if (is_callable($callback)) {
933
                $this->default_handler_plugins[ $plugin_type ][ $tag ] = array(
934
                    $callback,
935
                    true,
936
                    array()
937
                );
938
                return true;
939
            } else {
940
                $this->trigger_template_error("Default plugin handler: Returned callback for '{$tag}' not callable");
941
            }
942
        }
943
        return false;
944
    }
945
946
    /**
947
     * Append code segments and remove unneeded ?> <?php transitions
948
     *
949
     * @param string $left
950
     * @param string $right
951
     *
952
     * @return string
953
     */
954
    public function appendCode($left, $right)
955
    {
956
        if (preg_match('/\s*\?>\s?$/D', $left) && preg_match('/^<\?php\s+/', $right)) {
957
            $left = preg_replace('/\s*\?>\s?$/D', "\n", $left);
958
            $left .= preg_replace('/^<\?php\s+/', '', $right);
959
        } else {
960
            $left .= $right;
961
        }
962
        return $left;
963
    }
964
965
    /**
966
     * Inject inline code for nocache template sections
967
     * This method gets the content of each template element from the parser.
968
     * If the content is compiled code and it should be not cached the code is injected
969
     * into the rendered output.
970
     *
971
     * @param string  $content content of template element
972
     * @param boolean $is_code true if content is compiled code
973
     *
974
     * @return string  content
975
     */
976
    public function processNocacheCode($content, $is_code)
977
    {
978
        // If the template is not evaluated and we have a nocache section and or a nocache tag
979
        if ($is_code && !empty($content)) {
980
            // generate replacement code
981
            if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->caching
982
                && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
983
            ) {
984
                $this->template->compiled->has_nocache_code = true;
985
                $_output = addcslashes($content, '\'\\');
986
                $_output = str_replace('^#^', '\'', $_output);
987
                $_output =
988
                    "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/{$_output}/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
989
                // make sure we include modifier plugins for nocache code
990
                foreach ($this->modifier_plugins as $plugin_name => $dummy) {
991
                    if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) {
992
                        $this->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] =
993
                            $this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ];
994
                    }
995
                }
996
            } else {
997
                $_output = $content;
998
            }
999
        } else {
1000
            $_output = $content;
1001
        }
1002
        $this->modifier_plugins = array();
1003
        $this->suppressNocacheProcessing = false;
1004
        $this->tag_nocache = false;
1005
        return $_output;
1006
    }
1007
1008
    /**
1009
     * Get Id
1010
     *
1011
     * @param string $input
1012
     *
1013
     * @return bool|string
1014
     */
1015
    public function getId($input)
1016
    {
1017
        if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) {
1018
            return $match[ 2 ];
1019
        }
1020
        return false;
1021
    }
1022
1023
    /**
1024
     * Get variable name from string
1025
     *
1026
     * @param string $input
1027
     *
1028
     * @return bool|string
1029
     */
1030
    public function getVariableName($input)
1031
    {
1032
        if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) {
1033
            return $match[ 1 ];
1034
        }
1035
        return false;
1036
    }
1037
1038
    /**
1039
     * Set nocache flag in variable or create new variable
1040
     *
1041
     * @param string $varName
1042
     */
1043
    public function setNocacheInVariable($varName)
1044
    {
1045
        // create nocache var to make it know for further compiling
1046
        if ($_var = $this->getId($varName)) {
1047
            if (isset($this->template->tpl_vars[ $_var ])) {
1048
                $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ];
1049
                $this->template->tpl_vars[ $_var ]->nocache = true;
1050
            } else {
1051
                $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true);
1052
            }
1053
        }
1054
    }
1055
1056
    /**
1057
     * @param array $_attr tag attributes
1058
     * @param array $validScopes
1059
     *
1060
     * @return int|string
1061
     * @throws \SmartyCompilerException
1062
     */
1063
    public function convertScope($_attr, $validScopes)
1064
    {
1065
        $_scope = 0;
1066
        if (isset($_attr[ 'scope' ])) {
1067
            $_scopeName = trim($_attr[ 'scope' ], '\'"');
1068
            if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) {
1069
                $_scope = $_scopeName;
1070
            } elseif (is_string($_scopeName)) {
0 ignored issues
show
introduced by
The condition is_string($_scopeName) is always true.
Loading history...
1071
                $_scopeName = trim($_scopeName, '\'"');
1072
                $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false;
1073
            } else {
1074
                $_scope = false;
1075
            }
1076
            if ($_scope === false) {
1077
                $err = var_export($_scopeName, true);
1078
                $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true);
1079
            }
1080
        }
1081
        return $_scope;
1082
    }
1083
1084
    /**
1085
     * Generate nocache code string
1086
     *
1087
     * @param string $code PHP code
1088
     *
1089
     * @return string
1090
     */
1091
    public function makeNocacheCode($code)
1092
    {
1093
        return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " .
1094
               str_replace('^#^', '\'', addcslashes($code, '\'\\')) .
1095
               "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
1096
    }
1097
1098
    /**
1099
     * display compiler error messages without dying
1100
     * If parameter $args is empty it is a parser detected syntax error.
1101
     * In this case the parser is called to obtain information about expected tokens.
1102
     * If parameter $args contains a string this is used as error message
1103
     *
1104
     * @param string    $args    individual error message or null
1105
     * @param string    $line    line-number
1106
     * @param null|bool $tagline if true the line number of last tag
1107
     *
1108
     * @throws \SmartyCompilerException when an unexpected token is found
1109
     */
1110
    public function trigger_template_error($args = null, $line = null, $tagline = null)
1111
    {
1112
        $lex = $this->parser->lex;
1113
        if ($tagline === true) {
1114
            // get line number of Tag
1115
            $line = $lex->taglineno;
1116
        } elseif (!isset($line)) {
1117
            // get template source line which has error
1118
            $line = $lex->line;
1119
        } else {
1120
            $line = (int)$line;
1121
        }
1122
        if (in_array(
1123
            $this->template->source->type,
1124
            array(
1125
                'eval',
1126
                'string'
1127
            )
1128
        )
1129
        ) {
1130
            $templateName = $this->template->source->type . ':' . trim(
1131
                    preg_replace(
1132
                        '![\t\r\n]+!',
1133
                        ' ',
1134
                        strlen($lex->data) > 40 ?
1135
                            substr($lex->data, 0, 40) .
1136
                            '...' : $lex->data
1137
                    )
1138
                );
1139
        } else {
1140
            $templateName = $this->template->source->type . ':' . $this->template->source->filepath;
1141
        }
1142
        //        $line += $this->trace_line_offset;
1143
        $match = preg_split("/\n/", $lex->data);
1144
        $error_text =
1145
            'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) .
1146
            '"  on line ' . ($line + $this->trace_line_offset) . ' "' .
1147
            trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" ';
1148
        if (isset($args)) {
1149
            // individual error message
1150
            $error_text .= $args;
1151
        } else {
1152
            $expect = array();
1153
            // expected token from parser
1154
            $error_text .= ' - Unexpected "' . $lex->value . '"';
1155
            if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
1156
                foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
1157
                    $exp_token = $this->parser->yyTokenName[ $token ];
1158
                    if (isset($lex->smarty_token_names[ $exp_token ])) {
1159
                        // token type from lexer
1160
                        $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"';
1161
                    } else {
1162
                        // otherwise internal token name
1163
                        $expect[] = $this->parser->yyTokenName[ $token ];
1164
                    }
1165
                }
1166
                $error_text .= ', expected one of: ' . implode(' , ', $expect);
1167
            }
1168
        }
1169
        if ($this->smarty->_parserdebug) {
1170
            $this->parser->errorRunDown();
1171
            echo ob_get_clean();
1172
            flush();
1173
        }
1174
        $e = new SmartyCompilerException(
1175
            $error_text,
1176
            0,
1177
            $this->template->source->filepath,
1178
            $line
1179
        );
1180
        $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
1181
        $e->desc = $args;
1182
        $e->template = $this->template->source->filepath;
1183
        throw $e;
1184
    }
1185
1186
    /**
1187
     * Return var_export() value with all white spaces removed
1188
     *
1189
     * @param mixed $value
1190
     *
1191
     * @return string
1192
     */
1193
    public function getVarExport($value)
1194
    {
1195
        return preg_replace('/\s/', '', var_export($value, true));
1196
    }
1197
1198
    /**
1199
     *  enter double quoted string
1200
     *  - save tag stack count
1201
     */
1202
    public function enterDoubleQuote()
1203
    {
1204
        array_push($this->_tag_stack_count, $this->getTagStackCount());
1205
    }
1206
1207
    /**
1208
     * Return tag stack count
1209
     *
1210
     * @return int
1211
     */
1212
    public function getTagStackCount()
1213
    {
1214
        return count($this->_tag_stack);
1215
    }
1216
1217
    /**
1218
     * @param $lexerPreg
1219
     *
1220
     * @return mixed
1221
     */
1222
    public function replaceDelimiter($lexerPreg)
1223
    {
1224
        return str_replace(
1225
            array('SMARTYldel', 'SMARTYliteral', 'SMARTYrdel', 'SMARTYautoliteral', 'SMARTYal'),
1226
            array(
1227
                $this->ldelPreg, $this->literalPreg, $this->rdelPreg,
1228
                $this->smarty->getAutoLiteral() ? '{1,}' : '{9}',
1229
                $this->smarty->getAutoLiteral() ? '' : '\\s*'
1230
            ),
1231
            $lexerPreg
1232
        );
1233
    }
1234
1235
    /**
1236
     * Build lexer regular expressions for left and right delimiter and user defined literals
1237
     */
1238
    public function initDelimiterPreg()
1239
    {
1240
        $ldel = $this->smarty->getLeftDelimiter();
1241
        $this->ldelLength = strlen($ldel);
1242
        $this->ldelPreg = '';
1243
        foreach (str_split($ldel, 1) as $chr) {
1244
            $this->ldelPreg .= '[' . preg_quote($chr,'/') . ']';
1245
        }
1246
        $rdel = $this->smarty->getRightDelimiter();
1247
        $this->rdelLength = strlen($rdel);
1248
        $this->rdelPreg = '';
1249
        foreach (str_split($rdel, 1) as $chr) {
1250
            $this->rdelPreg .= '[' . preg_quote($chr,'/') . ']';
1251
        }
1252
        $literals = $this->smarty->getLiterals();
1253
        if (!empty($literals)) {
1254
            foreach ($literals as $key => $literal) {
1255
                $literalPreg = '';
1256
                foreach (str_split($literal, 1) as $chr) {
1257
                    $literalPreg .= '[' . preg_quote($chr,'/') . ']';
1258
                }
1259
                $literals[ $key ] = $literalPreg;
1260
            }
1261
            $this->literalPreg = '|' . implode('|', $literals);
1262
        } else {
1263
            $this->literalPreg = '';
1264
        }
1265
    }
1266
1267
    /**
1268
     *  leave double quoted string
1269
     *  - throw exception if block in string was not closed
1270
     *
1271
     * @throws \SmartyCompilerException
1272
     */
1273
    public function leaveDoubleQuote()
1274
    {
1275
        if (array_pop($this->_tag_stack_count) !== $this->getTagStackCount()) {
1276
            $tag = $this->getOpenBlockTag();
1277
            $this->trigger_template_error(
1278
                "unclosed '{{$tag}}' in doubled quoted string",
1279
                null,
1280
                true
1281
            );
1282
        }
1283
    }
1284
1285
    /**
1286
     * Get left delimiter preg
1287
     *
1288
     * @return string
1289
     */
1290
    public function getLdelPreg()
1291
    {
1292
        return $this->ldelPreg;
1293
    }
1294
1295
    /**
1296
     * Get right delimiter preg
1297
     *
1298
     * @return string
1299
     */
1300
    public function getRdelPreg()
1301
    {
1302
        return $this->rdelPreg;
1303
    }
1304
1305
    /**
1306
     * Get length of left delimiter
1307
     *
1308
     * @return int
1309
     */
1310
    public function getLdelLength()
1311
    {
1312
        return $this->ldelLength;
1313
    }
1314
1315
    /**
1316
     * Get length of right delimiter
1317
     *
1318
     * @return int
1319
     */
1320
    public function getRdelLength()
1321
    {
1322
        return $this->rdelLength;
1323
    }
1324
1325
    /**
1326
     * Get name of current open block tag
1327
     *
1328
     * @return string|boolean
1329
     */
1330
    public function getOpenBlockTag()
1331
    {
1332
        $tagCount = $this->getTagStackCount();
1333
        if ($tagCount) {
1334
            return $this->_tag_stack[ $tagCount - 1 ][ 0 ];
1335
        } else {
1336
            return false;
1337
        }
1338
    }
1339
1340
    /**
1341
     * Check if $value contains variable elements
1342
     *
1343
     * @param mixed $value
1344
     *
1345
     * @return bool|int
1346
     */
1347
    public function isVariable($value)
1348
    {
1349
        if (is_string($value)) {
1350
            return preg_match('/[$(]/', $value);
1351
        }
1352
        if (is_bool($value) || is_numeric($value)) {
1353
            return false;
1354
        }
1355
        if (is_array($value)) {
1356
            foreach ($value as $k => $v) {
1357
                if ($this->isVariable($k) || $this->isVariable($v)) {
1358
                    return true;
1359
                }
1360
            }
1361
            return false;
1362
        }
1363
        return false;
1364
    }
1365
1366
    /**
1367
     * Get new prefix variable name
1368
     *
1369
     * @return string
1370
     */
1371
    public function getNewPrefixVariable()
1372
    {
1373
        ++self::$prefixVariableNumber;
1374
        return $this->getPrefixVariable();
1375
    }
1376
1377
    /**
1378
     * Get current prefix variable name
1379
     *
1380
     * @return string
1381
     */
1382
    public function getPrefixVariable()
1383
    {
1384
        return '$_prefixVariable' . self::$prefixVariableNumber;
1385
    }
1386
1387
    /**
1388
     * append  code to prefix buffer
1389
     *
1390
     * @param string $code
1391
     */
1392
    public function appendPrefixCode($code)
1393
    {
1394
        $this->prefix_code[] = $code;
1395
    }
1396
1397
    /**
1398
     * get prefix code string
1399
     *
1400
     * @return string
1401
     */
1402
    public function getPrefixCode()
1403
    {
1404
        $code = '';
1405
        $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
1406
        $this->prefixCodeStack[] = array();
1407
        foreach ($prefixArray as $c) {
1408
            $code = $this->appendCode($code, $c);
1409
        }
1410
        $this->prefix_code = array();
1411
        return $code;
1412
    }
1413
1414
    /**
1415
     * Save current required plugins
1416
     *
1417
     * @param bool $init if true init required plugins
1418
     */
1419
    public function saveRequiredPlugins($init = false)
1420
    {
1421
        $this->required_plugins_stack[] = $this->required_plugins;
1422
        if ($init) {
1423
            $this->required_plugins = array('compiled' => array(), 'nocache' => array());
1424
        }
1425
    }
1426
1427
    /**
1428
     * Restore required plugins
1429
     */
1430
    public function restoreRequiredPlugins()
1431
    {
1432
        $this->required_plugins = array_pop($this->required_plugins_stack);
1433
    }
1434
1435
    /**
1436
     * Compile code to call Smarty_Internal_Template::_checkPlugins()
1437
     * for required plugins
1438
     *
1439
     * @return string
1440
     */
1441
    public function compileRequiredPlugins()
1442
    {
1443
        $code = $this->compileCheckPlugins($this->required_plugins[ 'compiled' ]);
1444
        if ($this->caching && !empty($this->required_plugins[ 'nocache' ])) {
1445
            $code .= $this->makeNocacheCode($this->compileCheckPlugins($this->required_plugins[ 'nocache' ]));
1446
        }
1447
        return $code;
1448
    }
1449
1450
    /**
1451
     * Compile code to call Smarty_Internal_Template::_checkPlugins
1452
     *   - checks if plugin is callable require otherwise
1453
     *
1454
     * @param $requiredPlugins
1455
     *
1456
     * @return string
1457
     */
1458
    public function compileCheckPlugins($requiredPlugins)
1459
    {
1460
        if (!empty($requiredPlugins)) {
1461
            $plugins = array();
1462
            foreach ($requiredPlugins as $plugin) {
1463
                foreach ($plugin as $data) {
1464
                    $plugins[] = $data;
1465
                }
1466
            }
1467
            return '$_smarty_tpl->_checkPlugins(' . $this->getVarExport($plugins) . ');' . "\n";
1468
        } else {
1469
            return '';
1470
        }
1471
    }
1472
1473
    /**
1474
     * method to compile a Smarty template
1475
     *
1476
     * @param mixed $_content template source
1477
     * @param bool  $isTemplateSource
1478
     *
1479
     * @return bool true if compiling succeeded, false if it failed
1480
     */
1481
    abstract protected function doCompile($_content, $isTemplateSource = false);
1482
1483
    public function cStyleComment($string) {
1484
        return '/*' . str_replace('*/', '* /' , $string) . '*/';
1485
    }
1486
1487
    /**
1488
     * Compile Tag
1489
     *
1490
     * @param string $tag       tag name
1491
     * @param array  $args      array with tag attributes
1492
     * @param array  $parameter array with compilation parameter
1493
     *
1494
     * @throws SmartyCompilerException
1495
     * @throws SmartyException
1496
     * @return string compiled code
1497
     */
1498
    private function compileTag2($tag, $args, $parameter)
1499
    {
1500
        $plugin_type = '';
1501
        // $args contains the attributes parsed and compiled by the lexer/parser
1502
        // assume that tag does compile into code, but creates no HTML output
1503
        $this->has_code = true;
1504
        // log tag/attributes
1505
        if (isset($this->smarty->_cache[ 'get_used_tags' ])) {
1506
            $this->template->_cache[ 'used_tags' ][] = array(
1507
                $tag,
1508
                $args
1509
            );
1510
        }
1511
        // check nocache option flag
1512
        foreach ($args as $arg) {
1513
            if (!is_array($arg)) {
1514
                if ($arg === "'nocache'" || $arg === 'nocache') {
1515
                    $this->tag_nocache = true;
1516
                }
1517
            } else {
1518
                foreach ($arg as $k => $v) {
1519
                    if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) {
1520
                        $this->tag_nocache = true;
1521
                    }
1522
                }
1523
            }
1524
        }
1525
        // compile the smarty tag (required compile classes to compile the tag are auto loaded)
1526
        if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
1527
            if (isset($this->parent_compiler->tpl_function[ $tag ])
1528
                || (isset($this->template->smarty->ext->_tplFunction)
1529
                    && $this->template->smarty->ext->_tplFunction->getTplFunction($this->template, $tag) !== false)
1530
            ) {
1531
                // template defined by {template} tag
1532
                $args[ '_attr' ][ 'name' ] = "'{$tag}'";
1533
                $_output = $this->callTagCompiler('call', $args, $parameter);
1534
            }
1535
        }
1536
        if ($_output !== false) {
1537
            if ($_output !== true) {
1538
                // did we get compiled code
1539
                if ($this->has_code) {
1540
                    // return compiled code
1541
                    return $_output;
1542
                }
1543
            }
1544
            // tag did not produce compiled code
1545
            return null;
1546
        } else {
1547
            // map_named attributes
1548
            if (isset($args[ '_attr' ])) {
1549
                foreach ($args[ '_attr' ] as $key => $attribute) {
1550
                    if (is_array($attribute)) {
1551
                        $args = array_merge($args, $attribute);
1552
                    }
1553
                }
1554
            }
1555
            // not an internal compiler tag
1556
            if (strlen($tag) < 6 || substr($tag, -5) !== 'close') {
1557
                // check if tag is a registered object
1558
                if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) {
1559
                    $method = $parameter[ 'object_method' ];
1560
                    if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])
1561
                        && (empty($this->smarty->registered_objects[ $tag ][ 1 ])
1562
                            || in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ]))
1563
                    ) {
1564
                        return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...rameter, $tag, $method) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1565
                    } elseif (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) {
1566
                        return $this->callTagCompiler(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...rameter, $tag, $method) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1567
                            'private_object_block_function',
1568
                            $args,
1569
                            $parameter,
1570
                            $tag,
1571
                            $method
1572
                        );
1573
                    } else {
1574
                        // throw exception
1575
                        $this->trigger_template_error(
1576
                            'not allowed method "' . $method . '" in registered object "' .
1577
                            $tag . '"',
1578
                            null,
1579
                            true
1580
                        );
1581
                    }
1582
                }
1583
                // check if tag is registered
1584
                foreach (array(
1585
                    Smarty::PLUGIN_COMPILER,
1586
                    Smarty::PLUGIN_FUNCTION,
1587
                    Smarty::PLUGIN_BLOCK,
1588
                ) as $plugin_type) {
1589
                    if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) {
1590
                        // if compiler function plugin call it now
1591
                        if ($plugin_type === Smarty::PLUGIN_COMPILER) {
1592
                            $new_args = array();
1593
                            foreach ($args as $key => $mixed) {
1594
                                if (is_array($mixed)) {
1595
                                    $new_args = array_merge($new_args, $mixed);
1596
                                } else {
1597
                                    $new_args[ $key ] = $mixed;
1598
                                }
1599
                            }
1600
                            if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) {
1601
                                $this->tag_nocache = true;
1602
                            }
1603
                            return call_user_func_array(
1604
                                $this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ],
1605
                                array(
1606
                                    $new_args,
1607
                                    $this
1608
                                )
1609
                            );
1610
                        }
1611
                        // compile registered function or block function
1612
                        if ($plugin_type === Smarty::PLUGIN_FUNCTION || $plugin_type === Smarty::PLUGIN_BLOCK) {
1613
                            return $this->callTagCompiler(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...args, $parameter, $tag) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1614
                                'private_registered_' . $plugin_type,
1615
                                $args,
1616
                                $parameter,
1617
                                $tag
1618
                            );
1619
                        }
1620
                    }
1621
                }
1622
                // check plugins from plugins folder
1623
                foreach ($this->plugin_search_order as $plugin_type) {
1624
                    if ($plugin_type === Smarty::PLUGIN_COMPILER
1625
                        && $this->smarty->loadPlugin('smarty_compiler_' . $tag)
1626
                        && (!isset($this->smarty->security_policy)
1627
                            || $this->smarty->security_policy->isTrustedTag($tag, $this))
1628
                    ) {
1629
                        $plugin = 'smarty_compiler_' . $tag;
1630
                        if (is_callable($plugin)) {
1631
                            // convert arguments format for old compiler plugins
1632
                            $new_args = array();
1633
                            foreach ($args as $key => $mixed) {
1634
                                if (is_array($mixed)) {
1635
                                    $new_args = array_merge($new_args, $mixed);
1636
                                } else {
1637
                                    $new_args[ $key ] = $mixed;
1638
                                }
1639
                            }
1640
                            return $plugin($new_args, $this->smarty);
1641
                        }
1642
                        if (class_exists($plugin, false)) {
1643
                            $plugin_object = new $plugin;
1644
                            if (method_exists($plugin_object, 'compile')) {
1645
                                return $plugin_object->compile($args, $this);
1646
                            }
1647
                        }
1648
                        throw new SmartyException("Plugin '{$tag}' not callable");
1649
                    } else {
1650
                        if ($function = $this->getPlugin($tag, $plugin_type)) {
1651
                            if (!isset($this->smarty->security_policy)
1652
                                || $this->smarty->security_policy->isTrustedTag($tag, $this)
1653
                            ) {
1654
                                return $this->callTagCompiler(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...meter, $tag, $function) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1655
                                    'private_' . $plugin_type . '_plugin',
1656
                                    $args,
1657
                                    $parameter,
1658
                                    $tag,
1659
                                    $function
1660
                                );
1661
                            }
1662
                        }
1663
                    }
1664
                }
1665
                if (is_callable($this->smarty->default_plugin_handler_func)) {
1666
                    $found = false;
1667
                    // look for already resolved tags
1668
                    foreach ($this->plugin_search_order as $plugin_type) {
1669
                        if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) {
1670
                            $found = true;
1671
                            break;
1672
                        }
1673
                    }
1674
                    if (!$found) {
1675
                        // call default handler
1676
                        foreach ($this->plugin_search_order as $plugin_type) {
1677
                            if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
1678
                                $found = true;
1679
                                break;
1680
                            }
1681
                        }
1682
                    }
1683
                    if ($found) {
1684
                        // if compiler function plugin call it now
1685
                        if ($plugin_type === Smarty::PLUGIN_COMPILER) {
1686
                            $new_args = array();
1687
                            foreach ($args as $key => $mixed) {
1688
                                if (is_array($mixed)) {
1689
                                    $new_args = array_merge($new_args, $mixed);
1690
                                } else {
1691
                                    $new_args[ $key ] = $mixed;
1692
                                }
1693
                            }
1694
                            return call_user_func_array(
1695
                                $this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ],
1696
                                array(
1697
                                    $new_args,
1698
                                    $this
1699
                                )
1700
                            );
1701
                        } else {
1702
                            return $this->callTagCompiler(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...args, $parameter, $tag) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1703
                                'private_registered_' . $plugin_type,
1704
                                $args,
1705
                                $parameter,
1706
                                $tag
1707
                            );
1708
                        }
1709
                    }
1710
                }
1711
            } else {
1712
                // compile closing tag of block function
1713
                $base_tag = substr($tag, 0, -5);
1714
                // check if closing tag is a registered object
1715
                if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) {
1716
                    $method = $parameter[ 'object_method' ];
1717
                    if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) {
1718
                        return $this->callTagCompiler(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...rameter, $tag, $method) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1719
                            'private_object_block_function',
1720
                            $args,
1721
                            $parameter,
1722
                            $tag,
1723
                            $method
1724
                        );
1725
                    } else {
1726
                        // throw exception
1727
                        $this->trigger_template_error(
1728
                            'not allowed closing tag method "' . $method .
1729
                            '" in registered object "' . $base_tag . '"',
1730
                            null,
1731
                            true
1732
                        );
1733
                    }
1734
                }
1735
                // registered block tag ?
1736
                if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
1737
                    || isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
1738
                ) {
1739
                    return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...args, $parameter, $tag) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1740
                }
1741
                // registered function tag ?
1742
                if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) {
1743
                    return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...args, $parameter, $tag) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1744
                }
1745
                // block plugin?
1746
                if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
1747
                    return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...meter, $tag, $function) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1748
                }
1749
                // function plugin?
1750
                if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) {
1751
                    if (!isset($this->smarty->security_policy)
1752
                        || $this->smarty->security_policy->isTrustedTag($tag, $this)
1753
                    ) {
1754
                        return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callTagCom...meter, $tag, $function) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
1755
                    }
1756
                }
1757
                // registered compiler plugin ?
1758
                if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) {
1759
                    // if compiler function plugin call it now
1760
                    $args = array();
1761
                    if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) {
1762
                        $this->tag_nocache = true;
1763
                    }
1764
                    return call_user_func_array(
1765
                        $this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ],
1766
                        array(
1767
                            $args,
1768
                            $this
1769
                        )
1770
                    );
1771
                }
1772
                if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
1773
                    $plugin = 'smarty_compiler_' . $tag;
1774
                    if (is_callable($plugin)) {
1775
                        return $plugin($args, $this->smarty);
1776
                    }
1777
                    if (class_exists($plugin, false)) {
1778
                        $plugin_object = new $plugin;
1779
                        if (method_exists($plugin_object, 'compile')) {
1780
                            return $plugin_object->compile($args, $this);
1781
                        }
1782
                    }
1783
                    throw new SmartyException("Plugin '{$tag}' not callable");
1784
                }
1785
            }
1786
            $this->trigger_template_error("unknown tag '{$tag}'", null, true);
1787
        }
1788
    }
1789
}
1790