Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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;
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 $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
                // we have array of inheritance templates by extends: resource
459
                // generate corresponding source code sequence
460
                $_content =
461
                    Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template);
462
            } else {
463
                // get template source
464
                $_content = $this->template->source->getContent();
465
            }
466
            $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
467
            if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) {
468
                $_compiled_code = '<?php ' . $this->compileRequiredPlugins() . "?>\n" . $_compiled_code;
469
            }
470
        } catch (Exception $e) {
471
            if ($this->smarty->debugging) {
472
                $this->smarty->_debug->end_compile($this->template);
473
            }
474
            $this->_tag_stack = array();
475
            // free memory
476
            $this->parent_compiler = null;
477
            $this->template = null;
478
            $this->parser = null;
479
            throw $e;
480
        }
481
        if ($this->smarty->debugging) {
482
            $this->smarty->_debug->end_compile($this->template);
483
        }
484
        $this->parent_compiler = null;
485
        $this->parser = null;
486
        return $_compiled_code;
487
    }
488
 
489
    /**
490
     * Optionally process compiled code by post filter
491
     *
492
     * @param string $code compiled code
493
     *
494
     * @return string
495
     * @throws \SmartyException
496
     */
497
    public function postFilter($code)
498
    {
499
        // run post filter if on code
500
        if (!empty($code)
501
            && (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
502
        ) {
503
            return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
504
        } else {
505
            return $code;
506
        }
507
    }
508
 
509
    /**
510
     * Run optional prefilter
511
     *
512
     * @param string $_content template source
513
     *
514
     * @return string
515
     * @throws \SmartyException
516
     */
517
    public function preFilter($_content)
518
    {
519
        // run pre filter if required
520
        if ($_content !== ''
521
            && ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
522
        ) {
523
            return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
524
        } else {
525
            return $_content;
526
        }
527
    }
528
 
529
    /**
530
     * Compile Tag
531
     * This is a call back from the lexer/parser
532
     *
533
     * Save current prefix code
534
     * Compile tag
535
     * Merge tag prefix code with saved one
536
     * (required nested tags in attributes)
537
     *
538
     * @param string $tag       tag name
539
     * @param array  $args      array with tag attributes
540
     * @param array  $parameter array with compilation parameter
541
     *
542
     * @throws SmartyCompilerException
543
     * @throws SmartyException
544
     * @return string compiled code
545
     */
546
    public function compileTag($tag, $args, $parameter = array())
547
    {
548
        $this->prefixCodeStack[] = $this->prefix_code;
549
        $this->prefix_code = array();
550
        $result = $this->compileTag2($tag, $args, $parameter);
551
        $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
552
        return $result;
553
    }
554
 
555
    /**
556
     * compile variable
557
     *
558
     * @param string $variable
559
     *
560
     * @return string
561
     */
562
    public function compileVariable($variable)
563
    {
564
        if (!strpos($variable, '(')) {
565
            // not a variable variable
566
            $var = trim($variable, '\'');
567
            $this->tag_nocache = $this->tag_nocache |
568
                                 $this->template->ext->getTemplateVars->_getVariable(
569
                                     $this->template,
570
                                     $var,
571
                                     null,
572
                                     true,
573
                                     false
574
                                 )->nocache;
575
            // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
576
        }
577
        return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
578
    }
579
 
580
    /**
581
     * compile config variable
582
     *
583
     * @param string $variable
584
     *
585
     * @return string
586
     */
587
    public function compileConfigVariable($variable)
588
    {
589
        // return '$_smarty_tpl->config_vars[' . $variable . ']';
590
        return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
591
    }
592
 
593
    /**
594
     * compile PHP function call
595
     *
596
     * @param string $name
597
     * @param array  $parameter
598
     *
599
     * @return string
600
     * @throws \SmartyCompilerException
601
     */
602
    public function compilePHPFunctionCall($name, $parameter)
603
    {
604
        if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
605
            if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
606
                || strcasecmp($name, 'array') === 0 || is_callable($name)
607
            ) {
608
                $func_name = smarty_strtolower_ascii($name);
609
 
610
                if ($func_name === 'isset') {
611
                    if (count($parameter) === 0) {
612
                        $this->trigger_template_error('Illegal number of parameter in "isset()"');
613
                    }
614
 
615
                    $pa = array();
616
                    foreach ($parameter as $p) {
617
                        $pa[] = $this->syntaxMatchesVariable($p) ? 'isset(' . $p . ')' : '(' . $p . ' !== null )';
618
                    }
619
                    return '(' . implode(' && ', $pa) . ')';
620
 
621
                } elseif (in_array(
622
                    $func_name,
623
                    array(
624
                        'empty',
625
                        'reset',
626
                        'current',
627
                        'end',
628
                        'prev',
629
                        'next'
630
                    )
631
                )
632
                ) {
633
                    if (count($parameter) !== 1) {
634
                        $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'");
635
                    }
636
                    if ($func_name === 'empty') {
637
                        return $func_name . '(' .
638
                               str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')';
639
                    } else {
640
                        return $func_name . '(' . $parameter[ 0 ] . ')';
641
                    }
642
                } else {
643
                    return $name . '(' . implode(',', $parameter) . ')';
644
                }
645
            } else {
646
                $this->trigger_template_error("unknown function '{$name}'");
647
            }
648
        }
649
    }
650
 
651
    /**
652
     * Determines whether the passed string represents a valid (PHP) variable.
653
     * This is important, because `isset()` only works on variables and `empty()` can only be passed
654
     * a variable prior to php5.5
655
     * @param $string
656
     * @return bool
657
     */
658
    private function syntaxMatchesVariable($string) {
659
        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]*|\[.*]*\])*$/';
660
        return 1 === preg_match($regex_pattern, trim($string));
661
    }
662
 
663
    /**
664
     * This method is called from parser to process a text content section if strip is enabled
665
     * - remove text from inheritance child templates as they may generate output
666
     *
667
     * @param string $text
668
     *
669
     * @return string
670
     */
671
    public function processText($text)
672
    {
673
 
674
        if (strpos($text, '<') === false) {
675
            return preg_replace($this->stripRegEx, '', $text);
676
        }
677
 
678
        $store = array();
679
        $_store = 0;
680
 
681
        // capture html elements not to be messed with
682
        $_offset = 0;
683
        if (preg_match_all(
684
            '#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
685
            $text,
686
            $matches,
687
            PREG_OFFSET_CAPTURE | PREG_SET_ORDER
688
        )
689
        ) {
690
            foreach ($matches as $match) {
691
                $store[] = $match[ 0 ][ 0 ];
692
                $_length = strlen($match[ 0 ][ 0 ]);
693
                $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
694
                $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
695
                $_offset += $_length - strlen($replace);
696
                $_store++;
697
            }
698
        }
699
        $expressions = array(// replace multiple spaces between tags by a single space
700
                             '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s'                            => '\1 \2',
701
                             // remove newline between tags
702
                             '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s'                     => '\1\2',
703
                             // remove multiple spaces between attributes (but not in attribute values!)
704
                             '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
705
                             '#>[\040\011]+$#Ss'                                                       => '> ',
706
                             '#>[\040\011]*[\n]\s*$#Ss'                                                => '>',
707
                             $this->stripRegEx                                                         => '',
708
        );
709
        $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
710
        $_offset = 0;
711
        if (preg_match_all(
712
            '#@!@SMARTY:([0-9]+):SMARTY@!@#is',
713
            $text,
714
            $matches,
715
            PREG_OFFSET_CAPTURE | PREG_SET_ORDER
716
        )
717
        ) {
718
            foreach ($matches as $match) {
719
                $_length = strlen($match[ 0 ][ 0 ]);
720
                $replace = $store[ $match[ 1 ][ 0 ] ];
721
                $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
722
                $_offset += strlen($replace) - $_length;
723
                $_store++;
724
            }
725
        }
726
        return $text;
727
    }
728
 
729
    /**
730
     * lazy loads internal compile plugin for tag and calls the compile method
731
     * compile objects cached for reuse.
732
     * class name format:  Smarty_Internal_Compile_TagName
733
     * plugin filename format: Smarty_Internal_TagName.php
734
     *
735
     * @param string $tag    tag name
736
     * @param array  $args   list of tag attributes
737
     * @param mixed  $param1 optional parameter
738
     * @param mixed  $param2 optional parameter
739
     * @param mixed  $param3 optional parameter
740
     *
741
     * @return bool|string compiled code or false
742
     * @throws \SmartyCompilerException
743
     */
744
    public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
745
    {
746
        /* @var Smarty_Internal_CompileBase $tagCompiler */
747
        $tagCompiler = $this->getTagCompiler($tag);
748
        // compile this tag
749
        return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3);
750
    }
751
 
752
    /**
753
     * lazy loads internal compile plugin for tag compile objects cached for reuse.
754
     *
755
     * class name format:  Smarty_Internal_Compile_TagName
756
     * plugin filename format: Smarty_Internal_TagName.php
757
     *
758
     * @param string $tag tag name
759
     *
760
     * @return bool|\Smarty_Internal_CompileBase tag compiler object or false if not found
761
     */
762
    public function getTagCompiler($tag)
763
    {
764
        // re-use object if already exists
765
        if (!isset(self::$_tag_objects[ $tag ])) {
766
            // lazy load internal compiler plugin
767
            $_tag = explode('_', $tag);
768
            $_tag = array_map('smarty_ucfirst_ascii', $_tag);
769
            $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
770
            if (class_exists($class_name)
771
                && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
772
            ) {
773
                self::$_tag_objects[ $tag ] = new $class_name;
774
            } else {
775
                self::$_tag_objects[ $tag ] = false;
776
            }
777
        }
778
        return self::$_tag_objects[ $tag ];
779
    }
780
 
781
    /**
782
     * Check for plugins and return function name
783
     *
784
     * @param        $plugin_name
785
     * @param string $plugin_type type of plugin
786
     *
787
     * @return string call name of function
788
     * @throws \SmartyException
789
     */
790
    public function getPlugin($plugin_name, $plugin_type)
791
    {
792
        $function = null;
793
        if ($this->caching && ($this->nocache || $this->tag_nocache)) {
794
            if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
795
                $function =
796
                    $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
797
            } elseif (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
798
                $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] =
799
                    $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ];
800
                $function =
801
                    $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
802
            }
803
        } else {
804
            if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
805
                $function =
806
                    $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
807
            } elseif (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
808
                $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] =
809
                    $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ];
810
                $function =
811
                    $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
812
            }
813
        }
814
        if (isset($function)) {
815
            if ($plugin_type === 'modifier') {
816
                $this->modifier_plugins[ $plugin_name ] = true;
817
            }
818
            return $function;
819
        }
820
        // loop through plugin dirs and find the plugin
821
        $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
822
        $file = $this->smarty->loadPlugin($function, false);
823
        if (is_string($file)) {
824
            if ($this->caching && ($this->nocache || $this->tag_nocache)) {
825
                $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
826
                    $file;
827
                $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
828
                    $function;
829
            } else {
830
                $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
831
                    $file;
832
                $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
833
                    $function;
834
            }
835
            if ($plugin_type === 'modifier') {
836
                $this->modifier_plugins[ $plugin_name ] = true;
837
            }
838
            return $function;
839
        }
840
        if (is_callable($function)) {
841
            // plugin function is defined in the script
842
            return $function;
843
        }
844
        return false;
845
    }
846
 
847
    /**
848
     * Check for plugins by default plugin handler
849
     *
850
     * @param string $tag         name of tag
851
     * @param string $plugin_type type of plugin
852
     *
853
     * @return bool true if found
854
     * @throws \SmartyCompilerException
855
     */
856
    public function getPluginFromDefaultHandler($tag, $plugin_type)
857
    {
858
        $callback = null;
859
        $script = null;
860
        $cacheable = true;
861
        $result = call_user_func_array(
862
            $this->smarty->default_plugin_handler_func,
863
            array(
864
                $tag,
865
                $plugin_type,
866
                $this->template,
867
                &$callback,
868
                &$script,
869
                &$cacheable,
870
            )
871
        );
872
        if ($result) {
873
            $this->tag_nocache = $this->tag_nocache || !$cacheable;
874
            if ($script !== null) {
875
                if (is_file($script)) {
876
                    if ($this->caching && ($this->nocache || $this->tag_nocache)) {
877
                        $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] =
878
                            $script;
879
                        $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] =
880
                            $callback;
881
                    } else {
882
                        $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] =
883
                            $script;
884
                        $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] =
885
                            $callback;
886
                    }
887
                    include_once $script;
888
                } else {
889
                    $this->trigger_template_error("Default plugin handler: Returned script file '{$script}' for '{$tag}' not found");
890
                }
891
            }
892
            if (is_callable($callback)) {
893
                $this->default_handler_plugins[ $plugin_type ][ $tag ] = array(
894
                    $callback,
895
                    true,
896
                    array()
897
                );
898
                return true;
899
            } else {
900
                $this->trigger_template_error("Default plugin handler: Returned callback for '{$tag}' not callable");
901
            }
902
        }
903
        return false;
904
    }
905
 
906
    /**
907
     * Append code segments and remove unneeded ?> <?php transitions
908
     *
909
     * @param string $left
910
     * @param string $right
911
     *
912
     * @return string
913
     */
914
    public function appendCode($left, $right)
915
    {
916
        if (preg_match('/\s*\?>\s?$/D', $left) && preg_match('/^<\?php\s+/', $right)) {
917
            $left = preg_replace('/\s*\?>\s?$/D', "\n", $left);
918
            $left .= preg_replace('/^<\?php\s+/', '', $right);
919
        } else {
920
            $left .= $right;
921
        }
922
        return $left;
923
    }
924
 
925
    /**
926
     * Inject inline code for nocache template sections
927
     * This method gets the content of each template element from the parser.
928
     * If the content is compiled code and it should be not cached the code is injected
929
     * into the rendered output.
930
     *
931
     * @param string  $content content of template element
932
     * @param boolean $is_code true if content is compiled code
933
     *
934
     * @return string  content
935
     */
936
    public function processNocacheCode($content, $is_code)
937
    {
938
        // If the template is not evaluated and we have a nocache section and or a nocache tag
939
        if ($is_code && !empty($content)) {
940
            // generate replacement code
941
            if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->caching
942
                && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
943
            ) {
944
                $this->template->compiled->has_nocache_code = true;
945
                $_output = addcslashes($content, '\'\\');
946
                $_output = str_replace('^#^', '\'', $_output);
947
                $_output =
948
                    "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/{$_output}/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
949
                // make sure we include modifier plugins for nocache code
950
                foreach ($this->modifier_plugins as $plugin_name => $dummy) {
951
                    if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) {
952
                        $this->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] =
953
                            $this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ];
954
                    }
955
                }
956
            } else {
957
                $_output = $content;
958
            }
959
        } else {
960
            $_output = $content;
961
        }
962
        $this->modifier_plugins = array();
963
        $this->suppressNocacheProcessing = false;
964
        $this->tag_nocache = false;
965
        return $_output;
966
    }
967
 
968
    /**
969
     * Get Id
970
     *
971
     * @param string $input
972
     *
973
     * @return bool|string
974
     */
975
    public function getId($input)
976
    {
977
        if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) {
978
            return $match[ 2 ];
979
        }
980
        return false;
981
    }
982
 
983
    /**
984
     * Get variable name from string
985
     *
986
     * @param string $input
987
     *
988
     * @return bool|string
989
     */
990
    public function getVariableName($input)
991
    {
992
        if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) {
993
            return $match[ 1 ];
994
        }
995
        return false;
996
    }
997
 
998
    /**
999
     * Set nocache flag in variable or create new variable
1000
     *
1001
     * @param string $varName
1002
     */
1003
    public function setNocacheInVariable($varName)
1004
    {
1005
        // create nocache var to make it know for further compiling
1006
        if ($_var = $this->getId($varName)) {
1007
            if (isset($this->template->tpl_vars[ $_var ])) {
1008
                $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ];
1009
                $this->template->tpl_vars[ $_var ]->nocache = true;
1010
            } else {
1011
                $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true);
1012
            }
1013
        }
1014
    }
1015
 
1016
    /**
1017
     * @param array $_attr tag attributes
1018
     * @param array $validScopes
1019
     *
1020
     * @return int|string
1021
     * @throws \SmartyCompilerException
1022
     */
1023
    public function convertScope($_attr, $validScopes)
1024
    {
1025
        $_scope = 0;
1026
        if (isset($_attr[ 'scope' ])) {
1027
            $_scopeName = trim($_attr[ 'scope' ], '\'"');
1028
            if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) {
1029
                $_scope = $_scopeName;
1030
            } elseif (is_string($_scopeName)) {
1031
                $_scopeName = trim($_scopeName, '\'"');
1032
                $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false;
1033
            } else {
1034
                $_scope = false;
1035
            }
1036
            if ($_scope === false) {
1037
                $err = var_export($_scopeName, true);
1038
                $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true);
1039
            }
1040
        }
1041
        return $_scope;
1042
    }
1043
 
1044
    /**
1045
     * Generate nocache code string
1046
     *
1047
     * @param string $code PHP code
1048
     *
1049
     * @return string
1050
     */
1051
    public function makeNocacheCode($code)
1052
    {
1053
        return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " .
1054
               str_replace('^#^', '\'', addcslashes($code, '\'\\')) .
1055
               "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
1056
    }
1057
 
1058
    /**
1059
     * display compiler error messages without dying
1060
     * If parameter $args is empty it is a parser detected syntax error.
1061
     * In this case the parser is called to obtain information about expected tokens.
1062
     * If parameter $args contains a string this is used as error message
1063
     *
1064
     * @param string    $args    individual error message or null
1065
     * @param string    $line    line-number
1066
     * @param null|bool $tagline if true the line number of last tag
1067
     *
1068
     * @throws \SmartyCompilerException when an unexpected token is found
1069
     */
1070
    public function trigger_template_error($args = null, $line = null, $tagline = null)
1071
    {
1072
        $lex = $this->parser->lex;
1073
        if ($tagline === true) {
1074
            // get line number of Tag
1075
            $line = $lex->taglineno;
1076
        } elseif (!isset($line)) {
1077
            // get template source line which has error
1078
            $line = $lex->line;
1079
        } else {
1080
            $line = (int)$line;
1081
        }
1082
        if (in_array(
1083
            $this->template->source->type,
1084
            array(
1085
                'eval',
1086
                'string'
1087
            )
1088
        )
1089
        ) {
1090
            $templateName = $this->template->source->type . ':' . trim(
1091
                    preg_replace(
1092
                        '![\t\r\n]+!',
1093
                        ' ',
1094
                        strlen($lex->data) > 40 ?
1095
                            substr($lex->data, 0, 40) .
1096
                            '...' : $lex->data
1097
                    )
1098
                );
1099
        } else {
1100
            $templateName = $this->template->source->type . ':' . $this->template->source->filepath;
1101
        }
1102
        //        $line += $this->trace_line_offset;
1103
        $match = preg_split("/\n/", $lex->data);
1104
        $error_text =
1105
            'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) .
1106
            '"  on line ' . ($line + $this->trace_line_offset) . ' "' .
1107
            trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" ';
1108
        if (isset($args)) {
1109
            // individual error message
1110
            $error_text .= $args;
1111
        } else {
1112
            $expect = array();
1113
            // expected token from parser
1114
            $error_text .= ' - Unexpected "' . $lex->value . '"';
1115
            if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
1116
                foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
1117
                    $exp_token = $this->parser->yyTokenName[ $token ];
1118
                    if (isset($lex->smarty_token_names[ $exp_token ])) {
1119
                        // token type from lexer
1120
                        $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"';
1121
                    } else {
1122
                        // otherwise internal token name
1123
                        $expect[] = $this->parser->yyTokenName[ $token ];
1124
                    }
1125
                }
1126
                $error_text .= ', expected one of: ' . implode(' , ', $expect);
1127
            }
1128
        }
1129
        if ($this->smarty->_parserdebug) {
1130
            $this->parser->errorRunDown();
1131
            echo ob_get_clean();
1132
            flush();
1133
        }
1134
        $e = new SmartyCompilerException(
1135
            $error_text,
1136
            0,
1137
            $this->template->source->filepath,
1138
            $line
1139
        );
1140
        $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
1141
        $e->desc = $args;
1142
        $e->template = $this->template->source->filepath;
1143
        throw $e;
1144
    }
1145
 
1146
    /**
1147
     * Return var_export() value with all white spaces removed
1148
     *
1149
     * @param mixed $value
1150
     *
1151
     * @return string
1152
     */
1153
    public function getVarExport($value)
1154
    {
1155
        return preg_replace('/\s/', '', var_export($value, true));
1156
    }
1157
 
1158
    /**
1159
     *  enter double quoted string
1160
     *  - save tag stack count
1161
     */
1162
    public function enterDoubleQuote()
1163
    {
1164
        array_push($this->_tag_stack_count, $this->getTagStackCount());
1165
    }
1166
 
1167
    /**
1168
     * Return tag stack count
1169
     *
1170
     * @return int
1171
     */
1172
    public function getTagStackCount()
1173
    {
1174
        return count($this->_tag_stack);
1175
    }
1176
 
1177
    /**
1178
     * @param $lexerPreg
1179
     *
1180
     * @return mixed
1181
     */
1182
    public function replaceDelimiter($lexerPreg)
1183
    {
1184
        return str_replace(
1185
            array('SMARTYldel', 'SMARTYliteral', 'SMARTYrdel', 'SMARTYautoliteral', 'SMARTYal'),
1186
            array(
1187
                $this->ldelPreg, $this->literalPreg, $this->rdelPreg,
1188
                $this->smarty->getAutoLiteral() ? '{1,}' : '{9}',
1189
                $this->smarty->getAutoLiteral() ? '' : '\\s*'
1190
            ),
1191
            $lexerPreg
1192
        );
1193
    }
1194
 
1195
    /**
1196
     * Build lexer regular expressions for left and right delimiter and user defined literals
1197
     */
1198
    public function initDelimiterPreg()
1199
    {
1200
        $ldel = $this->smarty->getLeftDelimiter();
1201
        $this->ldelLength = strlen($ldel);
1202
        $this->ldelPreg = '';
1203
        foreach (str_split($ldel, 1) as $chr) {
1204
            $this->ldelPreg .= '[' . preg_quote($chr,'/') . ']';
1205
        }
1206
        $rdel = $this->smarty->getRightDelimiter();
1207
        $this->rdelLength = strlen($rdel);
1208
        $this->rdelPreg = '';
1209
        foreach (str_split($rdel, 1) as $chr) {
1210
            $this->rdelPreg .= '[' . preg_quote($chr,'/') . ']';
1211
        }
1212
        $literals = $this->smarty->getLiterals();
1213
        if (!empty($literals)) {
1214
            foreach ($literals as $key => $literal) {
1215
                $literalPreg = '';
1216
                foreach (str_split($literal, 1) as $chr) {
1217
                    $literalPreg .= '[' . preg_quote($chr,'/') . ']';
1218
                }
1219
                $literals[ $key ] = $literalPreg;
1220
            }
1221
            $this->literalPreg = '|' . implode('|', $literals);
1222
        } else {
1223
            $this->literalPreg = '';
1224
        }
1225
    }
1226
 
1227
    /**
1228
     *  leave double quoted string
1229
     *  - throw exception if block in string was not closed
1230
     *
1231
     * @throws \SmartyCompilerException
1232
     */
1233
    public function leaveDoubleQuote()
1234
    {
1235
        if (array_pop($this->_tag_stack_count) !== $this->getTagStackCount()) {
1236
            $tag = $this->getOpenBlockTag();
1237
            $this->trigger_template_error(
1238
                "unclosed '{{$tag}}' in doubled quoted string",
1239
                null,
1240
                true
1241
            );
1242
        }
1243
    }
1244
 
1245
    /**
1246
     * Get left delimiter preg
1247
     *
1248
     * @return string
1249
     */
1250
    public function getLdelPreg()
1251
    {
1252
        return $this->ldelPreg;
1253
    }
1254
 
1255
    /**
1256
     * Get right delimiter preg
1257
     *
1258
     * @return string
1259
     */
1260
    public function getRdelPreg()
1261
    {
1262
        return $this->rdelPreg;
1263
    }
1264
 
1265
    /**
1266
     * Get length of left delimiter
1267
     *
1268
     * @return int
1269
     */
1270
    public function getLdelLength()
1271
    {
1272
        return $this->ldelLength;
1273
    }
1274
 
1275
    /**
1276
     * Get length of right delimiter
1277
     *
1278
     * @return int
1279
     */
1280
    public function getRdelLength()
1281
    {
1282
        return $this->rdelLength;
1283
    }
1284
 
1285
    /**
1286
     * Get name of current open block tag
1287
     *
1288
     * @return string|boolean
1289
     */
1290
    public function getOpenBlockTag()
1291
    {
1292
        $tagCount = $this->getTagStackCount();
1293
        if ($tagCount) {
1294
            return $this->_tag_stack[ $tagCount - 1 ][ 0 ];
1295
        } else {
1296
            return false;
1297
        }
1298
    }
1299
 
1300
    /**
1301
     * Check if $value contains variable elements
1302
     *
1303
     * @param mixed $value
1304
     *
1305
     * @return bool|int
1306
     */
1307
    public function isVariable($value)
1308
    {
1309
        if (is_string($value)) {
1310
            return preg_match('/[$(]/', $value);
1311
        }
1312
        if (is_bool($value) || is_numeric($value)) {
1313
            return false;
1314
        }
1315
        if (is_array($value)) {
1316
            foreach ($value as $k => $v) {
1317
                if ($this->isVariable($k) || $this->isVariable($v)) {
1318
                    return true;
1319
                }
1320
            }
1321
            return false;
1322
        }
1323
        return false;
1324
    }
1325
 
1326
    /**
1327
     * Get new prefix variable name
1328
     *
1329
     * @return string
1330
     */
1331
    public function getNewPrefixVariable()
1332
    {
1333
        ++self::$prefixVariableNumber;
1334
        return $this->getPrefixVariable();
1335
    }
1336
 
1337
    /**
1338
     * Get current prefix variable name
1339
     *
1340
     * @return string
1341
     */
1342
    public function getPrefixVariable()
1343
    {
1344
        return '$_prefixVariable' . self::$prefixVariableNumber;
1345
    }
1346
 
1347
    /**
1348
     * append  code to prefix buffer
1349
     *
1350
     * @param string $code
1351
     */
1352
    public function appendPrefixCode($code)
1353
    {
1354
        $this->prefix_code[] = $code;
1355
    }
1356
 
1357
    /**
1358
     * get prefix code string
1359
     *
1360
     * @return string
1361
     */
1362
    public function getPrefixCode()
1363
    {
1364
        $code = '';
1365
        $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
1366
        $this->prefixCodeStack[] = array();
1367
        foreach ($prefixArray as $c) {
1368
            $code = $this->appendCode($code, $c);
1369
        }
1370
        $this->prefix_code = array();
1371
        return $code;
1372
    }
1373
 
1374
    /**
1375
     * Save current required plugins
1376
     *
1377
     * @param bool $init if true init required plugins
1378
     */
1379
    public function saveRequiredPlugins($init = false)
1380
    {
1381
        $this->required_plugins_stack[] = $this->required_plugins;
1382
        if ($init) {
1383
            $this->required_plugins = array('compiled' => array(), 'nocache' => array());
1384
        }
1385
    }
1386
 
1387
    /**
1388
     * Restore required plugins
1389
     */
1390
    public function restoreRequiredPlugins()
1391
    {
1392
        $this->required_plugins = array_pop($this->required_plugins_stack);
1393
    }
1394
 
1395
    /**
1396
     * Compile code to call Smarty_Internal_Template::_checkPlugins()
1397
     * for required plugins
1398
     *
1399
     * @return string
1400
     */
1401
    public function compileRequiredPlugins()
1402
    {
1403
        $code = $this->compileCheckPlugins($this->required_plugins[ 'compiled' ]);
1404
        if ($this->caching && !empty($this->required_plugins[ 'nocache' ])) {
1405
            $code .= $this->makeNocacheCode($this->compileCheckPlugins($this->required_plugins[ 'nocache' ]));
1406
        }
1407
        return $code;
1408
    }
1409
 
1410
    /**
1411
     * Compile code to call Smarty_Internal_Template::_checkPlugins
1412
     *   - checks if plugin is callable require otherwise
1413
     *
1414
     * @param $requiredPlugins
1415
     *
1416
     * @return string
1417
     */
1418
    public function compileCheckPlugins($requiredPlugins)
1419
    {
1420
        if (!empty($requiredPlugins)) {
1421
            $plugins = array();
1422
            foreach ($requiredPlugins as $plugin) {
1423
                foreach ($plugin as $data) {
1424
                    $plugins[] = $data;
1425
                }
1426
            }
1427
            return '$_smarty_tpl->_checkPlugins(' . $this->getVarExport($plugins) . ');' . "\n";
1428
        } else {
1429
            return '';
1430
        }
1431
    }
1432
 
1433
    /**
1434
     * method to compile a Smarty template
1435
     *
1436
     * @param mixed $_content template source
1437
     * @param bool  $isTemplateSource
1438
     *
1439
     * @return bool true if compiling succeeded, false if it failed
1440
     */
1441
    abstract protected function doCompile($_content, $isTemplateSource = false);
1442
 
1443
    public function cStyleComment($string) {
1444
        return '/*' . str_replace('*/', '* /' , $string) . '*/';
1445
    }
1446
 
1447
    /**
1448
     * Compile Tag
1449
     *
1450
     * @param string $tag       tag name
1451
     * @param array  $args      array with tag attributes
1452
     * @param array  $parameter array with compilation parameter
1453
     *
1454
     * @throws SmartyCompilerException
1455
     * @throws SmartyException
1456
     * @return string compiled code
1457
     */
1458
    private function compileTag2($tag, $args, $parameter)
1459
    {
1460
        $plugin_type = '';
1461
        // $args contains the attributes parsed and compiled by the lexer/parser
1462
        // assume that tag does compile into code, but creates no HTML output
1463
        $this->has_code = true;
1464
        // log tag/attributes
1465
        if (isset($this->smarty->_cache[ 'get_used_tags' ])) {
1466
            $this->template->_cache[ 'used_tags' ][] = array(
1467
                $tag,
1468
                $args
1469
            );
1470
        }
1471
        // check nocache option flag
1472
        foreach ($args as $arg) {
1473
            if (!is_array($arg)) {
1474
                if ($arg === "'nocache'" || $arg === 'nocache') {
1475
                    $this->tag_nocache = true;
1476
                }
1477
            } else {
1478
                foreach ($arg as $k => $v) {
1479
                    if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) {
1480
                        $this->tag_nocache = true;
1481
                    }
1482
                }
1483
            }
1484
        }
1485
        // compile the smarty tag (required compile classes to compile the tag are auto loaded)
1486
        if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
1487
            if (isset($this->parent_compiler->tpl_function[ $tag ])
1488
                || (isset($this->template->smarty->ext->_tplFunction)
1489
                    && $this->template->smarty->ext->_tplFunction->getTplFunction($this->template, $tag) !== false)
1490
            ) {
1491
                // template defined by {template} tag
1492
                $args[ '_attr' ][ 'name' ] = "'{$tag}'";
1493
                $_output = $this->callTagCompiler('call', $args, $parameter);
1494
            }
1495
        }
1496
        if ($_output !== false) {
1497
            if ($_output !== true) {
1498
                // did we get compiled code
1499
                if ($this->has_code) {
1500
                    // return compiled code
1501
                    return $_output;
1502
                }
1503
            }
1504
            // tag did not produce compiled code
1505
            return null;
1506
        } else {
1507
            // map_named attributes
1508
            if (isset($args[ '_attr' ])) {
1509
                foreach ($args[ '_attr' ] as $key => $attribute) {
1510
                    if (is_array($attribute)) {
1511
                        $args = array_merge($args, $attribute);
1512
                    }
1513
                }
1514
            }
1515
            // not an internal compiler tag
1516
            if (strlen($tag) < 6 || substr($tag, -5) !== 'close') {
1517
                // check if tag is a registered object
1518
                if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) {
1519
                    $method = $parameter[ 'object_method' ];
1520
                    if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])
1521
                        && (empty($this->smarty->registered_objects[ $tag ][ 1 ])
1522
                            || in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ]))
1523
                    ) {
1524
                        return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
1525
                    } elseif (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) {
1526
                        return $this->callTagCompiler(
1527
                            'private_object_block_function',
1528
                            $args,
1529
                            $parameter,
1530
                            $tag,
1531
                            $method
1532
                        );
1533
                    } else {
1534
                        // throw exception
1535
                        $this->trigger_template_error(
1536
                            'not allowed method "' . $method . '" in registered object "' .
1537
                            $tag . '"',
1538
                            null,
1539
                            true
1540
                        );
1541
                    }
1542
                }
1543
                // check if tag is registered
1544
                foreach (array(
1545
                    Smarty::PLUGIN_COMPILER,
1546
                    Smarty::PLUGIN_FUNCTION,
1547
                    Smarty::PLUGIN_BLOCK,
1548
                ) as $plugin_type) {
1549
                    if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) {
1550
                        // if compiler function plugin call it now
1551
                        if ($plugin_type === Smarty::PLUGIN_COMPILER) {
1552
                            $new_args = array();
1553
                            foreach ($args as $key => $mixed) {
1554
                                if (is_array($mixed)) {
1555
                                    $new_args = array_merge($new_args, $mixed);
1556
                                } else {
1557
                                    $new_args[ $key ] = $mixed;
1558
                                }
1559
                            }
1560
                            if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) {
1561
                                $this->tag_nocache = true;
1562
                            }
1563
                            return call_user_func_array(
1564
                                $this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ],
1565
                                array(
1566
                                    $new_args,
1567
                                    $this
1568
                                )
1569
                            );
1570
                        }
1571
                        // compile registered function or block function
1572
                        if ($plugin_type === Smarty::PLUGIN_FUNCTION || $plugin_type === Smarty::PLUGIN_BLOCK) {
1573
                            return $this->callTagCompiler(
1574
                                'private_registered_' . $plugin_type,
1575
                                $args,
1576
                                $parameter,
1577
                                $tag
1578
                            );
1579
                        }
1580
                    }
1581
                }
1582
                // check plugins from plugins folder
1583
                foreach ($this->plugin_search_order as $plugin_type) {
1584
                    if ($plugin_type === Smarty::PLUGIN_COMPILER
1585
                        && $this->smarty->loadPlugin('smarty_compiler_' . $tag)
1586
                        && (!isset($this->smarty->security_policy)
1587
                            || $this->smarty->security_policy->isTrustedTag($tag, $this))
1588
                    ) {
1589
                        $plugin = 'smarty_compiler_' . $tag;
1590
                        if (is_callable($plugin)) {
1591
                            // convert arguments format for old compiler plugins
1592
                            $new_args = array();
1593
                            foreach ($args as $key => $mixed) {
1594
                                if (is_array($mixed)) {
1595
                                    $new_args = array_merge($new_args, $mixed);
1596
                                } else {
1597
                                    $new_args[ $key ] = $mixed;
1598
                                }
1599
                            }
1600
                            return $plugin($new_args, $this->smarty);
1601
                        }
1602
                        if (class_exists($plugin, false)) {
1603
                            $plugin_object = new $plugin;
1604
                            if (method_exists($plugin_object, 'compile')) {
1605
                                return $plugin_object->compile($args, $this);
1606
                            }
1607
                        }
1608
                        throw new SmartyException("Plugin '{$tag}' not callable");
1609
                    } else {
1610
                        if ($function = $this->getPlugin($tag, $plugin_type)) {
1611
                            if (!isset($this->smarty->security_policy)
1612
                                || $this->smarty->security_policy->isTrustedTag($tag, $this)
1613
                            ) {
1614
                                return $this->callTagCompiler(
1615
                                    'private_' . $plugin_type . '_plugin',
1616
                                    $args,
1617
                                    $parameter,
1618
                                    $tag,
1619
                                    $function
1620
                                );
1621
                            }
1622
                        }
1623
                    }
1624
                }
1625
                if (is_callable($this->smarty->default_plugin_handler_func)) {
1626
                    $found = false;
1627
                    // look for already resolved tags
1628
                    foreach ($this->plugin_search_order as $plugin_type) {
1629
                        if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) {
1630
                            $found = true;
1631
                            break;
1632
                        }
1633
                    }
1634
                    if (!$found) {
1635
                        // call default handler
1636
                        foreach ($this->plugin_search_order as $plugin_type) {
1637
                            if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
1638
                                $found = true;
1639
                                break;
1640
                            }
1641
                        }
1642
                    }
1643
                    if ($found) {
1644
                        // if compiler function plugin call it now
1645
                        if ($plugin_type === Smarty::PLUGIN_COMPILER) {
1646
                            $new_args = array();
1647
                            foreach ($args as $key => $mixed) {
1648
                                if (is_array($mixed)) {
1649
                                    $new_args = array_merge($new_args, $mixed);
1650
                                } else {
1651
                                    $new_args[ $key ] = $mixed;
1652
                                }
1653
                            }
1654
                            return call_user_func_array(
1655
                                $this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ],
1656
                                array(
1657
                                    $new_args,
1658
                                    $this
1659
                                )
1660
                            );
1661
                        } else {
1662
                            return $this->callTagCompiler(
1663
                                'private_registered_' . $plugin_type,
1664
                                $args,
1665
                                $parameter,
1666
                                $tag
1667
                            );
1668
                        }
1669
                    }
1670
                }
1671
            } else {
1672
                // compile closing tag of block function
1673
                $base_tag = substr($tag, 0, -5);
1674
                // check if closing tag is a registered object
1675
                if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) {
1676
                    $method = $parameter[ 'object_method' ];
1677
                    if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) {
1678
                        return $this->callTagCompiler(
1679
                            'private_object_block_function',
1680
                            $args,
1681
                            $parameter,
1682
                            $tag,
1683
                            $method
1684
                        );
1685
                    } else {
1686
                        // throw exception
1687
                        $this->trigger_template_error(
1688
                            'not allowed closing tag method "' . $method .
1689
                            '" in registered object "' . $base_tag . '"',
1690
                            null,
1691
                            true
1692
                        );
1693
                    }
1694
                }
1695
                // registered block tag ?
1696
                if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
1697
                    || isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
1698
                ) {
1699
                    return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
1700
                }
1701
                // registered function tag ?
1702
                if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) {
1703
                    return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag);
1704
                }
1705
                // block plugin?
1706
                if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
1707
                    return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
1708
                }
1709
                // function plugin?
1710
                if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) {
1711
                    if (!isset($this->smarty->security_policy)
1712
                        || $this->smarty->security_policy->isTrustedTag($tag, $this)
1713
                    ) {
1714
                        return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function);
1715
                    }
1716
                }
1717
                // registered compiler plugin ?
1718
                if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) {
1719
                    // if compiler function plugin call it now
1720
                    $args = array();
1721
                    if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) {
1722
                        $this->tag_nocache = true;
1723
                    }
1724
                    return call_user_func_array(
1725
                        $this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ],
1726
                        array(
1727
                            $args,
1728
                            $this
1729
                        )
1730
                    );
1731
                }
1732
                if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
1733
                    $plugin = 'smarty_compiler_' . $tag;
1734
                    if (is_callable($plugin)) {
1735
                        return $plugin($args, $this->smarty);
1736
                    }
1737
                    if (class_exists($plugin, false)) {
1738
                        $plugin_object = new $plugin;
1739
                        if (method_exists($plugin_object, 'compile')) {
1740
                            return $plugin_object->compile($args, $this);
1741
                        }
1742
                    }
1743
                    throw new SmartyException("Plugin '{$tag}' not callable");
1744
                }
1745
            }
1746
            $this->trigger_template_error("unknown tag '{$tag}'", null, true);
1747
        }
1748
    }
1749
}