Subversion-Projekte lars-tiefland.niewerth

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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