Хранилища Subversion ant

Редакция

Редакция 2 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS

Редакция Автор № строки Строка
2 alex-w 1
<?php
2
 
3
/**
4
 * Project:     Smarty: the PHP compiling template engine
5
 * File:        Smarty_Compiler.class.php
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 *
21
 * @link http://www.smarty.net/
22
 * @author Monte Ohrt <monte at ohrt dot com>
23
 * @author Andrei Zmievski <andrei@php.net>
24
 * @version 2.6.22
25
 * @copyright 2001-2005 New Digital Group, Inc.
26
 * @package Smarty
27
 */
28
 
29
/* $Id: Smarty_Compiler.class.php 2966 2008-12-08 15:10:03Z monte.ohrt $ */
30
 
31
/**
32
 * Template compiling class
33
 * @package Smarty
34
 */
35
class Smarty_Compiler extends Smarty {
36
 
37
    // internal vars
38
    /**#@+
39
     * @access private
40
     */
41
    var $_folded_blocks         =   array();    // keeps folded template blocks
42
    var $_current_file          =   null;       // the current template being compiled
43
    var $_current_line_no       =   1;          // line number for error messages
44
    var $_capture_stack         =   array();    // keeps track of nested capture buffers
45
    var $_plugin_info           =   array();    // keeps track of plugins to load
46
    var $_init_smarty_vars      =   false;
47
    var $_permitted_tokens      =   array('true','false','yes','no','on','off','null');
48
    var $_db_qstr_regexp        =   null;        // regexps are setup in the constructor
49
    var $_si_qstr_regexp        =   null;
50
    var $_qstr_regexp           =   null;
51
    var $_func_regexp           =   null;
52
    var $_reg_obj_regexp        =   null;
53
    var $_var_bracket_regexp    =   null;
54
    var $_num_const_regexp      =   null;
55
    var $_dvar_guts_regexp      =   null;
56
    var $_dvar_regexp           =   null;
57
    var $_cvar_regexp           =   null;
58
    var $_svar_regexp           =   null;
59
    var $_avar_regexp           =   null;
60
    var $_mod_regexp            =   null;
61
    var $_var_regexp            =   null;
62
    var $_parenth_param_regexp  =   null;
63
    var $_func_call_regexp      =   null;
64
    var $_obj_ext_regexp        =   null;
65
    var $_obj_start_regexp      =   null;
66
    var $_obj_params_regexp     =   null;
67
    var $_obj_call_regexp       =   null;
68
    var $_cacheable_state       =   0;
69
    var $_cache_attrs_count     =   0;
70
    var $_nocache_count         =   0;
71
    var $_cache_serial          =   null;
72
    var $_cache_include         =   null;
73
 
74
    var $_strip_depth           =   0;
75
    var $_additional_newline    =   "\n";
76
 
77
    var $_phpversion            =   0;
78
 
79
 
80
    /**#@-*/
81
    /**
82
     * The class constructor.
83
     */
84
    function Smarty_Compiler()
85
    {
86
        $this->_phpversion = substr(phpversion(),0,1);
87
 
88
        // matches double quoted strings:
89
        // "foobar"
90
        // "foo\"bar"
91
        $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
92
 
93
        // matches single quoted strings:
94
        // 'foobar'
95
        // 'foo\'bar'
96
        $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
97
 
98
        // matches single or double quoted strings
99
        $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
100
 
101
        // matches bracket portion of vars
102
        // [0]
103
        // [foo]
104
        // [$bar]
105
        $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
106
 
107
        // matches numerical constants
108
        // 30
109
        // -12
110
        // 13.22
111
        $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
112
 
113
        // matches $ vars (not objects):
114
        // $foo
115
        // $foo.bar
116
        // $foo.bar.foobar
117
        // $foo[0]
118
        // $foo[$bar]
119
        // $foo[5][blah]
120
        // $foo[5].bar[$foobar][4]
121
        $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
122
        $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
123
        $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
124
                . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
125
        $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
126
 
127
        // matches config vars:
128
        // #foo#
129
        // #foobar123_foo#
130
        $this->_cvar_regexp = '\#\w+\#';
131
 
132
        // matches section vars:
133
        // %foo.bar%
134
        $this->_svar_regexp = '\%\w+\.\w+\%';
135
 
136
        // matches all valid variables (no quotes, no modifiers)
137
        $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
138
           . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
139
 
140
        // matches valid variable syntax:
141
        // $foo
142
        // $foo
143
        // #foo#
144
        // #foo#
145
        // "text"
146
        // "text"
147
        $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
148
 
149
        // matches valid object call (one level of object nesting allowed in parameters):
150
        // $foo->bar
151
        // $foo->bar()
152
        // $foo->bar("text")
153
        // $foo->bar($foo, $bar, "text")
154
        // $foo->bar($foo, "foo")
155
        // $foo->bar->foo()
156
        // $foo->bar->foo->bar()
157
        // $foo->bar($foo->bar)
158
        // $foo->bar($foo->bar())
159
        // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
160
        // $foo->getBar()->getFoo()
161
        // $foo->getBar()->foo
162
        $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
163
        $this->_obj_restricted_param_regexp = '(?:'
164
             . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
165
             . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
166
 
167
       $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
168
                . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
169
 
170
       $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
171
                . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
172
       $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
173
       $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
174
 
175
        // matches valid modifier syntax:
176
        // |foo
177
        // |@foo
178
        // |foo:"bar"
179
        // |foo:$bar
180
        // |foo:"bar":$foobar
181
        // |foo|bar
182
        // |foo:$foo->bar
183
        $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
184
           . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
185
 
186
        // matches valid function name:
187
        // foo123
188
        // _foo_bar
189
        $this->_func_regexp = '[a-zA-Z_]\w*';
190
 
191
        // matches valid registered object:
192
        // foo->bar
193
        $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
194
 
195
        // matches valid parameter values:
196
        // true
197
        // $foo
198
        // $foo|bar
199
        // #foo#
200
        // #foo#|bar
201
        // "text"
202
        // "text"|bar
203
        // $foo->bar
204
        $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
205
           . $this->_var_regexp . '|' . $this->_num_const_regexp  . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
206
 
207
        // matches valid parenthesised function parameters:
208
        //
209
        // "text"
210
        //    $foo, $bar, "text"
211
        // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
212
        $this->_parenth_param_regexp = '(?:\((?:\w+|'
213
                . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
214
                . $this->_param_regexp . ')))*)?\))';
215
 
216
        // matches valid function call:
217
        // foo()
218
        // foo_bar($foo)
219
        // _foo_bar($foo,"bar")
220
        // foo123($foo,$foo->bar(),"foo")
221
        $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
222
           . $this->_parenth_param_regexp . '))';
223
    }
224
 
225
    /**
226
     * compile a resource
227
     *
228
     * sets $compiled_content to the compiled source
229
     * @param string $resource_name
230
     * @param string $source_content
231
     * @param string $compiled_content
232
     * @return true
233
     */
234
    function _compile_file($resource_name, $source_content, &$compiled_content)
235
    {
236
 
237
        if ($this->security) {
238
            // do not allow php syntax to be executed unless specified
239
            if ($this->php_handling == SMARTY_PHP_ALLOW &&
240
                !$this->security_settings['PHP_HANDLING']) {
241
                $this->php_handling = SMARTY_PHP_PASSTHRU;
242
            }
243
        }
244
 
245
        $this->_load_filters();
246
 
247
        $this->_current_file = $resource_name;
248
        $this->_current_line_no = 1;
249
        $ldq = preg_quote($this->left_delimiter, '~');
250
        $rdq = preg_quote($this->right_delimiter, '~');
251
 
252
        // run template source through prefilter functions
253
        if (count($this->_plugins['prefilter']) > 0) {
254
            foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
255
                if ($prefilter === false) continue;
256
                if ($prefilter[3] || is_callable($prefilter[0])) {
257
                    $source_content = call_user_func_array($prefilter[0],
258
                                                            array($source_content, &$this));
259
                    $this->_plugins['prefilter'][$filter_name][3] = true;
260
                } else {
261
                    $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
262
                }
263
            }
264
        }
265
 
266
        /* fetch all special blocks */
267
        $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
268
 
269
        preg_match_all($search, $source_content, $match,  PREG_SET_ORDER);
270
        $this->_folded_blocks = $match;
271
        reset($this->_folded_blocks);
272
 
273
        /* replace special blocks by "{php}" */
274
        $source_content = preg_replace($search.'e', "'"
275
                                       . $this->_quote_replace($this->left_delimiter) . 'php'
276
                                       . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
277
                                       . $this->_quote_replace($this->right_delimiter)
278
                                       . "'"
279
                                       , $source_content);
280
 
281
        /* Gather all template tags. */
282
        preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
283
        $template_tags = $_match[1];
284
        /* Split content by template tags to obtain non-template content. */
285
        $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
286
 
287
        /* loop through text blocks */
288
        for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
289
            /* match anything resembling php tags */
290
            if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?\s*php\s*[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
291
                /* replace tags with placeholders to prevent recursive replacements */
292
                $sp_match[1] = array_unique($sp_match[1]);
293
                usort($sp_match[1], '_smarty_sort_length');
294
                for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
295
                    $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
296
                }
297
                /* process each one */
298
                for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
299
                    if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
300
                        /* echo php contents */
301
                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
302
                    } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
303
                        /* quote php tags */
304
                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
305
                    } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
306
                        /* remove php tags */
307
                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
308
                    } else {
309
                        /* SMARTY_PHP_ALLOW, but echo non php starting tags */
310
                        $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
311
                        $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
312
                    }
313
                }
314
            }
315
        }
316
 
317
        /* Compile the template tags into PHP code. */
318
        $compiled_tags = array();
319
        for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
320
            $this->_current_line_no += substr_count($text_blocks[$i], "\n");
321
            $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
322
            $this->_current_line_no += substr_count($template_tags[$i], "\n");
323
        }
324
        if (count($this->_tag_stack)>0) {
325
            list($_open_tag, $_line_no) = end($this->_tag_stack);
326
            $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
327
            return;
328
        }
329
 
330
        /* Reformat $text_blocks between 'strip' and '/strip' tags,
331
           removing spaces, tabs and newlines. */
332
        $strip = false;
333
        for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
334
            if ($compiled_tags[$i] == '{strip}') {
335
                $compiled_tags[$i] = '';
336
                $strip = true;
337
                /* remove leading whitespaces */
338
                $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
339
            }
340
            if ($strip) {
341
                /* strip all $text_blocks before the next '/strip' */
342
                for ($j = $i + 1; $j < $for_max; $j++) {
343
                    /* remove leading and trailing whitespaces of each line */
344
                    $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
345
                    if ($compiled_tags[$j] == '{/strip}') {                      
346
                        /* remove trailing whitespaces from the last text_block */
347
                        $text_blocks[$j] = rtrim($text_blocks[$j]);
348
                    }
349
                    $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
350
                    if ($compiled_tags[$j] == '{/strip}') {
351
                        $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
352
                                    if a newline is following the closing strip-tag */
353
                        $strip = false;
354
                        $i = $j;
355
                        break;
356
                    }
357
                }
358
            }
359
        }
360
        $compiled_content = '';
361
 
362
        $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
363
 
364
        /* Interleave the compiled contents and text blocks to get the final result. */
365
        for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
366
            if ($compiled_tags[$i] == '') {
367
                // tag result empty, remove first newline from following text block
368
                $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
369
            }
370
            // replace legit PHP tags with placeholder
371
            $text_blocks[$i] = str_replace('<?', $tag_guard, $text_blocks[$i]);
372
            $compiled_tags[$i] = str_replace('<?', $tag_guard, $compiled_tags[$i]);
373
 
374
            $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
375
        }
376
        $compiled_content .= str_replace('<?', $tag_guard, $text_blocks[$i]);
377
 
378
        // escape php tags created by interleaving
379
        $compiled_content = str_replace('<?', "<?php echo '<?' ?>\n", $compiled_content);
380
        $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\n", $compiled_content);
381
 
382
        // recover legit tags
383
        $compiled_content = str_replace($tag_guard, '<?', $compiled_content);
384
 
385
        // remove \n from the end of the file, if any
386
        if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
387
            $compiled_content = substr($compiled_content, 0, -1);
388
        }
389
 
390
        if (!empty($this->_cache_serial)) {
391
            $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
392
        }
393
 
394
        // run compiled template through postfilter functions
395
        if (count($this->_plugins['postfilter']) > 0) {
396
            foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
397
                if ($postfilter === false) continue;
398
                if ($postfilter[3] || is_callable($postfilter[0])) {
399
                    $compiled_content = call_user_func_array($postfilter[0],
400
                                                              array($compiled_content, &$this));
401
                    $this->_plugins['postfilter'][$filter_name][3] = true;
402
                } else {
403
                    $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
404
                }
405
            }
406
        }
407
 
408
        // put header at the top of the compiled template
409
        $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
410
        $template_header .= "         compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
411
 
412
        /* Emit code to load needed plugins. */
413
        $this->_plugins_code = '';
414
        if (count($this->_plugin_info)) {
415
            $_plugins_params = "array('plugins' => array(";
416
            foreach ($this->_plugin_info as $plugin_type => $plugins) {
417
                foreach ($plugins as $plugin_name => $plugin_info) {
418
                    $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
419
                    $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
420
                }
421
            }
422
            $_plugins_params .= '))';
423
            $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
424
            $template_header .= $plugins_code;
425
            $this->_plugin_info = array();
426
            $this->_plugins_code = $plugins_code;
427
        }
428
 
429
        if ($this->_init_smarty_vars) {
430
            $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
431
            $this->_init_smarty_vars = false;
432
        }
433
 
434
        $compiled_content = $template_header . $compiled_content;
435
        return true;
436
    }
437
 
438
    /**
439
     * Compile a template tag
440
     *
441
     * @param string $template_tag
442
     * @return string
443
     */
444
    function _compile_tag($template_tag)
445
    {
446
        /* Matched comment. */
447
        if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
448
            return '';
449
 
450
        /* Split tag into two three parts: command, command modifiers and the arguments. */
451
        if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
452
                . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
453
                      (?:\s+(.*))?$
454
                    ~xs', $template_tag, $match)) {
455
            $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
456
        }
457
 
458
        $tag_command = $match[1];
459
        $tag_modifier = isset($match[2]) ? $match[2] : null;
460
        $tag_args = isset($match[3]) ? $match[3] : null;
461
 
462
        if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
463
            /* tag name is a variable or object */
464
            $_return = $this->_parse_var_props($tag_command . $tag_modifier);
465
            return "<?php echo $_return; ?>" . $this->_additional_newline;
466
        }
467
 
468
        /* If the tag name is a registered object, we process it. */
469
        if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
470
            return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
471
        }
472
 
473
        switch ($tag_command) {
474
            case 'include':
475
                return $this->_compile_include_tag($tag_args);
476
 
477
            case 'include_php':
478
                return $this->_compile_include_php_tag($tag_args);
479
 
480
            case 'if':
481
                $this->_push_tag('if');
482
                return $this->_compile_if_tag($tag_args);
483
 
484
            case 'else':
485
                list($_open_tag) = end($this->_tag_stack);
486
                if ($_open_tag != 'if' && $_open_tag != 'elseif')
487
                    $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
488
                else
489
                    $this->_push_tag('else');
490
                return '<?php else: ?>';
491
 
492
            case 'elseif':
493
                list($_open_tag) = end($this->_tag_stack);
494
                if ($_open_tag != 'if' && $_open_tag != 'elseif')
495
                    $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
496
                if ($_open_tag == 'if')
497
                    $this->_push_tag('elseif');
498
                return $this->_compile_if_tag($tag_args, true);
499
 
500
            case '/if':
501
                $this->_pop_tag('if');
502
                return '<?php endif; ?>';
503
 
504
            case 'capture':
505
                return $this->_compile_capture_tag(true, $tag_args);
506
 
507
            case '/capture':
508
                return $this->_compile_capture_tag(false);
509
 
510
            case 'ldelim':
511
                return $this->left_delimiter;
512
 
513
            case 'rdelim':
514
                return $this->right_delimiter;
515
 
516
            case 'section':
517
                $this->_push_tag('section');
518
                return $this->_compile_section_start($tag_args);
519
 
520
            case 'sectionelse':
521
                $this->_push_tag('sectionelse');
522
                return "<?php endfor; else: ?>";
523
                break;
524
 
525
            case '/section':
526
                $_open_tag = $this->_pop_tag('section');
527
                if ($_open_tag == 'sectionelse')
528
                    return "<?php endif; ?>";
529
                else
530
                    return "<?php endfor; endif; ?>";
531
 
532
            case 'foreach':
533
                $this->_push_tag('foreach');
534
                return $this->_compile_foreach_start($tag_args);
535
                break;
536
 
537
            case 'foreachelse':
538
                $this->_push_tag('foreachelse');
539
                return "<?php endforeach; else: ?>";
540
 
541
            case '/foreach':
542
                $_open_tag = $this->_pop_tag('foreach');
543
                if ($_open_tag == 'foreachelse')
544
                    return "<?php endif; unset(\$_from); ?>";
545
                else
546
                    return "<?php endforeach; endif; unset(\$_from); ?>";
547
                break;
548
 
549
            case 'strip':
550
            case '/strip':
551
                if (substr($tag_command, 0, 1)=='/') {
552
                    $this->_pop_tag('strip');
553
                    if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
554
                        $this->_additional_newline = "\n";
555
                        return '{' . $tag_command . '}';
556
                    }
557
                } else {
558
                    $this->_push_tag('strip');
559
                    if ($this->_strip_depth++==0) { /* outermost opening {strip} */
560
                        $this->_additional_newline = "";
561
                        return '{' . $tag_command . '}';
562
                    }
563
                }
564
                return '';
565
 
566
            case 'php':
567
                /* handle folded tags replaced by {php} */
568
                list(, $block) = each($this->_folded_blocks);
569
                $this->_current_line_no += substr_count($block[0], "\n");
570
                /* the number of matched elements in the regexp in _compile_file()
571
                   determins the type of folded tag that was found */
572
                switch (count($block)) {
573
                    case 2: /* comment */
574
                        return '';
575
 
576
                    case 3: /* literal */
577
                        return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
578
 
579
                    case 4: /* php */
580
                        if ($this->security && !$this->security_settings['PHP_TAGS']) {
581
                            $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
582
                            return;
583
                        }
584
                        return '<?php ' . $block[3] .' ?>';
585
                }
586
                break;
587
 
588
            case 'insert':
589
                return $this->_compile_insert_tag($tag_args);
590
 
591
            default:
592
                if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
593
                    return $output;
594
                } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
595
                    return $output;
596
                } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
597
                    return $output;                    
598
                } else {
599
                    $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
600
                }
601
 
602
        }
603
    }
604
 
605
 
606
    /**
607
     * compile the custom compiler tag
608
     *
609
     * sets $output to the compiled custom compiler tag
610
     * @param string $tag_command
611
     * @param string $tag_args
612
     * @param string $output
613
     * @return boolean
614
     */
615
    function _compile_compiler_tag($tag_command, $tag_args, &$output)
616
    {
617
        $found = false;
618
        $have_function = true;
619
 
620
        /*
621
         * First we check if the compiler function has already been registered
622
         * or loaded from a plugin file.
623
         */
624
        if (isset($this->_plugins['compiler'][$tag_command])) {
625
            $found = true;
626
            $plugin_func = $this->_plugins['compiler'][$tag_command][0];
627
            if (!is_callable($plugin_func)) {
628
                $message = "compiler function '$tag_command' is not implemented";
629
                $have_function = false;
630
            }
631
        }
632
        /*
633
         * Otherwise we need to load plugin file and look for the function
634
         * inside it.
635
         */
636
        else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
637
            $found = true;
638
 
639
            include_once $plugin_file;
640
 
641
            $plugin_func = 'smarty_compiler_' . $tag_command;
642
            if (!is_callable($plugin_func)) {
643
                $message = "plugin function $plugin_func() not found in $plugin_file\n";
644
                $have_function = false;
645
            } else {
646
                $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
647
            }
648
        }
649
 
650
        /*
651
         * True return value means that we either found a plugin or a
652
         * dynamically registered function. False means that we didn't and the
653
         * compiler should now emit code to load custom function plugin for this
654
         * tag.
655
         */
656
        if ($found) {
657
            if ($have_function) {
658
                $output = call_user_func_array($plugin_func, array($tag_args, &$this));
659
                if($output != '') {
660
                $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
661
                                   . $output
662
                                   . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
663
                }
664
            } else {
665
                $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
666
            }
667
            return true;
668
        } else {
669
            return false;
670
        }
671
    }
672
 
673
 
674
    /**
675
     * compile block function tag
676
     *
677
     * sets $output to compiled block function tag
678
     * @param string $tag_command
679
     * @param string $tag_args
680
     * @param string $tag_modifier
681
     * @param string $output
682
     * @return boolean
683
     */
684
    function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
685
    {
686
        if (substr($tag_command, 0, 1) == '/') {
687
            $start_tag = false;
688
            $tag_command = substr($tag_command, 1);
689
        } else
690
            $start_tag = true;
691
 
692
        $found = false;
693
        $have_function = true;
694
 
695
        /*
696
         * First we check if the block function has already been registered
697
         * or loaded from a plugin file.
698
         */
699
        if (isset($this->_plugins['block'][$tag_command])) {
700
            $found = true;
701
            $plugin_func = $this->_plugins['block'][$tag_command][0];
702
            if (!is_callable($plugin_func)) {
703
                $message = "block function '$tag_command' is not implemented";
704
                $have_function = false;
705
            }
706
        }
707
        /*
708
         * Otherwise we need to load plugin file and look for the function
709
         * inside it.
710
         */
711
        else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
712
            $found = true;
713
 
714
            include_once $plugin_file;
715
 
716
            $plugin_func = 'smarty_block_' . $tag_command;
717
            if (!function_exists($plugin_func)) {
718
                $message = "plugin function $plugin_func() not found in $plugin_file\n";
719
                $have_function = false;
720
            } else {
721
                $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
722
 
723
            }
724
        }
725
 
726
        if (!$found) {
727
            return false;
728
        } else if (!$have_function) {
729
            $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
730
            return true;
731
        }
732
 
733
        /*
734
         * Even though we've located the plugin function, compilation
735
         * happens only once, so the plugin will still need to be loaded
736
         * at runtime for future requests.
737
         */
738
        $this->_add_plugin('block', $tag_command);
739
 
740
        if ($start_tag)
741
            $this->_push_tag($tag_command);
742
        else
743
            $this->_pop_tag($tag_command);
744
 
745
        if ($start_tag) {
746
            $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
747
            $attrs = $this->_parse_attrs($tag_args);
748
            $_cache_attrs='';
749
            $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
750
            $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
751
            $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
752
            $output .= 'while ($_block_repeat) { ob_start(); ?>';
753
        } else {
754
            $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
755
            $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
756
            if ($tag_modifier != '') {
757
                $this->_parse_modifiers($_out_tag_text, $tag_modifier);
758
            }
759
            $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
760
            $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
761
        }
762
 
763
        return true;
764
    }
765
 
766
 
767
    /**
768
     * compile custom function tag
769
     *
770
     * @param string $tag_command
771
     * @param string $tag_args
772
     * @param string $tag_modifier
773
     * @return string
774
     */
775
    function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
776
    {
777
        $found = false;
778
        $have_function = true;
779
 
780
        /*
781
         * First we check if the custom function has already been registered
782
         * or loaded from a plugin file.
783
         */
784
        if (isset($this->_plugins['function'][$tag_command])) {
785
            $found = true;
786
            $plugin_func = $this->_plugins['function'][$tag_command][0];
787
            if (!is_callable($plugin_func)) {
788
                $message = "custom function '$tag_command' is not implemented";
789
                $have_function = false;
790
            }
791
        }
792
        /*
793
         * Otherwise we need to load plugin file and look for the function
794
         * inside it.
795
         */
796
        else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
797
            $found = true;
798
 
799
            include_once $plugin_file;
800
 
801
            $plugin_func = 'smarty_function_' . $tag_command;
802
            if (!function_exists($plugin_func)) {
803
                $message = "plugin function $plugin_func() not found in $plugin_file\n";
804
                $have_function = false;
805
            } else {
806
                $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
807
 
808
            }
809
        }
810
 
811
        if (!$found) {
812
            return false;
813
        } else if (!$have_function) {
814
            $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
815
            return true;
816
        }
817
 
818
        /* declare plugin to be loaded on display of the template that
819
           we compile right now */
820
        $this->_add_plugin('function', $tag_command);
821
 
822
        $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
823
        $attrs = $this->_parse_attrs($tag_args);
824
        $_cache_attrs = '';
825
        $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
826
 
827
        $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
828
        if($tag_modifier != '') {
829
            $this->_parse_modifiers($output, $tag_modifier);
830
        }
831
 
832
        if($output != '') {
833
            $output =  '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
834
                . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
835
        }
836
 
837
        return true;
838
    }
839
 
840
    /**
841
     * compile a registered object tag
842
     *
843
     * @param string $tag_command
844
     * @param array $attrs
845
     * @param string $tag_modifier
846
     * @return string
847
     */
848
    function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
849
    {
850
        if (substr($tag_command, 0, 1) == '/') {
851
            $start_tag = false;
852
            $tag_command = substr($tag_command, 1);
853
        } else {
854
            $start_tag = true;
855
        }
856
 
857
        list($object, $obj_comp) = explode('->', $tag_command);
858
 
859
        $arg_list = array();
860
        if(count($attrs)) {
861
            $_assign_var = false;
862
            foreach ($attrs as $arg_name => $arg_value) {
863
                if($arg_name == 'assign') {
864
                    $_assign_var = $arg_value;
865
                    unset($attrs['assign']);
866
                    continue;
867
                }
868
                if (is_bool($arg_value))
869
                    $arg_value = $arg_value ? 'true' : 'false';
870
                $arg_list[] = "'$arg_name' => $arg_value";
871
            }
872
        }
873
 
874
        if($this->_reg_objects[$object][2]) {
875
            // smarty object argument format
876
            $args = "array(".implode(',', (array)$arg_list)."), \$this";
877
        } else {
878
            // traditional argument format
879
            $args = implode(',', array_values($attrs));
880
            if (empty($args)) {
881
                $args = '';
882
            }
883
        }
884
 
885
        $prefix = '';
886
        $postfix = '';
887
        $newline = '';
888
        if(!is_object($this->_reg_objects[$object][0])) {
889
            $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
890
        } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
891
            $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
892
        } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
893
            // method
894
            if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
895
                // block method
896
                if ($start_tag) {
897
                    $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
898
                    $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
899
                    $prefix .= "while (\$_block_repeat) { ob_start();";
900
                    $return = null;
901
                    $postfix = '';
902
                } else {
903
                    $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
904
                    $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
905
                    $postfix = "} array_pop(\$this->_tag_stack);";
906
                }
907
            } else {
908
                // non-block method
909
                $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
910
            }
911
        } else {
912
            // property
913
            $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
914
        }
915
 
916
        if($return != null) {
917
            if($tag_modifier != '') {
918
                $this->_parse_modifiers($return, $tag_modifier);
919
            }
920
 
921
            if(!empty($_assign_var)) {
922
                $output = "\$this->assign('" . $this->_dequote($_assign_var) ."',  $return);";
923
            } else {
924
                $output = 'echo ' . $return . ';';
925
                $newline = $this->_additional_newline;
926
            }
927
        } else {
928
            $output = '';
929
        }
930
 
931
        return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
932
    }
933
 
934
    /**
935
     * Compile {insert ...} tag
936
     *
937
     * @param string $tag_args
938
     * @return string
939
     */
940
    function _compile_insert_tag($tag_args)
941
    {
942
        $attrs = $this->_parse_attrs($tag_args);
943
        $name = $this->_dequote($attrs['name']);
944
 
945
        if (empty($name)) {
946
            return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
947
        }
948
 
949
        if (!preg_match('~^\w+$~', $name)) {
950
            return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__);
951
        }
952
 
953
        if (!empty($attrs['script'])) {
954
            $delayed_loading = true;
955
        } else {
956
            $delayed_loading = false;
957
        }
958
 
959
        foreach ($attrs as $arg_name => $arg_value) {
960
            if (is_bool($arg_value))
961
                $arg_value = $arg_value ? 'true' : 'false';
962
            $arg_list[] = "'$arg_name' => $arg_value";
963
        }
964
 
965
        $this->_add_plugin('insert', $name, $delayed_loading);
966
 
967
        $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
968
 
969
        return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
970
    }
971
 
972
    /**
973
     * Compile {include ...} tag
974
     *
975
     * @param string $tag_args
976
     * @return string
977
     */
978
    function _compile_include_tag($tag_args)
979
    {
980
        $attrs = $this->_parse_attrs($tag_args);
981
        $arg_list = array();
982
 
983
        if (empty($attrs['file'])) {
984
            $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
985
        }
986
 
987
        foreach ($attrs as $arg_name => $arg_value) {
988
            if ($arg_name == 'file') {
989
                $include_file = $arg_value;
990
                continue;
991
            } else if ($arg_name == 'assign') {
992
                $assign_var = $arg_value;
993
                continue;
994
            }
995
            if (is_bool($arg_value))
996
                $arg_value = $arg_value ? 'true' : 'false';
997
            $arg_list[] = "'$arg_name' => $arg_value";
998
        }
999
 
1000
        $output = '<?php ';
1001
 
1002
        if (isset($assign_var)) {
1003
            $output .= "ob_start();\n";
1004
        }
1005
 
1006
        $output .=
1007
            "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
1008
 
1009
 
1010
        $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
1011
        $output .= "\$this->_smarty_include($_params);\n" .
1012
        "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
1013
        "unset(\$_smarty_tpl_vars);\n";
1014
 
1015
        if (isset($assign_var)) {
1016
            $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
1017
        }
1018
 
1019
        $output .= ' ?>';
1020
 
1021
        return $output;
1022
 
1023
    }
1024
 
1025
    /**
1026
     * Compile {include ...} tag
1027
     *
1028
     * @param string $tag_args
1029
     * @return string
1030
     */
1031
    function _compile_include_php_tag($tag_args)
1032
    {
1033
        $attrs = $this->_parse_attrs($tag_args);
1034
 
1035
        if (empty($attrs['file'])) {
1036
            $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1037
        }
1038
 
1039
        $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1040
        $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1041
 
1042
        $arg_list = array();
1043
        foreach($attrs as $arg_name => $arg_value) {
1044
            if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1045
                if(is_bool($arg_value))
1046
                    $arg_value = $arg_value ? 'true' : 'false';
1047
                $arg_list[] = "'$arg_name' => $arg_value";
1048
            }
1049
        }
1050
 
1051
        $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1052
 
1053
        return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1054
    }
1055
 
1056
 
1057
    /**
1058
     * Compile {section ...} tag
1059
     *
1060
     * @param string $tag_args
1061
     * @return string
1062
     */
1063
    function _compile_section_start($tag_args)
1064
    {
1065
        $attrs = $this->_parse_attrs($tag_args);
1066
        $arg_list = array();
1067
 
1068
        $output = '<?php ';
1069
        $section_name = $attrs['name'];
1070
        if (empty($section_name)) {
1071
            $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1072
        }
1073
 
1074
        $output .= "unset(\$this->_sections[$section_name]);\n";
1075
        $section_props = "\$this->_sections[$section_name]";
1076
 
1077
        foreach ($attrs as $attr_name => $attr_value) {
1078
            switch ($attr_name) {
1079
                case 'loop':
1080
                    $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1081
                    break;
1082
 
1083
                case 'show':
1084
                    if (is_bool($attr_value))
1085
                        $show_attr_value = $attr_value ? 'true' : 'false';
1086
                    else
1087
                        $show_attr_value = "(bool)$attr_value";
1088
                    $output .= "{$section_props}['show'] = $show_attr_value;\n";
1089
                    break;
1090
 
1091
                case 'name':
1092
                    $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1093
                    break;
1094
 
1095
                case 'max':
1096
                case 'start':
1097
                    $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1098
                    break;
1099
 
1100
                case 'step':
1101
                    $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1102
                    break;
1103
 
1104
                default:
1105
                    $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1106
                    break;
1107
            }
1108
        }
1109
 
1110
        if (!isset($attrs['show']))
1111
            $output .= "{$section_props}['show'] = true;\n";
1112
 
1113
        if (!isset($attrs['loop']))
1114
            $output .= "{$section_props}['loop'] = 1;\n";
1115
 
1116
        if (!isset($attrs['max']))
1117
            $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1118
        else
1119
            $output .= "if ({$section_props}['max'] < 0)\n" .
1120
                       "    {$section_props}['max'] = {$section_props}['loop'];\n";
1121
 
1122
        if (!isset($attrs['step']))
1123
            $output .= "{$section_props}['step'] = 1;\n";
1124
 
1125
        if (!isset($attrs['start']))
1126
            $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1127
        else {
1128
            $output .= "if ({$section_props}['start'] < 0)\n" .
1129
                       "    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1130
                       "else\n" .
1131
                       "    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1132
        }
1133
 
1134
        $output .= "if ({$section_props}['show']) {\n";
1135
        if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1136
            $output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
1137
        } else {
1138
            $output .= "    {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
1139
        }
1140
        $output .= "    if ({$section_props}['total'] == 0)\n" .
1141
                   "        {$section_props}['show'] = false;\n" .
1142
                   "} else\n" .
1143
                   "    {$section_props}['total'] = 0;\n";
1144
 
1145
        $output .= "if ({$section_props}['show']):\n";
1146
        $output .= "
1147
            for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1148
                 {$section_props}['iteration'] <= {$section_props}['total'];
1149
                 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1150
        $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1151
        $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1152
        $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1153
        $output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
1154
        $output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1155
 
1156
        $output .= "?>";
1157
 
1158
        return $output;
1159
    }
1160
 
1161
 
1162
    /**
1163
     * Compile {foreach ...} tag.
1164
     *
1165
     * @param string $tag_args
1166
     * @return string
1167
     */
1168
    function _compile_foreach_start($tag_args)
1169
    {
1170
        $attrs = $this->_parse_attrs($tag_args);
1171
        $arg_list = array();
1172
 
1173
        if (empty($attrs['from'])) {
1174
            return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1175
        }
1176
        $from = $attrs['from'];
1177
 
1178
        if (empty($attrs['item'])) {
1179
            return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1180
        }
1181
        $item = $this->_dequote($attrs['item']);
1182
        if (!preg_match('~^\w+$~', $item)) {
1183
            return $this->_syntax_error("foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1184
        }
1185
 
1186
        if (isset($attrs['key'])) {
1187
            $key  = $this->_dequote($attrs['key']);
1188
            if (!preg_match('~^\w+$~', $key)) {
1189
                return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1190
            }
1191
            $key_part = "\$this->_tpl_vars['$key'] => ";
1192
        } else {
1193
            $key = null;
1194
            $key_part = '';
1195
        }
1196
 
1197
        if (isset($attrs['name'])) {
1198
            $name = $attrs['name'];
1199
        } else {
1200
            $name = null;
1201
        }
1202
 
1203
        $output = '<?php ';
1204
        $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1205
        if (isset($name)) {
1206
            $foreach_props = "\$this->_foreach[$name]";
1207
            $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1208
            $output .= "if ({$foreach_props}['total'] > 0):\n";
1209
            $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1210
            $output .= "        {$foreach_props}['iteration']++;\n";
1211
        } else {
1212
            $output .= "if (count(\$_from)):\n";
1213
            $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1214
        }
1215
        $output .= '?>';
1216
 
1217
        return $output;
1218
    }
1219
 
1220
 
1221
    /**
1222
     * Compile {capture} .. {/capture} tags
1223
     *
1224
     * @param boolean $start true if this is the {capture} tag
1225
     * @param string $tag_args
1226
     * @return string
1227
     */
1228
 
1229
    function _compile_capture_tag($start, $tag_args = '')
1230
    {
1231
        $attrs = $this->_parse_attrs($tag_args);
1232
 
1233
        if ($start) {
1234
            $buffer = isset($attrs['name']) ? $attrs['name'] : "'default'";
1235
            $assign = isset($attrs['assign']) ? $attrs['assign'] : null;
1236
            $append = isset($attrs['append']) ? $attrs['append'] : null;
1237
 
1238
            $output = "<?php ob_start(); ?>";
1239
            $this->_capture_stack[] = array($buffer, $assign, $append);
1240
        } else {
1241
            list($buffer, $assign, $append) = array_pop($this->_capture_stack);
1242
            $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1243
            if (isset($assign)) {
1244
                $output .= " \$this->assign($assign, ob_get_contents());";
1245
            }
1246
            if (isset($append)) {
1247
                $output .= " \$this->append($append, ob_get_contents());";
1248
            }
1249
            $output .= "ob_end_clean(); ?>";
1250
        }
1251
 
1252
        return $output;
1253
    }
1254
 
1255
    /**
1256
     * Compile {if ...} tag
1257
     *
1258
     * @param string $tag_args
1259
     * @param boolean $elseif if true, uses elseif instead of if
1260
     * @return string
1261
     */
1262
    function _compile_if_tag($tag_args, $elseif = false)
1263
    {
1264
 
1265
        /* Tokenize args for 'if' tag. */
1266
        preg_match_all('~(?>
1267
                ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1268
                ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
1269
                \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@    | # valid non-word token
1270
                \b\w+\b                                                        | # valid word token
1271
                \S+                                                           # anything else
1272
                )~x', $tag_args, $match);
1273
 
1274
        $tokens = $match[0];
1275
 
1276
        if(empty($tokens)) {
1277
            $_error_msg = $elseif ? "'elseif'" : "'if'";
1278
            $_error_msg .= ' statement requires arguments';
1279
            $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
1280
        }
1281
 
1282
 
1283
        // make sure we have balanced parenthesis
1284
        $token_count = array_count_values($tokens);
1285
        if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1286
            $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1287
        }
1288
 
1289
        $is_arg_stack = array();
1290
 
1291
        for ($i = 0; $i < count($tokens); $i++) {
1292
 
1293
            $token = &$tokens[$i];
1294
 
1295
            switch (strtolower($token)) {
1296
                case '!':
1297
                case '%':
1298
                case '!==':
1299
                case '==':
1300
                case '===':
1301
                case '>':
1302
                case '<':
1303
                case '!=':
1304
                case '<>':
1305
                case '<<':
1306
                case '>>':
1307
                case '<=':
1308
                case '>=':
1309
                case '&&':
1310
                case '||':
1311
                case '|':
1312
                case '^':
1313
                case '&':
1314
                case '~':
1315
                case ')':
1316
                case ',':
1317
                case '+':
1318
                case '-':
1319
                case '*':
1320
                case '/':
1321
                case '@':
1322
                    break;
1323
 
1324
                case 'eq':
1325
                    $token = '==';
1326
                    break;
1327
 
1328
                case 'ne':
1329
                case 'neq':
1330
                    $token = '!=';
1331
                    break;
1332
 
1333
                case 'lt':
1334
                    $token = '<';
1335
                    break;
1336
 
1337
                case 'le':
1338
                case 'lte':
1339
                    $token = '<=';
1340
                    break;
1341
 
1342
                case 'gt':
1343
                    $token = '>';
1344
                    break;
1345
 
1346
                case 'ge':
1347
                case 'gte':
1348
                    $token = '>=';
1349
                    break;
1350
 
1351
                case 'and':
1352
                    $token = '&&';
1353
                    break;
1354
 
1355
                case 'or':
1356
                    $token = '||';
1357
                    break;
1358
 
1359
                case 'not':
1360
                    $token = '!';
1361
                    break;
1362
 
1363
                case 'mod':
1364
                    $token = '%';
1365
                    break;
1366
 
1367
                case '(':
1368
                    array_push($is_arg_stack, $i);
1369
                    break;
1370
 
1371
                case 'is':
1372
                    /* If last token was a ')', we operate on the parenthesized
1373
                       expression. The start of the expression is on the stack.
1374
                       Otherwise, we operate on the last encountered token. */
1375
                    if ($tokens[$i-1] == ')') {
1376
                        $is_arg_start = array_pop($is_arg_stack);
1377
                        if ($is_arg_start != 0) {
1378
                            if (preg_match('~^' . $this->_func_regexp . '$~', $tokens[$is_arg_start-1])) {
1379
                                $is_arg_start--;
1380
                            }
1381
                        }
1382
                    } else
1383
                        $is_arg_start = $i-1;
1384
                    /* Construct the argument for 'is' expression, so it knows
1385
                       what to operate on. */
1386
                    $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1387
 
1388
                    /* Pass all tokens from next one until the end to the
1389
                       'is' expression parsing function. The function will
1390
                       return modified tokens, where the first one is the result
1391
                       of the 'is' expression and the rest are the tokens it
1392
                       didn't touch. */
1393
                    $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1394
 
1395
                    /* Replace the old tokens with the new ones. */
1396
                    array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1397
 
1398
                    /* Adjust argument start so that it won't change from the
1399
                       current position for the next iteration. */
1400
                    $i = $is_arg_start;
1401
                    break;
1402
 
1403
                default:
1404
                    if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1405
                            // function call
1406
                            if($this->security &&
1407
                               !in_array($token, $this->security_settings['IF_FUNCS'])) {
1408
                                $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1409
                            }
1410
                    } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1411
                        // variable function call
1412
                        $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);                      
1413
                    } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1414
                        // object or variable
1415
                        $token = $this->_parse_var_props($token);
1416
                    } elseif(is_numeric($token)) {
1417
                        // number, skip it
1418
                    } else {
1419
                        $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1420
                    }
1421
                    break;
1422
            }
1423
        }
1424
 
1425
        if ($elseif)
1426
            return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1427
        else
1428
            return '<?php if ('.implode(' ', $tokens).'): ?>';
1429
    }
1430
 
1431
 
1432
    function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1433
        $arg_list = array();
1434
 
1435
        if (isset($type) && isset($name)
1436
            && isset($this->_plugins[$type])
1437
            && isset($this->_plugins[$type][$name])
1438
            && empty($this->_plugins[$type][$name][4])
1439
            && is_array($this->_plugins[$type][$name][5])
1440
            ) {
1441
            /* we have a list of parameters that should be cached */
1442
            $_cache_attrs = $this->_plugins[$type][$name][5];
1443
            $_count = $this->_cache_attrs_count++;
1444
            $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1445
 
1446
        } else {
1447
            /* no parameters are cached */
1448
            $_cache_attrs = null;
1449
        }
1450
 
1451
        foreach ($attrs as $arg_name => $arg_value) {
1452
            if (is_bool($arg_value))
1453
                $arg_value = $arg_value ? 'true' : 'false';
1454
            if (is_null($arg_value))
1455
                $arg_value = 'null';
1456
            if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1457
                $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1458
            } else {
1459
                $arg_list[] = "'$arg_name' => $arg_value";
1460
            }
1461
        }
1462
        return $arg_list;
1463
    }
1464
 
1465
    /**
1466
     * Parse is expression
1467
     *
1468
     * @param string $is_arg
1469
     * @param array $tokens
1470
     * @return array
1471
     */
1472
    function _parse_is_expr($is_arg, $tokens)
1473
    {
1474
        $expr_end = 0;
1475
        $negate_expr = false;
1476
 
1477
        if (($first_token = array_shift($tokens)) == 'not') {
1478
            $negate_expr = true;
1479
            $expr_type = array_shift($tokens);
1480
        } else
1481
            $expr_type = $first_token;
1482
 
1483
        switch ($expr_type) {
1484
            case 'even':
1485
                if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1486
                    $expr_end++;
1487
                    $expr_arg = $tokens[$expr_end++];
1488
                    $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1489
                } else
1490
                    $expr = "!(1 & $is_arg)";
1491
                break;
1492
 
1493
            case 'odd':
1494
                if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1495
                    $expr_end++;
1496
                    $expr_arg = $tokens[$expr_end++];
1497
                    $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1498
                } else
1499
                    $expr = "(1 & $is_arg)";
1500
                break;
1501
 
1502
            case 'div':
1503
                if (@$tokens[$expr_end] == 'by') {
1504
                    $expr_end++;
1505
                    $expr_arg = $tokens[$expr_end++];
1506
                    $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1507
                } else {
1508
                    $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1509
                }
1510
                break;
1511
 
1512
            default:
1513
                $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1514
                break;
1515
        }
1516
 
1517
        if ($negate_expr) {
1518
            $expr = "!($expr)";
1519
        }
1520
 
1521
        array_splice($tokens, 0, $expr_end, $expr);
1522
 
1523
        return $tokens;
1524
    }
1525
 
1526
 
1527
    /**
1528
     * Parse attribute string
1529
     *
1530
     * @param string $tag_args
1531
     * @return array
1532
     */
1533
    function _parse_attrs($tag_args)
1534
    {
1535
 
1536
        /* Tokenize tag attributes. */
1537
        preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1538
                         )+ |
1539
                         [=]
1540
                        ~x', $tag_args, $match);
1541
        $tokens       = $match[0];
1542
 
1543
        $attrs = array();
1544
        /* Parse state:
1545
 
1546
            1 - expecting '='
1547
            2 - expecting attribute value (not '=') */
1548
        $state = 0;
1549
 
1550
        foreach ($tokens as $token) {
1551
            switch ($state) {
1552
                case 0:
1553
                    /* If the token is a valid identifier, we set attribute name
1554
                       and go to state 1. */
1555
                    if (preg_match('~^\w+$~', $token)) {
1556
                        $attr_name = $token;
1557
                        $state = 1;
1558
                    } else
1559
                        $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1560
                    break;
1561
 
1562
                case 1:
1563
                    /* If the token is '=', then we go to state 2. */
1564
                    if ($token == '=') {
1565
                        $state = 2;
1566
                    } else
1567
                        $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1568
                    break;
1569
 
1570
                case 2:
1571
                    /* If token is not '=', we set the attribute value and go to
1572
                       state 0. */
1573
                    if ($token != '=') {
1574
                        /* We booleanize the token if it's a non-quoted possible
1575
                           boolean value. */
1576
                        if (preg_match('~^(on|yes|true)$~', $token)) {
1577
                            $token = 'true';
1578
                        } else if (preg_match('~^(off|no|false)$~', $token)) {
1579
                            $token = 'false';
1580
                        } else if ($token == 'null') {
1581
                            $token = 'null';
1582
                        } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1583
                            /* treat integer literally */
1584
                        } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1585
                            /* treat as a string, double-quote it escaping quotes */
1586
                            $token = '"'.addslashes($token).'"';
1587
                        }
1588
 
1589
                        $attrs[$attr_name] = $token;
1590
                        $state = 0;
1591
                    } else
1592
                        $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1593
                    break;
1594
            }
1595
            $last_token = $token;
1596
        }
1597
 
1598
        if($state != 0) {
1599
            if($state == 1) {
1600
                $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1601
            } else {
1602
                $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1603
            }
1604
        }
1605
 
1606
        $this->_parse_vars_props($attrs);
1607
 
1608
        return $attrs;
1609
    }
1610
 
1611
    /**
1612
     * compile multiple variables and section properties tokens into
1613
     * PHP code
1614
     *
1615
     * @param array $tokens
1616
     */
1617
    function _parse_vars_props(&$tokens)
1618
    {
1619
        foreach($tokens as $key => $val) {
1620
            $tokens[$key] = $this->_parse_var_props($val);
1621
        }
1622
    }
1623
 
1624
    /**
1625
     * compile single variable and section properties token into
1626
     * PHP code
1627
     *
1628
     * @param string $val
1629
     * @param string $tag_attrs
1630
     * @return string
1631
     */
1632
    function _parse_var_props($val)
1633
    {
1634
        $val = trim($val);
1635
 
1636
        if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1637
            // $ variable or object
1638
            $return = $this->_parse_var($match[1]);
1639
            $modifiers = $match[2];
1640
            if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1641
                $_default_mod_string = implode('|',(array)$this->default_modifiers);
1642
                $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1643
            }
1644
            $this->_parse_modifiers($return, $modifiers);
1645
            return $return;
1646
        } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1647
                // double quoted text
1648
                preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1649
                $return = $this->_expand_quoted_text($match[1]);
1650
                if($match[2] != '') {
1651
                    $this->_parse_modifiers($return, $match[2]);
1652
                }
1653
                return $return;
1654
            }
1655
        elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1656
                // numerical constant
1657
                preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1658
                if($match[2] != '') {
1659
                    $this->_parse_modifiers($match[1], $match[2]);
1660
                    return $match[1];
1661
                }
1662
            }
1663
        elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1664
                // single quoted text
1665
                preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1666
                if($match[2] != '') {
1667
                    $this->_parse_modifiers($match[1], $match[2]);
1668
                    return $match[1];
1669
                }
1670
            }
1671
        elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1672
                // config var
1673
                return $this->_parse_conf_var($val);
1674
            }
1675
        elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1676
                // section var
1677
                return $this->_parse_section_prop($val);
1678
            }
1679
        elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1680
            // literal string
1681
            return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
1682
        }
1683
        return $val;
1684
    }
1685
 
1686
    /**
1687
     * expand quoted text with embedded variables
1688
     *
1689
     * @param string $var_expr
1690
     * @return string
1691
     */
1692
    function _expand_quoted_text($var_expr)
1693
    {
1694
        // if contains unescaped $, expand it
1695
        if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1696
            $_match = $_match[0];
1697
            $_replace = array();
1698
            foreach($_match as $_var) {
1699
                $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
1700
            }
1701
            $var_expr = strtr($var_expr, $_replace);
1702
            $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1703
        } else {
1704
            $_return = $var_expr;
1705
        }
1706
        // replace double quoted literal string with single quotes
1707
        $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1708
        // escape dollar sign if not printing a var
1709
        $_return = preg_replace('~\$(\W)~',"\\\\\$\\1",$_return);
1710
        return $_return;
1711
    }
1712
 
1713
    /**
1714
     * parse variable expression into PHP code
1715
     *
1716
     * @param string $var_expr
1717
     * @param string $output
1718
     * @return string
1719
     */
1720
    function _parse_var($var_expr)
1721
    {
1722
        $_has_math = false;
1723
        $_has_php4_method_chaining = false;
1724
        $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1725
 
1726
        if(count($_math_vars) > 1) {
1727
            $_first_var = "";
1728
            $_complete_var = "";
1729
            $_output = "";
1730
            // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1731
            foreach($_math_vars as $_k => $_math_var) {
1732
                $_math_var = $_math_vars[$_k];
1733
 
1734
                if(!empty($_math_var) || is_numeric($_math_var)) {
1735
                    // hit a math operator, so process the stuff which came before it
1736
                    if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1737
                        $_has_math = true;
1738
                        if(!empty($_complete_var) || is_numeric($_complete_var)) {
1739
                            $_output .= $this->_parse_var($_complete_var);
1740
                        }
1741
 
1742
                        // just output the math operator to php
1743
                        $_output .= $_math_var;
1744
 
1745
                        if(empty($_first_var))
1746
                            $_first_var = $_complete_var;
1747
 
1748
                        $_complete_var = "";
1749
                    } else {
1750
                        $_complete_var .= $_math_var;
1751
                    }
1752
                }
1753
            }
1754
            if($_has_math) {
1755
                if(!empty($_complete_var) || is_numeric($_complete_var))
1756
                    $_output .= $this->_parse_var($_complete_var);
1757
 
1758
                // get the modifiers working (only the last var from math + modifier is left)
1759
                $var_expr = $_complete_var;
1760
            }
1761
        }
1762
 
1763
        // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1764
        if(is_numeric(substr($var_expr, 0, 1)))
1765
            $_var_ref = $var_expr;
1766
        else
1767
            $_var_ref = substr($var_expr, 1);
1768
 
1769
        if(!$_has_math) {
1770
 
1771
            // get [foo] and .foo and ->foo and (...) pieces
1772
            preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1773
 
1774
            $_indexes = $match[0];
1775
            $_var_name = array_shift($_indexes);
1776
 
1777
            /* Handle $smarty.* variable references as a special case. */
1778
            if ($_var_name == 'smarty') {
1779
                /*
1780
                 * If the reference could be compiled, use the compiled output;
1781
                 * otherwise, fall back on the $smarty variable generated at
1782
                 * run-time.
1783
                 */
1784
                if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1785
                    $_output = $smarty_ref;
1786
                } else {
1787
                    $_var_name = substr(array_shift($_indexes), 1);
1788
                    $_output = "\$this->_smarty_vars['$_var_name']";
1789
                }
1790
            } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
1791
                // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1792
                if(count($_indexes) > 0)
1793
                {
1794
                    $_var_name .= implode("", $_indexes);
1795
                    $_indexes = array();
1796
                }
1797
                $_output = $_var_name;
1798
            } else {
1799
                $_output = "\$this->_tpl_vars['$_var_name']";
1800
            }
1801
 
1802
            foreach ($_indexes as $_index) {
1803
                if (substr($_index, 0, 1) == '[') {
1804
                    $_index = substr($_index, 1, -1);
1805
                    if (is_numeric($_index)) {
1806
                        $_output .= "[$_index]";
1807
                    } elseif (substr($_index, 0, 1) == '$') {
1808
                        if (strpos($_index, '.') !== false) {
1809
                            $_output .= '[' . $this->_parse_var($_index) . ']';
1810
                        } else {
1811
                            $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1812
                        }
1813
                    } else {
1814
                        $_var_parts = explode('.', $_index);
1815
                        $_var_section = $_var_parts[0];
1816
                        $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1817
                        $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1818
                    }
1819
                } else if (substr($_index, 0, 1) == '.') {
1820
                    if (substr($_index, 1, 1) == '$')
1821
                        $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1822
                    else
1823
                        $_output .= "['" . substr($_index, 1) . "']";
1824
                } else if (substr($_index,0,2) == '->') {
1825
                    if(substr($_index,2,2) == '__') {
1826
                        $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1827
                    } elseif($this->security && substr($_index, 2, 1) == '_') {
1828
                        $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1829
                    } elseif (substr($_index, 2, 1) == '$') {
1830
                        if ($this->security) {
1831
                            $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1832
                        } else {
1833
                            $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1834
                        }
1835
                    } else {
1836
                       if ($this->_phpversion < 5) {
1837
                         $_has_php4_method_chaining = true;
1838
                         $_output .= "; \$_foo = \$_foo";
1839
                       }
1840
                        $_output .= $_index;
1841
                    }
1842
                } elseif (substr($_index, 0, 1) == '(') {
1843
                    $_index = $this->_parse_parenth_args($_index);
1844
                    $_output .= $_index;
1845
                } else {
1846
                    $_output .= $_index;
1847
                }
1848
            }
1849
        }
1850
 
1851
        if ($_has_php4_method_chaining) {
1852
           $_tmp = str_replace("'","\'",'$_foo = '.$_output.'; return $_foo;');
1853
           return "eval('".$_tmp."')";
1854
        } else {
1855
           return $_output;
1856
        }
1857
    }
1858
 
1859
    /**
1860
     * parse arguments in function call parenthesis
1861
     *
1862
     * @param string $parenth_args
1863
     * @return string
1864
     */
1865
    function _parse_parenth_args($parenth_args)
1866
    {
1867
        preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1868
        $orig_vals = $match = $match[0];
1869
        $this->_parse_vars_props($match);
1870
        $replace = array();
1871
        for ($i = 0, $count = count($match); $i < $count; $i++) {
1872
            $replace[$orig_vals[$i]] = $match[$i];
1873
        }
1874
        return strtr($parenth_args, $replace);
1875
    }
1876
 
1877
    /**
1878
     * parse configuration variable expression into PHP code
1879
     *
1880
     * @param string $conf_var_expr
1881
     */
1882
    function _parse_conf_var($conf_var_expr)
1883
    {
1884
        $parts = explode('|', $conf_var_expr, 2);
1885
        $var_ref = $parts[0];
1886
        $modifiers = isset($parts[1]) ? $parts[1] : '';
1887
 
1888
        $var_name = substr($var_ref, 1, -1);
1889
 
1890
        $output = "\$this->_config[0]['vars']['$var_name']";
1891
 
1892
        $this->_parse_modifiers($output, $modifiers);
1893
 
1894
        return $output;
1895
    }
1896
 
1897
    /**
1898
     * parse section property expression into PHP code
1899
     *
1900
     * @param string $section_prop_expr
1901
     * @return string
1902
     */
1903
    function _parse_section_prop($section_prop_expr)
1904
    {
1905
        $parts = explode('|', $section_prop_expr, 2);
1906
        $var_ref = $parts[0];
1907
        $modifiers = isset($parts[1]) ? $parts[1] : '';
1908
 
1909
        preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1910
        $section_name = $match[1];
1911
        $prop_name = $match[2];
1912
 
1913
        $output = "\$this->_sections['$section_name']['$prop_name']";
1914
 
1915
        $this->_parse_modifiers($output, $modifiers);
1916
 
1917
        return $output;
1918
    }
1919
 
1920
 
1921
    /**
1922
     * parse modifier chain into PHP code
1923
     *
1924
     * sets $output to parsed modified chain
1925
     * @param string $output
1926
     * @param string $modifier_string
1927
     */
1928
    function _parse_modifiers(&$output, $modifier_string)
1929
    {
1930
        preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1931
        list(, $_modifiers, $modifier_arg_strings) = $_match;
1932
 
1933
        for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1934
            $_modifier_name = $_modifiers[$_i];
1935
 
1936
            if($_modifier_name == 'smarty') {
1937
                // skip smarty modifier
1938
                continue;
1939
            }
1940
 
1941
            preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1942
            $_modifier_args = $_match[1];
1943
 
1944
            if (substr($_modifier_name, 0, 1) == '@') {
1945
                $_map_array = false;
1946
                $_modifier_name = substr($_modifier_name, 1);
1947
            } else {
1948
                $_map_array = true;
1949
            }
1950
 
1951
            if (empty($this->_plugins['modifier'][$_modifier_name])
1952
                && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1953
                && function_exists($_modifier_name)) {
1954
                if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1955
                    $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1956
                } else {
1957
                    $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name,  null, null, false);
1958
                }
1959
            }
1960
            $this->_add_plugin('modifier', $_modifier_name);
1961
 
1962
            $this->_parse_vars_props($_modifier_args);
1963
 
1964
            if($_modifier_name == 'default') {
1965
                // supress notifications of default modifier vars and args
1966
                if(substr($output, 0, 1) == '$') {
1967
                    $output = '@' . $output;
1968
                }
1969
                if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
1970
                    $_modifier_args[0] = '@' . $_modifier_args[0];
1971
                }
1972
            }
1973
            if (count($_modifier_args) > 0)
1974
                $_modifier_args = ', '.implode(', ', $_modifier_args);
1975
            else
1976
                $_modifier_args = '';
1977
 
1978
            if ($_map_array) {
1979
                $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
1980
 
1981
            } else {
1982
 
1983
                $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1984
 
1985
            }
1986
        }
1987
    }
1988
 
1989
 
1990
    /**
1991
     * add plugin
1992
     *
1993
     * @param string $type
1994
     * @param string $name
1995
     * @param boolean? $delayed_loading
1996
     */
1997
    function _add_plugin($type, $name, $delayed_loading = null)
1998
    {
1999
        if (!isset($this->_plugin_info[$type])) {
2000
            $this->_plugin_info[$type] = array();
2001
        }
2002
        if (!isset($this->_plugin_info[$type][$name])) {
2003
            $this->_plugin_info[$type][$name] = array($this->_current_file,
2004
                                                      $this->_current_line_no,
2005
                                                      $delayed_loading);
2006
        }
2007
    }
2008
 
2009
 
2010
    /**
2011
     * Compiles references of type $smarty.foo
2012
     *
2013
     * @param string $indexes
2014
     * @return string
2015
     */
2016
    function _compile_smarty_ref(&$indexes)
2017
    {
2018
        /* Extract the reference name. */
2019
        $_ref = substr($indexes[0], 1);
2020
        foreach($indexes as $_index_no=>$_index) {
2021
            if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
2022
                $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2023
            }
2024
        }
2025
 
2026
        switch ($_ref) {
2027
            case 'now':
2028
                $compiled_ref = 'time()';
2029
                $_max_index = 1;
2030
                break;
2031
 
2032
            case 'foreach':
2033
                array_shift($indexes);
2034
                $_var = $this->_parse_var_props(substr($indexes[0], 1));
2035
                $_propname = substr($indexes[1], 1);
2036
                $_max_index = 1;
2037
                switch ($_propname) {
2038
                    case 'index':
2039
                        array_shift($indexes);
2040
                        $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
2041
                        break;
2042
 
2043
                    case 'first':
2044
                        array_shift($indexes);
2045
                        $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
2046
                        break;
2047
 
2048
                    case 'last':
2049
                        array_shift($indexes);
2050
                        $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2051
                        break;
2052
 
2053
                    case 'show':
2054
                        array_shift($indexes);
2055
                        $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2056
                        break;
2057
 
2058
                    default:
2059
                        unset($_max_index);
2060
                        $compiled_ref = "\$this->_foreach[$_var]";
2061
                }
2062
                break;
2063
 
2064
            case 'section':
2065
                array_shift($indexes);
2066
                $_var = $this->_parse_var_props(substr($indexes[0], 1));
2067
                $compiled_ref = "\$this->_sections[$_var]";
2068
                break;
2069
 
2070
            case 'get':
2071
                $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
2072
                break;
2073
 
2074
            case 'post':
2075
                $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
2076
                break;
2077
 
2078
            case 'cookies':
2079
                $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
2080
                break;
2081
 
2082
            case 'env':
2083
                $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
2084
                break;
2085
 
2086
            case 'server':
2087
                $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
2088
                break;
2089
 
2090
            case 'session':
2091
                $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
2092
                break;
2093
 
2094
            /*
2095
             * These cases are handled either at run-time or elsewhere in the
2096
             * compiler.
2097
             */
2098
            case 'request':
2099
                if ($this->request_use_auto_globals) {
2100
                    $compiled_ref = '$_REQUEST';
2101
                    break;
2102
                } else {
2103
                    $this->_init_smarty_vars = true;
2104
                }
2105
                return null;
2106
 
2107
            case 'capture':
2108
                return null;
2109
 
2110
            case 'template':
2111
                $compiled_ref = "'$this->_current_file'";
2112
                $_max_index = 1;
2113
                break;
2114
 
2115
            case 'version':
2116
                $compiled_ref = "'$this->_version'";
2117
                $_max_index = 1;
2118
                break;
2119
 
2120
            case 'const':
2121
                if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2122
                    $this->_syntax_error("(secure mode) constants not permitted",
2123
                                         E_USER_WARNING, __FILE__, __LINE__);
2124
                    return;
2125
                }
2126
                array_shift($indexes);
2127
                if (preg_match('!^\.\w+$!', $indexes[0])) {
2128
                    $compiled_ref = '@' . substr($indexes[0], 1);
2129
                } else {
2130
                    $_val = $this->_parse_var_props(substr($indexes[0], 1));
2131
                    $compiled_ref = '@constant(' . $_val . ')';
2132
                }
2133
                $_max_index = 1;
2134
                break;
2135
 
2136
            case 'config':
2137
                $compiled_ref = "\$this->_config[0]['vars']";
2138
                $_max_index = 3;
2139
                break;
2140
 
2141
            case 'ldelim':
2142
                $compiled_ref = "'$this->left_delimiter'";
2143
                break;
2144
 
2145
            case 'rdelim':
2146
                $compiled_ref = "'$this->right_delimiter'";
2147
                break;
2148
 
2149
            default:
2150
                $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2151
                break;
2152
        }
2153
 
2154
        if (isset($_max_index) && count($indexes) > $_max_index) {
2155
            $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2156
        }
2157
 
2158
        array_shift($indexes);
2159
        return $compiled_ref;
2160
    }
2161
 
2162
    /**
2163
     * compiles call to plugin of type $type with name $name
2164
     * returns a string containing the function-name or method call
2165
     * without the paramter-list that would have follow to make the
2166
     * call valid php-syntax
2167
     *
2168
     * @param string $type
2169
     * @param string $name
2170
     * @return string
2171
     */
2172
    function _compile_plugin_call($type, $name) {
2173
        if (isset($this->_plugins[$type][$name])) {
2174
            /* plugin loaded */
2175
            if (is_array($this->_plugins[$type][$name][0])) {
2176
                return ((is_object($this->_plugins[$type][$name][0][0])) ?
2177
                        "\$this->_plugins['$type']['$name'][0][0]->"    /* method callback */
2178
                        : (string)($this->_plugins[$type][$name][0][0]).'::'    /* class callback */
2179
                       ). $this->_plugins[$type][$name][0][1];
2180
 
2181
            } else {
2182
                /* function callback */
2183
                return $this->_plugins[$type][$name][0];
2184
 
2185
            }
2186
        } else {
2187
            /* plugin not loaded -> auto-loadable-plugin */
2188
            return 'smarty_'.$type.'_'.$name;
2189
 
2190
        }
2191
    }
2192
 
2193
    /**
2194
     * load pre- and post-filters
2195
     */
2196
    function _load_filters()
2197
    {
2198
        if (count($this->_plugins['prefilter']) > 0) {
2199
            foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2200
                if ($prefilter === false) {
2201
                    unset($this->_plugins['prefilter'][$filter_name]);
2202
                    $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2203
                    require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2204
                    smarty_core_load_plugins($_params, $this);
2205
                }
2206
            }
2207
        }
2208
        if (count($this->_plugins['postfilter']) > 0) {
2209
            foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2210
                if ($postfilter === false) {
2211
                    unset($this->_plugins['postfilter'][$filter_name]);
2212
                    $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2213
                    require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2214
                    smarty_core_load_plugins($_params, $this);
2215
                }
2216
            }
2217
        }
2218
    }
2219
 
2220
 
2221
    /**
2222
     * Quote subpattern references
2223
     *
2224
     * @param string $string
2225
     * @return string
2226
     */
2227
    function _quote_replace($string)
2228
    {
2229
        return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2230
    }
2231
 
2232
    /**
2233
     * display Smarty syntax error
2234
     *
2235
     * @param string $error_msg
2236
     * @param integer $error_type
2237
     * @param string $file
2238
     * @param integer $line
2239
     */
2240
    function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2241
    {
2242
        $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2243
    }
2244
 
2245
 
2246
    /**
2247
     * check if the compilation changes from cacheable to
2248
     * non-cacheable state with the beginning of the current
2249
     * plugin. return php-code to reflect the transition.
2250
     * @return string
2251
     */
2252
    function _push_cacheable_state($type, $name) {
2253
        $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2254
        if ($_cacheable
2255
            || 0<$this->_cacheable_state++) return '';
2256
        if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2257
        $_ret = 'if ($this->caching && !$this->_cache_including): echo \'{nocache:'
2258
            . $this->_cache_serial . '#' . $this->_nocache_count
2259
            . '}\'; endif;';
2260
        return $_ret;
2261
    }
2262
 
2263
 
2264
    /**
2265
     * check if the compilation changes from non-cacheable to
2266
     * cacheable state with the end of the current plugin return
2267
     * php-code to reflect the transition.
2268
     * @return string
2269
     */
2270
    function _pop_cacheable_state($type, $name) {
2271
        $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2272
        if ($_cacheable
2273
            || --$this->_cacheable_state>0) return '';
2274
        return 'if ($this->caching && !$this->_cache_including): echo \'{/nocache:'
2275
            . $this->_cache_serial . '#' . ($this->_nocache_count++)
2276
            . '}\'; endif;';
2277
    }
2278
 
2279
 
2280
    /**
2281
     * push opening tag-name, file-name and line-number on the tag-stack
2282
     * @param string the opening tag's name
2283
     */
2284
    function _push_tag($open_tag)
2285
    {
2286
        array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2287
    }
2288
 
2289
    /**
2290
     * pop closing tag-name
2291
     * raise an error if this stack-top doesn't match with the closing tag
2292
     * @param string the closing tag's name
2293
     * @return string the opening tag's name
2294
     */
2295
    function _pop_tag($close_tag)
2296
    {
2297
        $message = '';
2298
        if (count($this->_tag_stack)>0) {
2299
            list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2300
            if ($close_tag == $_open_tag) {
2301
                return $_open_tag;
2302
            }
2303
            if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2304
                return $this->_pop_tag($close_tag);
2305
            }
2306
            if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2307
                $this->_pop_tag($close_tag);
2308
                return $_open_tag;
2309
            }
2310
            if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2311
                $this->_pop_tag($close_tag);
2312
                return $_open_tag;
2313
            }
2314
            if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2315
                $_open_tag = 'if';
2316
            } elseif ($_open_tag == 'sectionelse') {
2317
                $_open_tag = 'section';
2318
            } elseif ($_open_tag == 'foreachelse') {
2319
                $_open_tag = 'foreach';
2320
            }
2321
            $message = " expected {/$_open_tag} (opened line $_line_no).";
2322
        }
2323
        $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2324
                             E_USER_ERROR, __FILE__, __LINE__);
2325
    }
2326
 
2327
}
2328
 
2329
/**
2330
 * compare to values by their string length
2331
 *
2332
 * @access private
2333
 * @param string $a
2334
 * @param string $b
2335
 * @return 0|-1|1
2336
 */
2337
function _smarty_sort_length($a, $b)
2338
{
2339
    if($a == $b)
2340
        return 0;
2341
 
2342
    if(strlen($a) == strlen($b))
2343
        return ($a > $b) ? -1 : 1;
2344
 
2345
    return (strlen($a) > strlen($b)) ? -1 : 1;
2346
}
2347
 
2348
 
2349
/* vim: set et: */
2350
 
2351
?>