PageRenderTime 48ms CodeModel.GetById 2ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 1ms

/framework/vendor/smarty2/lib/Smarty_Compiler.class.php

http://zoop.googlecode.com/
PHP | 1632 lines | 1105 code | 195 blank | 332 comment | 228 complexity | 348841a029ab7a7e5f51ca3b2cd3b3b1 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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.14
  25 * @copyright 2001-2005 New Digital Group, Inc.
  26 * @package Smarty
  27 */
  28
  29/* $Id: Smarty_Compiler.class.php,v 1.381 2006/05/25 14:46:18 boots Exp $ */
  30
  31/**
  32 * Template compiling class
  33 * @package Smarty
  34 */
  35class 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            $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
 931        }
 932
 933        if (!empty($attrs['script'])) {
 934            $delayed_loading = true;
 935        } else {
 936            $delayed_loading = false;
 937        }
 938
 939        foreach ($attrs as $arg_name => $arg_value) {
 940            if (is_bool($arg_value))
 941                $arg_value = $arg_value ? 'true' : 'false';
 942            $arg_list[] = "'$arg_name' => $arg_value";
 943        }
 944
 945        $this->_add_plugin('insert', $name, $delayed_loading);
 946
 947        $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
 948
 949        return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
 950    }
 951
 952    /**
 953     * Compile {include ...} tag
 954     *
 955     * @param string $tag_args
 956     * @return string
 957     */
 958    function _compile_include_tag($tag_args)
 959    {
 960        $attrs = $this->_parse_attrs($tag_args);
 961        $arg_list = array();
 962
 963        if (empty($attrs['file'])) {
 964            $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
 965        }
 966
 967        foreach ($attrs as $arg_name => $arg_value) {
 968            if ($arg_name == 'file') {
 969                $include_file = $arg_value;
 970                continue;
 971            } else if ($arg_name == 'assign') {
 972                $assign_var = $arg_value;
 973                continue;
 974            }
 975            if (is_bool($arg_value))
 976                $arg_value = $arg_value ? 'true' : 'false';
 977            $arg_list[] = "'$arg_name' => $arg_value";
 978        }
 979
 980        $output = '<?php ';
 981
 982        if (isset($assign_var)) {
 983            $output .= "ob_start();\n";
 984        }
 985
 986        $output .=
 987            "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
 988
 989
 990        $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
 991        $output .= "\$this->_smarty_include($_params);\n" .
 992        "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
 993        "unset(\$_smarty_tpl_vars);\n";
 994
 995        if (isset($assign_var)) {
 996            $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
 997        }
 998
 999        $output .= ' ?>';
1000
1001        return $output;
1002
1003    }
1004
1005    /**
1006     * Compile {include ...} tag
1007     *
1008     * @param string $tag_args
1009     * @return string
1010     */
1011    function _compile_include_php_tag($tag_args)
1012    {
1013        $attrs = $this->_parse_attrs($tag_args);
1014
1015        if (empty($attrs['file'])) {
1016            $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1017        }
1018
1019        $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1020        $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1021
1022        $arg_list = array();
1023        foreach($attrs as $arg_name => $arg_value) {
1024            if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1025                if(is_bool($arg_value))
1026                    $arg_value = $arg_value ? 'true' : 'false';
1027                $arg_list[] = "'$arg_name' => $arg_value";
1028            }
1029        }
1030
1031        $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1032
1033        return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1034    }
1035
1036
1037    /**
1038     * Compile {section ...} tag
1039     *
1040     * @param string $tag_args
1041     * @return string
1042     */
1043    function _compile_section_start($tag_args)
1044    {
1045        $attrs = $this->_parse_attrs($tag_args);
1046        $arg_list = array();
1047
1048        $output = '<?php ';
1049        $section_name = $attrs['name'];
1050        if (empty($section_name)) {
1051            $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1052        }
1053
1054        $output .= "unset(\$this->_sections[$section_name]);\n";
1055        $section_props = "\$this->_sections[$section_name]";
1056
1057        foreach ($attrs as $attr_name => $attr_value) {
1058            switch ($attr_name) {
1059                case 'loop':
1060                    $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1061                    break;
1062
1063                case 'show':
1064                    if (is_bool($attr_value))
1065                        $show_attr_value = $attr_value ? 'true' : 'false';
1066                    else
1067                        $show_attr_value = "(bool)$attr_value";
1068                    $output .= "{$section_props}['show'] = $show_attr_value;\n";
1069                    break;
1070
1071                case 'name':
1072                    $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1073                    break;
1074
1075                case 'max':
1076                case 'start':
1077                    $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1078                    break;
1079
1080                case 'step':
1081                    $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1082                    break;
1083
1084                default:
1085                    $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1086                    break;
1087            }
1088        }
1089
1090        if (!isset($attrs['show']))
1091            $output .= "{$section_props}['show'] = true;\n";
1092
1093        if (!isset($attrs['loop']))
1094            $output .= "{$section_props}['loop'] = 1;\n";
1095
1096        if (!isset($attrs['max']))
1097            $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1098        else
1099            $output .= "if ({$section_props}['max'] < 0)\n" .
1100                       "    {$section_props}['max'] = {$section_props}['loop'];\n";
1101
1102        if (!isset($attrs['step']))
1103            $output .= "{$section_props}['step'] = 1;\n";
1104
1105        if (!isset($attrs['start']))
1106            $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1107        else {
1108            $output .= "if ({$section_props}['start'] < 0)\n" .
1109                       "    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1110                       "else\n" .
1111                       "    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1112        }
1113
1114        $output .= "if ({$section_props}['show']) {\n";
1115        if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1116            $output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
1117        } else {
1118            $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";
1119        }
1120        $output .= "    if ({$section_props}['total'] == 0)\n" .
1121                   "        {$section_props}['show'] = false;\n" .
1122                   "} else\n" .
1123                   "    {$section_props}['total'] = 0;\n";
1124
1125        $output .= "if ({$section_props}['show']):\n";
1126        $output .= "
1127            for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1128                 {$section_props}['iteration'] <= {$section_props}['total'];
1129                 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1130        $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1131        $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1132        $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1133        $output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
1134        $output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1135
1136        $output .= "?>";
1137
1138        return $output;
1139    }
1140
1141
1142    /**
1143     * Compile {foreach ...} tag.
1144     *
1145     * @param string $tag_args
1146     * @return string
1147     */
1148    function _compile_foreach_start($tag_args)
1149    {
1150        $attrs = $this->_parse_attrs($tag_args);
1151        $arg_list = array();
1152
1153        if (empty($attrs['from'])) {
1154            return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1155        }
1156        $from = $attrs['from'];
1157
1158        if (empty($attrs['item'])) {
1159            return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1160        }
1161        $item = $this->_dequote($attrs['item']);
1162        if (!preg_match('~^\w+$~', $item)) {
1163            return $this->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1164        }
1165
1166        if (isset($attrs['key'])) {
1167            $key  = $this->_dequote($attrs['key']);
1168            if (!preg_match('~^\w+$~', $key)) {
1169                return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1170            }
1171            $key_part = "\$this->_tpl_vars['$key'] => ";
1172        } else {
1173            $key = null;
1174            $key_part = '';
1175        }
1176
1177        if (isset($attrs['name'])) {
1178            $name = $attrs['name'];
1179        } else {
1180            $name = null;
1181        }
1182
1183        $output = '<?php ';
1184        $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1185        if (isset($name)) {
1186            $foreach_props = "\$this->_foreach[$name]";
1187            $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1188            $output .= "if ({$foreach_props}['total'] > 0):\n";
1189            $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1190            $output .= "        {$foreach_props}['iteration']++;\n";
1191        } else {
1192            $output .= "if (count(\$_from)):\n";
1193            $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1194        }
1195        $output .= '?>';
1196
1197        return $output;
1198    }
1199
1200
1201    /**
1202     * Compile {capture} .. {/capture} tags
1203     *
1204     * @param boolean $start true if this is the {capture} tag
1205     * @param string $tag_args
1206     * @return string
1207     */
1208
1209    function _compile_capture_tag($start, $tag_args = '')
1210    {
1211        $attrs = $this->_parse_attrs($tag_args);
1212
1213        if ($start) {
1214            if (isset($attrs['name']))
1215                $buffer = $attrs['name'];
1216            else
1217                $buffer = "'default'";
1218
1219            if (isset($attrs['assign']))
1220                $assign = $attrs['assign'];
1221            else
1222               

Large files files are truncated, but you can click here to view the full file