getRdelPreg()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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