Subversion Repositories ALCASAR

Rev

Details | Last modification | View Log

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