convertScope()   B
last analyzed

Complexity

Conditions 7
Paths 9

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 1
b 0
f 0
nc 9
nop 2
dl 0
loc 19
rs 8.8333
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
     * php_handling setting either from Smarty or security
208
     *
209
     * @var int
210
     */
211
    public $php_handling = 0;
212
213
    /**
214
     * flags for used modifier plugins
215
     *
216
     * @var array
217
     */
218
    public $modifier_plugins = array();
219
220
    /**
221
     * type of already compiled modifier
222
     *
223
     * @var array
224
     */
225
    public $known_modifier_type = array();
226
227
    /**
228
     * parent compiler object for merged subtemplates and template functions
229
     *
230
     * @var Smarty_Internal_TemplateCompilerBase
231
     */
232
    public $parent_compiler = null;
233
234
    /**
235
     * Flag true when compiling nocache section
236
     *
237
     * @var bool
238
     */
239
    public $nocache = false;
240
241
    /**
242
     * Flag true when tag is compiled as nocache
243
     *
244
     * @var bool
245
     */
246
    public $tag_nocache = false;
247
248
    /**
249
     * Compiled tag prefix code
250
     *
251
     * @var array
252
     */
253
    public $prefix_code = array();
254
255
    /**
256
     * used prefix variables by current compiled tag
257
     *
258
     * @var array
259
     */
260
    public $usedPrefixVariables = array();
261
262
    /**
263
     * Prefix code  stack
264
     *
265
     * @var array
266
     */
267
    public $prefixCodeStack = array();
268
269
    /**
270
     * Tag has compiled code
271
     *
272
     * @var bool
273
     */
274
    public $has_code = false;
275
276
    /**
277
     * A variable string was compiled
278
     *
279
     * @var bool
280
     */
281
    public $has_variable_string = false;
282
283
    /**
284
     * Stack for {setfilter} {/setfilter}
285
     *
286
     * @var array
287
     */
288
    public $variable_filter_stack = array();
289
290
    /**
291
     * variable filters for {setfilter} {/setfilter}
292
     *
293
     * @var array
294
     */
295
    public $variable_filters = array();
296
297
    /**
298
     * Nesting count of looping tags like {foreach}, {for}, {section}, {while}
299
     *
300
     * @var int
301
     */
302
    public $loopNesting = 0;
303
304
    /**
305
     * Strip preg pattern
306
     *
307
     * @var string
308
     */
309
    public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!';
310
311
    /**
312
     * plugin search order
313
     *
314
     * @var array
315
     */
316
    public $plugin_search_order = array(
317
        'function',
318
        'block',
319
        'compiler',
320
        'class'
321
    );
322
323
    /**
324
     * General storage area for tag compiler plugins
325
     *
326
     * @var array
327
     */
328
    public $_cache = array();
329
330
    /**
331
     * Lexer preg pattern for left delimiter
332
     *
333
     * @var string
334
     */
335
    private $ldelPreg = '[{]';
336
337
    /**
338
     * Lexer preg pattern for right delimiter
339
     *
340
     * @var string
341
     */
342
    private $rdelPreg = '[}]';
343
344
    /**
345
     * Length of right delimiter
346
     *
347
     * @var int
348
     */
349
    private $rdelLength = 0;
350
351
    /**
352
     * Length of left delimiter
353
     *
354
     * @var int
355
     */
356
    private $ldelLength = 0;
357
358
    /**
359
     * Lexer preg pattern for user literals
360
     *
361
     * @var string
362
     */
363
    private $literalPreg = '';
364
365
    /**
366
     * Initialize compiler
367
     *
368
     * @param Smarty $smarty global instance
369
     */
370
    public function __construct(Smarty $smarty)
371
    {
372
        $this->smarty = $smarty;
373
        $this->nocache_hash = str_replace(
374
            array(
375
                '.',
376
                ','
377
            ),
378
            '_',
379
            uniqid(mt_rand(), true)
380
        );
381
    }
382
383
    /**
384
     * Method to compile a Smarty template
385
     *
386
     * @param Smarty_Internal_Template                  $template template object to compile
387
     * @param bool                                      $nocache  true is shall be compiled in nocache mode
388
     * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
389
     *
390
     * @return bool true if compiling succeeded, false if it failed
391
     * @throws \Exception
392
     */
393
    public function compileTemplate(
394
        Smarty_Internal_Template $template,
395
        $nocache = null,
396
        Smarty_Internal_TemplateCompilerBase $parent_compiler = null
397
    ) {
398
        // get code frame of compiled template
399
        $_compiled_code = $template->smarty->ext->_codeFrame->create(
400
            $template,
401
            $this->compileTemplateSource(
402
                $template,
403
                $nocache,
404
                $parent_compiler
405
            ),
406
            $this->postFilter($this->blockOrFunctionCode) .
407
            join('', $this->mergedSubTemplatesCode),
408
            false,
409
            $this
410
        );
411
        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...
412
    }
413
414
    /**
415
     * Compile template source and run optional post filter
416
     *
417
     * @param \Smarty_Internal_Template             $template
418
     * @param null|bool                             $nocache flag if template must be compiled in nocache mode
419
     * @param \Smarty_Internal_TemplateCompilerBase $parent_compiler
420
     *
421
     * @return string
422
     * @throws \Exception
423
     */
424
    public function compileTemplateSource(
425
        Smarty_Internal_Template $template,
426
        $nocache = null,
427
        Smarty_Internal_TemplateCompilerBase $parent_compiler = null
428
    ) {
429
        try {
430
            // save template object in compiler class
431
            $this->template = $template;
432
            if (property_exists($this->template->smarty, 'plugin_search_order')) {
433
                $this->plugin_search_order = $this->template->smarty->plugin_search_order;
0 ignored issues
show
Bug Best Practice introduced by
The property plugin_search_order does not exist on Smarty. Since you implemented __get, consider adding a @property annotation.
Loading history...
434
            }
435
            if ($this->smarty->debugging) {
436
                if (!isset($this->smarty->_debug)) {
437
                    $this->smarty->_debug = new Smarty_Internal_Debug();
438
                }
439
                $this->smarty->_debug->start_compile($this->template);
440
            }
441
            if (isset($this->template->smarty->security_policy)) {
442
                $this->php_handling = $this->template->smarty->security_policy->php_handling;
443
            } else {
444
                $this->php_handling = $this->template->smarty->php_handling;
445
            }
446
            $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
447
            $nocache = isset($nocache) ? $nocache : false;
448
            if (empty($template->compiled->nocache_hash)) {
449
                $template->compiled->nocache_hash = $this->nocache_hash;
450
            } else {
451
                $this->nocache_hash = $template->compiled->nocache_hash;
452
            }
453
            $this->caching = $template->caching;
454
            // flag for nocache sections
455
            $this->nocache = $nocache;
456
            $this->tag_nocache = false;
457
            // reset has nocache code flag
458
            $this->template->compiled->has_nocache_code = false;
459
            $this->has_variable_string = false;
460
            $this->prefix_code = array();
461
            // add file dependency
462
            if ($this->smarty->merge_compiled_includes || $this->template->source->handler->checkTimestamps()) {
463
                $this->parent_compiler->template->compiled->file_dependency[ $this->template->source->uid ] =
464
                    array(
465
                        $this->template->source->filepath,
466
                        $this->template->source->getTimeStamp(),
467
                        $this->template->source->type,
468
                    );
469
            }
470
            $this->smarty->_current_file = $this->template->source->filepath;
471
            // get template source
472
            if (!empty($this->template->source->components)) {
473
                // we have array of inheritance templates by extends: resource
474
                // generate corresponding source code sequence
475
                $_content =
476
                    Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template);
477
            } else {
478
                // get template source
479
                $_content = $this->template->source->getContent();
480
            }
481
            $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
482
            if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) {
483
                $_compiled_code = '<?php ' . $this->compileRequiredPlugins() . "?>\n" . $_compiled_code;
484
            }
485
        } catch (Exception $e) {
486
            if ($this->smarty->debugging) {
487
                $this->smarty->_debug->end_compile($this->template);
488
            }
489
            $this->_tag_stack = array();
490
            // free memory
491
            $this->parent_compiler = null;
492
            $this->template = null;
493
            $this->parser = null;
494
            throw $e;
495
        }
496
        if ($this->smarty->debugging) {
497
            $this->smarty->_debug->end_compile($this->template);
498
        }
499
        $this->parent_compiler = null;
500
        $this->parser = null;
501
        return $_compiled_code;
502
    }
503
504
    /**
505
     * Optionally process compiled code by post filter
506
     *
507
     * @param string $code compiled code
508
     *
509
     * @return string
510
     * @throws \SmartyException
511
     */
512
    public function postFilter($code)
513
    {
514
        // run post filter if on code
515
        if (!empty($code)
516
            && (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
517
        ) {
518
            return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
519
        } else {
520
            return $code;
521
        }
522
    }
523
524
    /**
525
     * Run optional prefilter
526
     *
527
     * @param string $_content template source
528
     *
529
     * @return string
530
     * @throws \SmartyException
531
     */
532
    public function preFilter($_content)
533
    {
534
        // run pre filter if required
535
        if ($_content !== ''
536
            && ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
537
        ) {
538
            return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
539
        } else {
540
            return $_content;
541
        }
542
    }
543
544
    /**
545
     * Compile Tag
546
     * This is a call back from the lexer/parser
547
     *
548
     * Save current prefix code
549
     * Compile tag
550
     * Merge tag prefix code with saved one
551
     * (required nested tags in attributes)
552
     *
553
     * @param string $tag       tag name
554
     * @param array  $args      array with tag attributes
555
     * @param array  $parameter array with compilation parameter
556
     *
557
     * @throws SmartyCompilerException
558
     * @throws SmartyException
559
     * @return string compiled code
560
     */
561
    public function compileTag($tag, $args, $parameter = array())
562
    {
563
        $this->prefixCodeStack[] = $this->prefix_code;
564
        $this->prefix_code = array();
565
        $result = $this->compileTag2($tag, $args, $parameter);
566
        $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
567
        return $result;
568
    }
569
570
    /**
571
     * compile variable
572
     *
573
     * @param string $variable
574
     *
575
     * @return string
576
     */
577
    public function compileVariable($variable)
578
    {
579
        if (!strpos($variable, '(')) {
580
            // not a variable variable
581
            $var = trim($variable, '\'');
582
            $this->tag_nocache = $this->tag_nocache |
0 ignored issues
show
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...
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
583
                                 $this->template->ext->getTemplateVars->_getVariable(
584
                                     $this->template,
585
                                     $var,
586
                                     null,
587
                                     true,
588
                                     false
589
                                 )->nocache;
590
            // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
591
        }
592
        return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
593
    }
594
595
    /**
596
     * compile config variable
597
     *
598
     * @param string $variable
599
     *
600
     * @return string
601
     */
602
    public function compileConfigVariable($variable)
603
    {
604
        // return '$_smarty_tpl->config_vars[' . $variable . ']';
605
        return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
606
    }
607
608
    /**
609
     * compile PHP function call
610
     *
611
     * @param string $name
612
     * @param array  $parameter
613
     *
614
     * @return string
615
     * @throws \SmartyCompilerException
616
     */
617
    public function compilePHPFunctionCall($name, $parameter)
618
    {
619
        if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
620
            if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
621
                || strcasecmp($name, 'array') === 0 || is_callable($name)
622
            ) {
623
                $func_name = strtolower($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
                        if (!$this->syntaxMatchesVariable($parameter[0]) && version_compare(PHP_VERSION, '5.5.0', '<')) {
653
                            return '(' . $parameter[ 0 ] . ' === false )';
654
                        } else {
655
                            return $func_name . '(' .
656
                                   str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')';
657
                        }
658
                    } else {
659
                        return $func_name . '(' . $parameter[ 0 ] . ')';
660
                    }
661
                } else {
662
                    return $name . '(' . implode(',', $parameter) . ')';
663
                }
664
            } else {
665
                $this->trigger_template_error("unknown function '{$name}'");
666
            }
667
        }
668
    }
669
670
	/**
671
	 * Determines whether the passed string represents a valid (PHP) variable.
672
	 * This is important, because `isset()` only works on variables and `empty()` can only be passed
673
	 * a variable prior to php5.5
674
	 * @param $string
675
	 * @return bool
676
	 */
677
	private function syntaxMatchesVariable($string) {
678
    	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]*|\[.*]*\])*$/';
679
    	return 1 === preg_match($regex_pattern, trim($string));
680
    }
681
682
    /**
683
     * This method is called from parser to process a text content section if strip is enabled
684
     * - remove text from inheritance child templates as they may generate output
685
     *
686
     * @param string $text
687
     *
688
     * @return string
689
     */
690
    public function processText($text)
691
    {
692
693
        if (strpos($text, '<') === false) {
694
        	return preg_replace($this->stripRegEx, '', $text);
695
        }
696
697
	    $store = array();
698
	    $_store = 0;
699
700
        // capture html elements not to be messed with
701
        $_offset = 0;
702
        if (preg_match_all(
703
            '#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
704
            $text,
705
            $matches,
706
            PREG_OFFSET_CAPTURE | PREG_SET_ORDER
707
        )
708
        ) {
709
            foreach ($matches as $match) {
710
                $store[] = $match[ 0 ][ 0 ];
711
                $_length = strlen($match[ 0 ][ 0 ]);
712
                $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
713
                $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
714
                $_offset += $_length - strlen($replace);
715
                $_store++;
716
            }
717
        }
718
        $expressions = array(// replace multiple spaces between tags by a single space
719
                             '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s'                            => '\1 \2',
720
                             // remove newline between tags
721
                             '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s'                     => '\1\2',
722
                             // remove multiple spaces between attributes (but not in attribute values!)
723
                             '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
724
                             '#>[\040\011]+$#Ss'                                                       => '> ',
725
                             '#>[\040\011]*[\n]\s*$#Ss'                                                => '>',
726
                             $this->stripRegEx                                                         => '',
727
        );
728
        $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
729
        $_offset = 0;
730
        if (preg_match_all(
731
            '#@!@SMARTY:([0-9]+):SMARTY@!@#is',
732
            $text,
733
            $matches,
734
            PREG_OFFSET_CAPTURE | PREG_SET_ORDER
735
        )
736
        ) {
737
            foreach ($matches as $match) {
738
                $_length = strlen($match[ 0 ][ 0 ]);
739
                $replace = $store[ $match[ 1 ][ 0 ] ];
740
                $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
741
                $_offset += strlen($replace) - $_length;
742
                $_store++;
743
            }
744
        }
745
        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...
746
    }
747
748
    /**
749
     * lazy loads internal compile plugin for tag and calls the compile method
750
     * compile objects cached for reuse.
751
     * class name format:  Smarty_Internal_Compile_TagName
752
     * plugin filename format: Smarty_Internal_TagName.php
753
     *
754
     * @param string $tag    tag name
755
     * @param array  $args   list of tag attributes
756
     * @param mixed  $param1 optional parameter
757
     * @param mixed  $param2 optional parameter
758
     * @param mixed  $param3 optional parameter
759
     *
760
     * @return bool|string compiled code or false
761
     * @throws \SmartyCompilerException
762
     */
763
    public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
764
    {
765
        /* @var Smarty_Internal_CompileBase $tagCompiler */
766
        $tagCompiler = $this->getTagCompiler($tag);
767
        // compile this tag
768
        return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3);
0 ignored issues
show
introduced by
The condition $tagCompiler === false is always false.
Loading history...
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

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