lib/smarty/Smarty_Compiler.class.php

00001 <?php
00002 
00029 /* $Id: Smarty_Compiler.class.php 3887 2007-04-27 18:23:18Z calguy1000 $ */
00030 
00035 class Smarty_Compiler extends Smarty {
00036 
00037     // internal vars
00041     var $_folded_blocks         =   array();    // keeps folded template blocks
00042     var $_current_file          =   null;       // the current template being compiled
00043     var $_current_line_no       =   1;          // line number for error messages
00044     var $_capture_stack         =   array();    // keeps track of nested capture buffers
00045     var $_plugin_info           =   array();    // keeps track of plugins to load
00046     var $_init_smarty_vars      =   false;
00047     var $_permitted_tokens      =   array('true','false','yes','no','on','off','null');
00048     var $_db_qstr_regexp        =   null;        // regexps are setup in the constructor
00049     var $_si_qstr_regexp        =   null;
00050     var $_qstr_regexp           =   null;
00051     var $_func_regexp           =   null;
00052     var $_reg_obj_regexp        =   null;
00053     var $_var_bracket_regexp    =   null;
00054     var $_num_const_regexp      =   null;
00055     var $_dvar_guts_regexp      =   null;
00056     var $_dvar_regexp           =   null;
00057     var $_cvar_regexp           =   null;
00058     var $_svar_regexp           =   null;
00059     var $_avar_regexp           =   null;
00060     var $_mod_regexp            =   null;
00061     var $_var_regexp            =   null;
00062     var $_parenth_param_regexp  =   null;
00063     var $_func_call_regexp      =   null;
00064     var $_obj_ext_regexp        =   null;
00065     var $_obj_start_regexp      =   null;
00066     var $_obj_params_regexp     =   null;
00067     var $_obj_call_regexp       =   null;
00068     var $_cacheable_state       =   0;
00069     var $_cache_attrs_count     =   0;
00070     var $_nocache_count         =   0;
00071     var $_cache_serial          =   null;
00072     var $_cache_include         =   null;
00073 
00074     var $_strip_depth           =   0;
00075     var $_additional_newline    =   "\n";
00076 
00081     function Smarty_Compiler()
00082     {
00083         // matches double quoted strings:
00084         // "foobar"
00085         // "foo\"bar"
00086         $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
00087 
00088         // matches single quoted strings:
00089         // 'foobar'
00090         // 'foo\'bar'
00091         $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
00092 
00093         // matches single or double quoted strings
00094         $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
00095 
00096         // matches bracket portion of vars
00097         // [0]
00098         // [foo]
00099         // [$bar]
00100         $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
00101 
00102         // matches numerical constants
00103         // 30
00104         // -12
00105         // 13.22
00106         $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
00107 
00108         // matches $ vars (not objects):
00109         // $foo
00110         // $foo.bar
00111         // $foo.bar.foobar
00112         // $foo[0]
00113         // $foo[$bar]
00114         // $foo[5][blah]
00115         // $foo[5].bar[$foobar][4]
00116         $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
00117         $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
00118         $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
00119                 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
00120         $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
00121 
00122         // matches config vars:
00123         // #foo#
00124         // #foobar123_foo#
00125         $this->_cvar_regexp = '\#\w+\#';
00126 
00127         // matches section vars:
00128         // %foo.bar%
00129         $this->_svar_regexp = '\%\w+\.\w+\%';
00130 
00131         // matches all valid variables (no quotes, no modifiers)
00132         $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
00133            . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
00134 
00135         // matches valid variable syntax:
00136         // $foo
00137         // $foo
00138         // #foo#
00139         // #foo#
00140         // "text"
00141         // "text"
00142         $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
00143 
00144         // matches valid object call (one level of object nesting allowed in parameters):
00145         // $foo->bar
00146         // $foo->bar()
00147         // $foo->bar("text")
00148         // $foo->bar($foo, $bar, "text")
00149         // $foo->bar($foo, "foo")
00150         // $foo->bar->foo()
00151         // $foo->bar->foo->bar()
00152         // $foo->bar($foo->bar)
00153         // $foo->bar($foo->bar())
00154         // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
00155         $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
00156         $this->_obj_restricted_param_regexp = '(?:'
00157                 . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
00158                 . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
00159         $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
00160                 . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
00161         $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
00162                 . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
00163         $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
00164         $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 . ')*)?)';
00165         
00166         // matches valid modifier syntax:
00167         // |foo
00168         // |@foo
00169         // |foo:"bar"
00170         // |foo:$bar
00171         // |foo:"bar":$foobar
00172         // |foo|bar
00173         // |foo:$foo->bar
00174         $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
00175            . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
00176 
00177         // matches valid function name:
00178         // foo123
00179         // _foo_bar
00180         $this->_func_regexp = '[a-zA-Z_]\w*';
00181 
00182         // matches valid registered object:
00183         // foo->bar
00184         $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
00185 
00186         // matches valid parameter values:
00187         // true
00188         // $foo
00189         // $foo|bar
00190         // #foo#
00191         // #foo#|bar
00192         // "text"
00193         // "text"|bar
00194         // $foo->bar
00195         $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
00196            . $this->_var_regexp . '|' . $this->_num_const_regexp  . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
00197 
00198         // matches valid parenthesised function parameters:
00199         //
00200         // "text"
00201         //    $foo, $bar, "text"
00202         // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
00203         $this->_parenth_param_regexp = '(?:\((?:\w+|'
00204                 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
00205                 . $this->_param_regexp . ')))*)?\))';
00206 
00207         // matches valid function call:
00208         // foo()
00209         // foo_bar($foo)
00210         // _foo_bar($foo,"bar")
00211         // foo123($foo,$foo->bar(),"foo")
00212         $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
00213            . $this->_parenth_param_regexp . '))';
00214     }
00215 
00225     function _compile_file($resource_name, $source_content, &$compiled_content)
00226     {
00227 
00228         if ($this->security) {
00229             // do not allow php syntax to be executed unless specified
00230             if ($this->php_handling == SMARTY_PHP_ALLOW &&
00231                 !$this->security_settings['PHP_HANDLING']) {
00232                 $this->php_handling = SMARTY_PHP_PASSTHRU;
00233             }
00234         }
00235 
00236         $this->_load_filters();
00237 
00238         $this->_current_file = $resource_name;
00239         $this->_current_line_no = 1;
00240         $ldq = preg_quote($this->left_delimiter, '~');
00241         $rdq = preg_quote($this->right_delimiter, '~');
00242 
00243         // run template source through prefilter functions
00244         if (count($this->_plugins['prefilter']) > 0) {
00245             foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
00246                 if ($prefilter === false) continue;
00247                 if ($prefilter[3] || is_callable($prefilter[0])) {
00248                     $source_content = call_user_func_array($prefilter[0],
00249                                                             array($source_content, &$this));
00250                     $this->_plugins['prefilter'][$filter_name][3] = true;
00251                 } else {
00252                     $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
00253                 }
00254             }
00255         }
00256 
00257         /* fetch all special blocks */
00258         $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
00259 
00260         preg_match_all($search, $source_content, $match,  PREG_SET_ORDER);
00261         $this->_folded_blocks = $match;
00262         reset($this->_folded_blocks);
00263 
00264         /* replace special blocks by "{php}" */
00265         $source_content = preg_replace($search.'e', "'"
00266                                        . $this->_quote_replace($this->left_delimiter) . 'php'
00267                                        . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
00268                                        . $this->_quote_replace($this->right_delimiter)
00269                                        . "'"
00270                                        , $source_content);
00271 
00272         /* Gather all template tags. */
00273         preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
00274         $template_tags = $_match[1];
00275         /* Split content by template tags to obtain non-template content. */
00276         $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
00277 
00278         /* loop through text blocks */
00279         for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
00280             /* match anything resembling php tags */
00281             if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?\s*php\s*[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
00282                 /* replace tags with placeholders to prevent recursive replacements */
00283                 $sp_match[1] = array_unique($sp_match[1]);
00284                 usort($sp_match[1], '_smarty_sort_length');
00285                 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
00286                     $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
00287                 }
00288                 /* process each one */
00289                 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
00290                     if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
00291                         /* echo php contents */
00292                         $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
00293                     } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
00294                         /* quote php tags */
00295                         $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
00296                     } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
00297                         /* remove php tags */
00298                         $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
00299                     } else {
00300                         /* SMARTY_PHP_ALLOW, but echo non php starting tags */
00301                         $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
00302                         $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
00303                     }
00304                 }
00305             }
00306         }
00307         
00308         /* Compile the template tags into PHP code. */
00309         $compiled_tags = array();
00310         for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
00311             $this->_current_line_no += substr_count($text_blocks[$i], "\n");
00312             $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
00313             $this->_current_line_no += substr_count($template_tags[$i], "\n");
00314         }
00315         if (count($this->_tag_stack)>0) {
00316             list($_open_tag, $_line_no) = end($this->_tag_stack);
00317             $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
00318             return;
00319         }
00320 
00321         /* Reformat $text_blocks between 'strip' and '/strip' tags,
00322            removing spaces, tabs and newlines. */
00323         $strip = false;
00324         for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
00325             if ($compiled_tags[$i] == '{strip}') {
00326                 $compiled_tags[$i] = '';
00327                 $strip = true;
00328                 /* remove leading whitespaces */
00329                 $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
00330             }
00331             if ($strip) {
00332                 /* strip all $text_blocks before the next '/strip' */
00333                 for ($j = $i + 1; $j < $for_max; $j++) {
00334                     /* remove leading and trailing whitespaces of each line */
00335                     $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
00336                     if ($compiled_tags[$j] == '{/strip}') {                       
00337                         /* remove trailing whitespaces from the last text_block */
00338                         $text_blocks[$j] = rtrim($text_blocks[$j]);
00339                     }
00340                     $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
00341                     if ($compiled_tags[$j] == '{/strip}') {
00342                         $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
00343                                     if a newline is following the closing strip-tag */
00344                         $strip = false;
00345                         $i = $j;
00346                         break;
00347                     }
00348                 }
00349             }
00350         }
00351         $compiled_content = '';
00352         
00353         $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
00354         
00355         /* Interleave the compiled contents and text blocks to get the final result. */
00356         for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
00357             if ($compiled_tags[$i] == '') {
00358                 // tag result empty, remove first newline from following text block
00359                 $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
00360             }
00361             // replace legit PHP tags with placeholder
00362             $text_blocks[$i] = str_replace('<?', $tag_guard, $text_blocks[$i]);
00363             $compiled_tags[$i] = str_replace('<?', $tag_guard, $compiled_tags[$i]);
00364             
00365             $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
00366         }
00367         $compiled_content .= str_replace('<?', $tag_guard, $text_blocks[$i]);
00368 
00369         // escape php tags created by interleaving
00370         $compiled_content = str_replace('<?', "<?php echo '<?' ?>\n", $compiled_content);
00371         $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\n", $compiled_content);
00372 
00373         // recover legit tags
00374         $compiled_content = str_replace($tag_guard, '<?', $compiled_content); 
00375         
00376         // remove \n from the end of the file, if any
00377         if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
00378             $compiled_content = substr($compiled_content, 0, -1);
00379         }
00380 
00381         if (!empty($this->_cache_serial)) {
00382             $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
00383         }
00384 
00385         // run compiled template through postfilter functions
00386         if (count($this->_plugins['postfilter']) > 0) {
00387             foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
00388                 if ($postfilter === false) continue;
00389                 if ($postfilter[3] || is_callable($postfilter[0])) {
00390                     $compiled_content = call_user_func_array($postfilter[0],
00391                                                               array($compiled_content, &$this));
00392                     $this->_plugins['postfilter'][$filter_name][3] = true;
00393                 } else {
00394                     $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
00395                 }
00396             }
00397         }
00398 
00399         // put header at the top of the compiled template
00400         $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
00401         $template_header .= "         compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
00402 
00403         /* Emit code to load needed plugins. */
00404         $this->_plugins_code = '';
00405         if (count($this->_plugin_info)) {
00406             $_plugins_params = "array('plugins' => array(";
00407             foreach ($this->_plugin_info as $plugin_type => $plugins) {
00408                 foreach ($plugins as $plugin_name => $plugin_info) {
00409                     $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
00410                     $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
00411                 }
00412             }
00413             $_plugins_params .= '))';
00414             $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
00415             $template_header .= $plugins_code;
00416             $this->_plugin_info = array();
00417             $this->_plugins_code = $plugins_code;
00418         }
00419 
00420         if ($this->_init_smarty_vars) {
00421             $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
00422             $this->_init_smarty_vars = false;
00423         }
00424 
00425         $compiled_content = $template_header . $compiled_content;
00426         return true;
00427     }
00428 
00435     function _compile_tag($template_tag)
00436     {
00437         /* Matched comment. */
00438         if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
00439             return '';
00440         
00441         /* Split tag into two three parts: command, command modifiers and the arguments. */
00442         if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
00443                 . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
00444                       (?:\s+(.*))?$
00445                     ~xs', $template_tag, $match)) {
00446             $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
00447         }
00448         
00449         $tag_command = $match[1];
00450         $tag_modifier = isset($match[2]) ? $match[2] : null;
00451         $tag_args = isset($match[3]) ? $match[3] : null;
00452 
00453         if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
00454             /* tag name is a variable or object */
00455             $_return = $this->_parse_var_props($tag_command . $tag_modifier);
00456             return "<?php echo $_return; ?>" . $this->_additional_newline;
00457         }
00458 
00459         /* If the tag name is a registered object, we process it. */
00460         if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
00461             return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
00462         }
00463 
00464         switch ($tag_command) {
00465             case 'include':
00466                 return $this->_compile_include_tag($tag_args);
00467 
00468             case 'include_php':
00469                 return $this->_compile_include_php_tag($tag_args);
00470 
00471             case 'if':
00472                 $this->_push_tag('if');
00473                 return $this->_compile_if_tag($tag_args);
00474 
00475             case 'else':
00476                 list($_open_tag) = end($this->_tag_stack);
00477                 if ($_open_tag != 'if' && $_open_tag != 'elseif')
00478                     $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
00479                 else
00480                     $this->_push_tag('else');
00481                 return '<?php else: ?>';
00482 
00483             case 'elseif':
00484                 list($_open_tag) = end($this->_tag_stack);
00485                 if ($_open_tag != 'if' && $_open_tag != 'elseif')
00486                     $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
00487                 if ($_open_tag == 'if')
00488                     $this->_push_tag('elseif');
00489                 return $this->_compile_if_tag($tag_args, true);
00490 
00491             case '/if':
00492                 $this->_pop_tag('if');
00493                 return '<?php endif; ?>';
00494 
00495             case 'capture':
00496                 return $this->_compile_capture_tag(true, $tag_args);
00497 
00498             case '/capture':
00499                 return $this->_compile_capture_tag(false);
00500 
00501             case 'ldelim':
00502                 return $this->left_delimiter;
00503 
00504             case 'rdelim':
00505                 return $this->right_delimiter;
00506 
00507             case 'section':
00508                 $this->_push_tag('section');
00509                 return $this->_compile_section_start($tag_args);
00510 
00511             case 'sectionelse':
00512                 $this->_push_tag('sectionelse');
00513                 return "<?php endfor; else: ?>";
00514                 break;
00515 
00516             case '/section':
00517                 $_open_tag = $this->_pop_tag('section');
00518                 if ($_open_tag == 'sectionelse')
00519                     return "<?php endif; ?>";
00520                 else
00521                     return "<?php endfor; endif; ?>";
00522 
00523             case 'foreach':
00524                 $this->_push_tag('foreach');
00525                 return $this->_compile_foreach_start($tag_args);
00526                 break;
00527 
00528             case 'foreachelse':
00529                 $this->_push_tag('foreachelse');
00530                 return "<?php endforeach; else: ?>";
00531 
00532             case '/foreach':
00533                 $_open_tag = $this->_pop_tag('foreach');
00534                 if ($_open_tag == 'foreachelse')
00535                     return "<?php endif; unset(\$_from); ?>";
00536                 else
00537                     return "<?php endforeach; endif; unset(\$_from); ?>";
00538                 break;
00539 
00540             case 'strip':
00541             case '/strip':
00542                 if (substr($tag_command, 0, 1)=='/') {
00543                     $this->_pop_tag('strip');
00544                     if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
00545                         $this->_additional_newline = "\n";
00546                         return '{' . $tag_command . '}';
00547                     }
00548                 } else {
00549                     $this->_push_tag('strip');
00550                     if ($this->_strip_depth++==0) { /* outermost opening {strip} */
00551                         $this->_additional_newline = "";
00552                         return '{' . $tag_command . '}';
00553                     }
00554                 }
00555                 return '';
00556 
00557             case 'php':
00558                 /* handle folded tags replaced by {php} */
00559                 list(, $block) = each($this->_folded_blocks);
00560                 $this->_current_line_no += substr_count($block[0], "\n");
00561                 /* the number of matched elements in the regexp in _compile_file()
00562                    determins the type of folded tag that was found */
00563                 switch (count($block)) {
00564                     case 2: /* comment */
00565                         return '';
00566 
00567                     case 3: /* literal */
00568                         return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
00569 
00570                     case 4: /* php */
00571                         if ($this->security && !$this->security_settings['PHP_TAGS']) {
00572                             $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
00573                             return;
00574                         }
00575                         return '<?php ' . $block[3] .' ?>';
00576                 }
00577                 break;
00578 
00579             case 'insert':
00580                 return $this->_compile_insert_tag($tag_args);
00581 
00582             default:
00583                 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
00584                     return $output;
00585                 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
00586                     return $output;
00587                 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
00588                     return $output;                    
00589                 } else {
00590                     $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
00591                 }
00592 
00593         }
00594     }
00595 
00596 
00606     function _compile_compiler_tag($tag_command, $tag_args, &$output)
00607     {
00608         $found = false;
00609         $have_function = true;
00610 
00611         /*
00612          * First we check if the compiler function has already been registered
00613          * or loaded from a plugin file.
00614          */
00615         if (isset($this->_plugins['compiler'][$tag_command])) {
00616             $found = true;
00617             $plugin_func = $this->_plugins['compiler'][$tag_command][0];
00618             if (!is_callable($plugin_func)) {
00619                 $message = "compiler function '$tag_command' is not implemented";
00620                 $have_function = false;
00621             }
00622         }
00623         /*
00624          * Otherwise we need to load plugin file and look for the function
00625          * inside it.
00626          */
00627         else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
00628             $found = true;
00629 
00630             include_once $plugin_file;
00631 
00632             $plugin_func = 'smarty_compiler_' . $tag_command;
00633             if (!is_callable($plugin_func)) {
00634                 $message = "plugin function $plugin_func() not found in $plugin_file\n";
00635                 $have_function = false;
00636             } else {
00637                 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
00638             }
00639         }
00640 
00641         /*
00642          * True return value means that we either found a plugin or a
00643          * dynamically registered function. False means that we didn't and the
00644          * compiler should now emit code to load custom function plugin for this
00645          * tag.
00646          */
00647         if ($found) {
00648             if ($have_function) {
00649                 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
00650                 if($output != '') {
00651                 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
00652                                    . $output
00653                                    . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
00654                 }
00655             } else {
00656                 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
00657             }
00658             return true;
00659         } else {
00660             return false;
00661         }
00662     }
00663 
00664 
00675     function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
00676     {
00677         if (substr($tag_command, 0, 1) == '/') {
00678             $start_tag = false;
00679             $tag_command = substr($tag_command, 1);
00680         } else
00681             $start_tag = true;
00682 
00683         $found = false;
00684         $have_function = true;
00685 
00686         /*
00687          * First we check if the block function has already been registered
00688          * or loaded from a plugin file.
00689          */
00690         if (isset($this->_plugins['block'][$tag_command])) {
00691             $found = true;
00692             $plugin_func = $this->_plugins['block'][$tag_command][0];
00693             if (!is_callable($plugin_func)) {
00694                 $message = "block function '$tag_command' is not implemented";
00695                 $have_function = false;
00696             }
00697         }
00698         /*
00699          * Otherwise we need to load plugin file and look for the function
00700          * inside it.
00701          */
00702         else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
00703             $found = true;
00704 
00705             include_once $plugin_file;
00706 
00707             $plugin_func = 'smarty_block_' . $tag_command;
00708             if (!function_exists($plugin_func)) {
00709                 $message = "plugin function $plugin_func() not found in $plugin_file\n";
00710                 $have_function = false;
00711             } else {
00712                 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
00713 
00714             }
00715         }
00716 
00717         if (!$found) {
00718             return false;
00719         } else if (!$have_function) {
00720             $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
00721             return true;
00722         }
00723 
00724         /*
00725          * Even though we've located the plugin function, compilation
00726          * happens only once, so the plugin will still need to be loaded
00727          * at runtime for future requests.
00728          */
00729         $this->_add_plugin('block', $tag_command);
00730 
00731         if ($start_tag)
00732             $this->_push_tag($tag_command);
00733         else
00734             $this->_pop_tag($tag_command);
00735 
00736         if ($start_tag) {
00737             $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
00738             $attrs = $this->_parse_attrs($tag_args);
00739             $_cache_attrs='';
00740             $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
00741             $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
00742             $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
00743             $output .= 'while ($_block_repeat) { ob_start(); ?>';
00744         } else {
00745             $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
00746             $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
00747             if ($tag_modifier != '') {
00748                 $this->_parse_modifiers($_out_tag_text, $tag_modifier);
00749             }
00750             $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
00751             $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
00752         }
00753 
00754         return true;
00755     }
00756 
00757 
00766     function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
00767     {
00768         $found = false;
00769         $have_function = true;
00770 
00771         /*
00772          * First we check if the custom function has already been registered
00773          * or loaded from a plugin file.
00774          */
00775         if (isset($this->_plugins['function'][$tag_command])) {
00776             $found = true;
00777             $plugin_func = $this->_plugins['function'][$tag_command][0];
00778             if (!is_callable($plugin_func)) {
00779                 $message = "custom function '$tag_command' is not implemented";
00780                 $have_function = false;
00781             }
00782         }
00783         /*
00784          * Otherwise we need to load plugin file and look for the function
00785          * inside it.
00786          */
00787         else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
00788             $found = true;
00789 
00790             include_once $plugin_file;
00791 
00792             $plugin_func = 'smarty_function_' . $tag_command;
00793             if (!function_exists($plugin_func)) {
00794                 $message = "plugin function $plugin_func() not found in $plugin_file\n";
00795                 $have_function = false;
00796             } else {
00797                 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
00798 
00799             }
00800         }
00801 
00802         if (!$found) {
00803             return false;
00804         } else if (!$have_function) {
00805             $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
00806             return true;
00807         }
00808 
00809         /* declare plugin to be loaded on display of the template that
00810            we compile right now */
00811         $this->_add_plugin('function', $tag_command);
00812 
00813         $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
00814         $attrs = $this->_parse_attrs($tag_args);
00815         $_cache_attrs = '';
00816         $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
00817 
00818         $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
00819         if($tag_modifier != '') {
00820             $this->_parse_modifiers($output, $tag_modifier);
00821         }
00822 
00823         if($output != '') {
00824             $output =  '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
00825                 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
00826         }
00827 
00828         return true;
00829     }
00830 
00839     function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
00840     {
00841         if (substr($tag_command, 0, 1) == '/') {
00842             $start_tag = false;
00843             $tag_command = substr($tag_command, 1);
00844         } else {
00845             $start_tag = true;
00846         }
00847 
00848         list($object, $obj_comp) = explode('->', $tag_command);
00849 
00850         $arg_list = array();
00851         if(count($attrs)) {
00852             $_assign_var = false;
00853             foreach ($attrs as $arg_name => $arg_value) {
00854                 if($arg_name == 'assign') {
00855                     $_assign_var = $arg_value;
00856                     unset($attrs['assign']);
00857                     continue;
00858                 }
00859                 if (is_bool($arg_value))
00860                     $arg_value = $arg_value ? 'true' : 'false';
00861                 $arg_list[] = "'$arg_name' => $arg_value";
00862             }
00863         }
00864 
00865         if($this->_reg_objects[$object][2]) {
00866             // smarty object argument format
00867             $args = "array(".implode(',', (array)$arg_list)."), \$this";
00868         } else {
00869             // traditional argument format
00870             $args = implode(',', array_values($attrs));
00871             if (empty($args)) {
00872                 $args = 'null';
00873             }
00874         }
00875 
00876         $prefix = '';
00877         $postfix = '';
00878         $newline = '';
00879         if(!is_object($this->_reg_objects[$object][0])) {
00880             $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
00881         } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
00882             $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
00883         } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
00884             // method
00885             if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
00886                 // block method
00887                 if ($start_tag) {
00888                     $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
00889                     $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
00890                     $prefix .= "while (\$_block_repeat) { ob_start();";
00891                     $return = null;
00892                     $postfix = '';
00893                 } else {
00894                     $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
00895                     $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
00896                     $postfix = "} array_pop(\$this->_tag_stack);";
00897                 }
00898             } else {
00899                 // non-block method
00900                 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
00901             }
00902         } else {
00903             // property
00904             $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
00905         }
00906 
00907         if($return != null) {
00908             if($tag_modifier != '') {
00909                 $this->_parse_modifiers($return, $tag_modifier);
00910             }
00911 
00912             if(!empty($_assign_var)) {
00913                 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."',  $return);";
00914             } else {
00915                 $output = 'echo ' . $return . ';';
00916                 $newline = $this->_additional_newline;
00917             }
00918         } else {
00919             $output = '';
00920         }
00921 
00922         return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
00923     }
00924 
00931     function _compile_insert_tag($tag_args)
00932     {
00933         $attrs = $this->_parse_attrs($tag_args);
00934         $name = $this->_dequote($attrs['name']);
00935 
00936         if (empty($name)) {
00937             return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
00938         }
00939         
00940         if (!preg_match('~^\w+$~', $name)) {
00941             return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__);
00942         }
00943 
00944         if (!empty($attrs['script'])) {
00945             $delayed_loading = true;
00946         } else {
00947             $delayed_loading = false;
00948         }
00949 
00950         foreach ($attrs as $arg_name => $arg_value) {
00951             if (is_bool($arg_value))
00952                 $arg_value = $arg_value ? 'true' : 'false';
00953             $arg_list[] = "'$arg_name' => $arg_value";
00954         }
00955 
00956         $this->_add_plugin('insert', $name, $delayed_loading);
00957 
00958         $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
00959 
00960         return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
00961     }
00962 
00969     function _compile_include_tag($tag_args)
00970     {
00971         $attrs = $this->_parse_attrs($tag_args);
00972         $arg_list = array();
00973 
00974         if (empty($attrs['file'])) {
00975             $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
00976         }
00977 
00978         foreach ($attrs as $arg_name => $arg_value) {
00979             if ($arg_name == 'file') {
00980                 $include_file = $arg_value;
00981                 continue;
00982             } else if ($arg_name == 'assign') {
00983                 $assign_var = $arg_value;
00984                 continue;
00985             }
00986             if (is_bool($arg_value))
00987                 $arg_value = $arg_value ? 'true' : 'false';
00988             $arg_list[] = "'$arg_name' => $arg_value";
00989         }
00990 
00991         $output = '<?php ';
00992 
00993         if (isset($assign_var)) {
00994             $output .= "ob_start();\n";
00995         }
00996 
00997         $output .=
00998             "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
00999 
01000 
01001         $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
01002         $output .= "\$this->_smarty_include($_params);\n" .
01003         "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
01004         "unset(\$_smarty_tpl_vars);\n";
01005 
01006         if (isset($assign_var)) {
01007             $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
01008         }
01009 
01010         $output .= ' ?>';
01011 
01012         return $output;
01013 
01014     }
01015 
01022     function _compile_include_php_tag($tag_args)
01023     {
01024         $attrs = $this->_parse_attrs($tag_args);
01025 
01026         if (empty($attrs['file'])) {
01027             $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
01028         }
01029 
01030         $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
01031         $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
01032 
01033         $arg_list = array();
01034         foreach($attrs as $arg_name => $arg_value) {
01035             if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
01036                 if(is_bool($arg_value))
01037                     $arg_value = $arg_value ? 'true' : 'false';
01038                 $arg_list[] = "'$arg_name' => $arg_value";
01039             }
01040         }
01041 
01042         $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
01043 
01044         return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
01045     }
01046 
01047 
01054     function _compile_section_start($tag_args)
01055     {
01056         $attrs = $this->_parse_attrs($tag_args);
01057         $arg_list = array();
01058 
01059         $output = '<?php ';
01060         $section_name = $attrs['name'];
01061         if (empty($section_name)) {
01062             $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
01063         }
01064 
01065         $output .= "unset(\$this->_sections[$section_name]);\n";
01066         $section_props = "\$this->_sections[$section_name]";
01067 
01068         foreach ($attrs as $attr_name => $attr_value) {
01069             switch ($attr_name) {
01070                 case 'loop':
01071                     $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
01072                     break;
01073 
01074                 case 'show':
01075                     if (is_bool($attr_value))
01076                         $show_attr_value = $attr_value ? 'true' : 'false';
01077                     else
01078                         $show_attr_value = "(bool)$attr_value";
01079                     $output .= "{$section_props}['show'] = $show_attr_value;\n";
01080                     break;
01081 
01082                 case 'name':
01083                     $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
01084                     break;
01085 
01086                 case 'max':
01087                 case 'start':
01088                     $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
01089                     break;
01090 
01091                 case 'step':
01092                     $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
01093                     break;
01094 
01095                 default:
01096                     $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
01097                     break;
01098             }
01099         }
01100 
01101         if (!isset($attrs['show']))
01102             $output .= "{$section_props}['show'] = true;\n";
01103 
01104         if (!isset($attrs['loop']))
01105             $output .= "{$section_props}['loop'] = 1;\n";
01106 
01107         if (!isset($attrs['max']))
01108             $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
01109         else
01110             $output .= "if ({$section_props}['max'] < 0)\n" .
01111                        "    {$section_props}['max'] = {$section_props}['loop'];\n";
01112 
01113         if (!isset($attrs['step']))
01114             $output .= "{$section_props}['step'] = 1;\n";
01115 
01116         if (!isset($attrs['start']))
01117             $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
01118         else {
01119             $output .= "if ({$section_props}['start'] < 0)\n" .
01120                        "    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
01121                        "else\n" .
01122                        "    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
01123         }
01124 
01125         $output .= "if ({$section_props}['show']) {\n";
01126         if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
01127             $output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
01128         } else {
01129             $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";
01130         }
01131         $output .= "    if ({$section_props}['total'] == 0)\n" .
01132                    "        {$section_props}['show'] = false;\n" .
01133                    "} else\n" .
01134                    "    {$section_props}['total'] = 0;\n";
01135 
01136         $output .= "if ({$section_props}['show']):\n";
01137         $output .= "
01138             for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
01139                  {$section_props}['iteration'] <= {$section_props}['total'];
01140                  {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
01141         $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
01142         $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
01143         $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
01144         $output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
01145         $output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
01146 
01147         $output .= "?>";
01148 
01149         return $output;
01150     }
01151 
01152 
01159     function _compile_foreach_start($tag_args)
01160     {
01161         $attrs = $this->_parse_attrs($tag_args);
01162         $arg_list = array();
01163 
01164         if (empty($attrs['from'])) {
01165             return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
01166         }
01167         $from = $attrs['from'];
01168 
01169         if (empty($attrs['item'])) {
01170             return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
01171         }
01172         $item = $this->_dequote($attrs['item']);
01173         if (!preg_match('~^\w+$~', $item)) {
01174             return $this->_syntax_error("'foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
01175         }
01176 
01177         if (isset($attrs['key'])) {
01178             $key  = $this->_dequote($attrs['key']);
01179             if (!preg_match('~^\w+$~', $key)) {
01180                 return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
01181             }
01182             $key_part = "\$this->_tpl_vars['$key'] => ";
01183         } else {
01184             $key = null;
01185             $key_part = '';
01186         }
01187 
01188         if (isset($attrs['name'])) {
01189             $name = $attrs['name'];
01190         } else {
01191             $name = null;
01192         }
01193 
01194         $output = '<?php ';
01195         $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
01196         if (isset($name)) {
01197             $foreach_props = "\$this->_foreach[$name]";
01198             $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
01199             $output .= "if ({$foreach_props}['total'] > 0):\n";
01200             $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
01201             $output .= "        {$foreach_props}['iteration']++;\n";
01202         } else {
01203             $output .= "if (count(\$_from)):\n";
01204             $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
01205         }
01206         $output .= '?>';
01207 
01208         return $output;
01209     }
01210 
01211 
01220     function _compile_capture_tag($start, $tag_args = '')
01221     {
01222         $attrs = $this->_parse_attrs($tag_args);
01223 
01224         if ($start) {
01225             if (isset($attrs['name']))
01226                 $buffer = $attrs['name'];
01227             else
01228                 $buffer = "'default'";
01229 
01230             if (isset($attrs['assign']))
01231                 $assign = $attrs['assign'];
01232             else
01233                 $assign = null;
01234             $output = "<?php ob_start(); ?>";
01235             $this->_capture_stack[] = array($buffer, $assign);
01236         } else {
01237             list($buffer, $assign) = array_pop($this->_capture_stack);
01238             $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
01239             if (isset($assign)) {
01240                 $output .= " \$this->assign($assign, ob_get_contents());";
01241             }
01242             $output .= "ob_end_clean(); ?>";
01243         }
01244 
01245         return $output;
01246     }
01247 
01255     function _compile_if_tag($tag_args, $elseif = false)
01256     {
01257 
01258         /* Tokenize args for 'if' tag. */
01259         preg_match_all('~(?>
01260                 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
01261                 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
01262                 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@    | # valid non-word token
01263                 \b\w+\b                                                        | # valid word token
01264                 \S+                                                           # anything else
01265                 )~x', $tag_args, $match);
01266 
01267         $tokens = $match[0];
01268 
01269         if(empty($tokens)) {
01270             $_error_msg = $elseif ? "'elseif'" : "'if'";
01271             $_error_msg .= ' statement requires arguments'; 
01272             $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
01273         }
01274             
01275                 
01276         // make sure we have balanced parenthesis
01277         $token_count = array_count_values($tokens);
01278         if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
01279             $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
01280         }
01281 
01282         $is_arg_stack = array();
01283 
01284         for ($i = 0; $i < count($tokens); $i++) {
01285 
01286             $token = &$tokens[$i];
01287 
01288             switch (strtolower($token)) {
01289                 case '!':
01290                 case '%':
01291                 case '!==':
01292                 case '==':
01293                 case '===':
01294                 case '>':
01295                 case '<':
01296                 case '!=':
01297                 case '<>':
01298                 case '<<':
01299                 case '>>':
01300                 case '<=':
01301                 case '>=':
01302                 case '&&':
01303                 case '||':
01304                 case '|':
01305                 case '^':
01306                 case '&':
01307                 case '~':
01308                 case ')':
01309                 case ',':
01310                 case '+':
01311                 case '-':
01312                 case '*':
01313                 case '/':
01314                 case '@':
01315                     break;
01316 
01317                 case 'eq':
01318                     $token = '==';
01319                     break;
01320 
01321                 case 'ne':
01322                 case 'neq':
01323                     $token = '!=';
01324                     break;
01325 
01326                 case 'lt':
01327                     $token = '<';
01328                     break;
01329 
01330                 case 'le':
01331                 case 'lte':
01332                     $token = '<=';
01333                     break;
01334 
01335                 case 'gt':
01336                     $token = '>';
01337                     break;
01338 
01339                 case 'ge':
01340                 case 'gte':
01341                     $token = '>=';
01342                     break;
01343 
01344                 case 'and':
01345                     $token = '&&';
01346                     break;
01347 
01348                 case 'or':
01349                     $token = '||';
01350                     break;
01351 
01352                 case 'not':
01353                     $token = '!';
01354                     break;
01355 
01356                 case 'mod':
01357                     $token = '%';
01358                     break;
01359 
01360                 case '(':
01361                     array_push($is_arg_stack, $i);
01362                     break;
01363 
01364                 case 'is':
01365                     /* If last token was a ')', we operate on the parenthesized
01366                        expression. The start of the expression is on the stack.
01367                        Otherwise, we operate on the last encountered token. */
01368                     if ($tokens[$i-1] == ')')
01369                         $is_arg_start = array_pop($is_arg_stack);
01370                     else
01371                         $is_arg_start = $i-1;
01372                     /* Construct the argument for 'is' expression, so it knows
01373                        what to operate on. */
01374                     $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
01375 
01376                     /* Pass all tokens from next one until the end to the
01377                        'is' expression parsing function. The function will
01378                        return modified tokens, where the first one is the result
01379                        of the 'is' expression and the rest are the tokens it
01380                        didn't touch. */
01381                     $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
01382 
01383                     /* Replace the old tokens with the new ones. */
01384                     array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
01385 
01386                     /* Adjust argument start so that it won't change from the
01387                        current position for the next iteration. */
01388                     $i = $is_arg_start;
01389                     break;
01390 
01391                 default:
01392                     if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
01393                             // function call
01394                             if($this->security &&
01395                                !in_array($token, $this->security_settings['IF_FUNCS'])) {
01396                                 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
01397                             }
01398                     } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
01399                         // variable function call
01400                         $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);                      
01401                     } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
01402                         // object or variable
01403                         $token = $this->_parse_var_props($token);
01404                     } elseif(is_numeric($token)) {
01405                         // number, skip it
01406                     } else {
01407                         $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
01408                     }
01409                     break;
01410             }
01411         }
01412 
01413         if ($elseif)
01414             return '<?php elseif ('.implode(' ', $tokens).'): ?>';
01415         else
01416             return '<?php if ('.implode(' ', $tokens).'): ?>';
01417     }
01418 
01419 
01420     function _compile_arg_list($type, $name, $attrs, &$cache_code) {
01421         $arg_list = array();
01422 
01423         if (isset($type) && isset($name)
01424             && isset($this->_plugins[$type])
01425             && isset($this->_plugins[$type][$name])
01426             && empty($this->_plugins[$type][$name][4])
01427             && is_array($this->_plugins[$type][$name][5])
01428             ) {
01429             /* we have a list of parameters that should be cached */
01430             $_cache_attrs = $this->_plugins[$type][$name][5];
01431             $_count = $this->_cache_attrs_count++;
01432             $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
01433 
01434         } else {
01435             /* no parameters are cached */
01436             $_cache_attrs = null;
01437         }
01438 
01439         foreach ($attrs as $arg_name => $arg_value) {
01440             if (is_bool($arg_value))
01441                 $arg_value = $arg_value ? 'true' : 'false';
01442             if (is_null($arg_value))
01443                 $arg_value = 'null';
01444             if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
01445                 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
01446             } else {
01447                 $arg_list[] = "'$arg_name' => $arg_value";
01448             }
01449         }
01450         return $arg_list;
01451     }
01452 
01460     function _parse_is_expr($is_arg, $tokens)
01461     {
01462         $expr_end = 0;
01463         $negate_expr = false;
01464 
01465         if (($first_token = array_shift($tokens)) == 'not') {
01466             $negate_expr = true;
01467             $expr_type = array_shift($tokens);
01468         } else
01469             $expr_type = $first_token;
01470 
01471         switch ($expr_type) {
01472             case 'even':
01473                 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
01474                     $expr_end++;
01475                     $expr_arg = $tokens[$expr_end++];
01476                     $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
01477                 } else
01478                     $expr = "!(1 & $is_arg)";
01479                 break;
01480 
01481             case 'odd':
01482                 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
01483                     $expr_end++;
01484                     $expr_arg = $tokens[$expr_end++];
01485                     $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
01486                 } else
01487                     $expr = "(1 & $is_arg)";
01488                 break;
01489 
01490             case 'div':
01491                 if (@$tokens[$expr_end] == 'by') {
01492                     $expr_end++;
01493                     $expr_arg = $tokens[$expr_end++];
01494                     $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
01495                 } else {
01496                     $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
01497                 }
01498                 break;
01499 
01500             default:
01501                 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
01502                 break;
01503         }
01504 
01505         if ($negate_expr) {
01506             $expr = "!($expr)";
01507         }
01508 
01509         array_splice($tokens, 0, $expr_end, $expr);
01510 
01511         return $tokens;
01512     }
01513 
01514 
01521     function _parse_attrs($tag_args)
01522     {
01523 
01524         /* Tokenize tag attributes. */
01525         preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
01526                          )+ |
01527                          [=]
01528                         ~x', $tag_args, $match);
01529         $tokens       = $match[0];
01530 
01531         $attrs = array();
01532         /* Parse state:
01533             0 - expecting attribute name
01534             1 - expecting '='
01535             2 - expecting attribute value (not '=') */
01536         $state = 0;
01537 
01538         foreach ($tokens as $token) {
01539             switch ($state) {
01540                 case 0:
01541                     /* If the token is a valid identifier, we set attribute name
01542                        and go to state 1. */
01543                     if (preg_match('~^\w+$~', $token)) {
01544                         $attr_name = $token;
01545                         $state = 1;
01546                     } else
01547                         $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
01548                     break;
01549 
01550                 case 1:
01551                     /* If the token is '=', then we go to state 2. */
01552                     if ($token == '=') {
01553                         $state = 2;
01554                     } else
01555                         $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
01556                     break;
01557 
01558                 case 2:
01559                     /* If token is not '=', we set the attribute value and go to
01560                        state 0. */
01561                     if ($token != '=') {
01562                         /* We booleanize the token if it's a non-quoted possible
01563                            boolean value. */
01564                         if (preg_match('~^(on|yes|true)$~', $token)) {
01565                             $token = 'true';
01566                         } else if (preg_match('~^(off|no|false)$~', $token)) {
01567                             $token = 'false';
01568                         } else if ($token == 'null') {
01569                             $token = 'null';
01570                         } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
01571                             /* treat integer literally */
01572                         } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
01573                             /* treat as a string, double-quote it escaping quotes */
01574                             $token = '"'.addslashes($token).'"';
01575                         }
01576 
01577                         $attrs[$attr_name] = $token;
01578                         $state = 0;
01579                     } else
01580                         $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
01581                     break;
01582             }
01583             $last_token = $token;
01584         }
01585 
01586         if($state != 0) {
01587             if($state == 1) {
01588                 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
01589             } else {
01590                 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
01591             }
01592         }
01593 
01594         $this->_parse_vars_props($attrs);
01595 
01596         return $attrs;
01597     }
01598 
01605     function _parse_vars_props(&$tokens)
01606     {
01607         foreach($tokens as $key => $val) {
01608             $tokens[$key] = $this->_parse_var_props($val);
01609         }
01610     }
01611 
01620     function _parse_var_props($val)
01621     {
01622         $val = trim($val);
01623 
01624         if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
01625             // $ variable or object
01626             $return = $this->_parse_var($match[1]);
01627             $modifiers = $match[2];
01628             if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
01629                 $_default_mod_string = implode('|',(array)$this->default_modifiers);
01630                 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
01631             }
01632             $this->_parse_modifiers($return, $modifiers);
01633             return $return;
01634         } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
01635                 // double quoted text
01636                 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
01637                 $return = $this->_expand_quoted_text($match[1]);
01638                 if($match[2] != '') {
01639                     $this->_parse_modifiers($return, $match[2]);
01640                 }
01641                 return $return;
01642             }
01643         elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
01644                 // numerical constant
01645                 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
01646                 if($match[2] != '') {
01647                     $this->_parse_modifiers($match[1], $match[2]);
01648                     return $match[1];
01649                 }
01650             }
01651         elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
01652                 // single quoted text
01653                 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
01654                 if($match[2] != '') {
01655                     $this->_parse_modifiers($match[1], $match[2]);
01656                     return $match[1];
01657                 }
01658             }
01659         elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
01660                 // config var
01661                 return $this->_parse_conf_var($val);
01662             }
01663         elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
01664                 // section var
01665                 return $this->_parse_section_prop($val);
01666             }
01667         elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
01668             // literal string
01669             return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
01670         }
01671         return $val;
01672     }
01673 
01680     function _expand_quoted_text($var_expr)
01681     {
01682         // if contains unescaped $, expand it
01683         if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
01684             $_match = $_match[0];
01685             $_replace = array();
01686             foreach($_match as $_var) {
01687                 $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
01688             }
01689             $var_expr = strtr($var_expr, $_replace);
01690             $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
01691         } else {
01692             $_return = $var_expr;
01693         }
01694         // replace double quoted literal string with single quotes
01695         $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
01696         return $_return;
01697     }
01698 
01706     function _parse_var($var_expr)
01707     {
01708         $_has_math = false;
01709         $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
01710 
01711         if(count($_math_vars) > 1) {
01712             $_first_var = "";
01713             $_complete_var = "";
01714             $_output = "";
01715             // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
01716             foreach($_math_vars as $_k => $_math_var) {
01717                 $_math_var = $_math_vars[$_k];
01718 
01719                 if(!empty($_math_var) || is_numeric($_math_var)) {
01720                     // hit a math operator, so process the stuff which came before it
01721                     if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
01722                         $_has_math = true;
01723                         if(!empty($_complete_var) || is_numeric($_complete_var)) {
01724                             $_output .= $this->_parse_var($_complete_var);
01725                         }
01726 
01727                         // just output the math operator to php
01728                         $_output .= $_math_var;
01729 
01730                         if(empty($_first_var))
01731                             $_first_var = $_complete_var;
01732 
01733                         $_complete_var = "";
01734                     } else {
01735                         $_complete_var .= $_math_var;
01736                     }
01737                 }
01738             }
01739             if($_has_math) {
01740                 if(!empty($_complete_var) || is_numeric($_complete_var))
01741                     $_output .= $this->_parse_var($_complete_var);
01742 
01743                 // get the modifiers working (only the last var from math + modifier is left)
01744                 $var_expr = $_complete_var;
01745             }
01746         }
01747 
01748         // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
01749         if(is_numeric(substr($var_expr, 0, 1)))
01750             $_var_ref = $var_expr;
01751         else
01752             $_var_ref = substr($var_expr, 1);
01753         
01754         if(!$_has_math) {
01755             
01756             // get [foo] and .foo and ->foo and (...) pieces
01757             preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
01758                         
01759             $_indexes = $match[0];
01760             $_var_name = array_shift($_indexes);
01761 
01762             /* Handle $smarty.* variable references as a special case. */
01763             if ($_var_name == 'smarty') {
01764                 /*
01765                  * If the reference could be compiled, use the compiled output;
01766                  * otherwise, fall back on the $smarty variable generated at
01767                  * run-time.
01768                  */
01769                 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
01770                     $_output = $smarty_ref;
01771                 } else {
01772                     $_var_name = substr(array_shift($_indexes), 1);
01773                     $_output = "\$this->_smarty_vars['$_var_name']";
01774                 }
01775             } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
01776                 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
01777                 if(count($_indexes) > 0)
01778                 {
01779                     $_var_name .= implode("", $_indexes);
01780                     $_indexes = array();
01781                 }
01782                 $_output = $_var_name;
01783             } else {
01784                 $_output = "\$this->_tpl_vars['$_var_name']";
01785             }
01786 
01787             foreach ($_indexes as $_index) {
01788                 if (substr($_index, 0, 1) == '[') {
01789                     $_index = substr($_index, 1, -1);
01790                     if (is_numeric($_index)) {
01791                         $_output .= "[$_index]";
01792                     } elseif (substr($_index, 0, 1) == '$') {
01793                         if (strpos($_index, '.') !== false) {
01794                             $_output .= '[' . $this->_parse_var($_index) . ']';
01795                         } else {
01796                             $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
01797                         }
01798                     } else {
01799                         $_var_parts = explode('.', $_index);
01800                         $_var_section = $_var_parts[0];
01801                         $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
01802                         $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
01803                     }
01804                 } else if (substr($_index, 0, 1) == '.') {
01805                     if (substr($_index, 1, 1) == '$')
01806                         $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
01807                     else
01808                         $_output .= "['" . substr($_index, 1) . "']";
01809                 } else if (substr($_index,0,2) == '->') {
01810                     if(substr($_index,2,2) == '__') {
01811                         $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
01812                     } elseif($this->security && substr($_index, 2, 1) == '_') {
01813                         $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
01814                     } elseif (substr($_index, 2, 1) == '$') {
01815                         if ($this->security) {
01816                             $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
01817                         } else {
01818                             $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
01819                         }
01820                     } else {
01821                         $_output .= $_index;
01822                     }
01823                 } elseif (substr($_index, 0, 1) == '(') {
01824                     $_index = $this->_parse_parenth_args($_index);
01825                     $_output .= $_index;
01826                 } else {
01827                     $_output .= $_index;
01828                 }
01829             }
01830         }
01831 
01832         return $_output;
01833     }
01834 
01841     function _parse_parenth_args($parenth_args)
01842     {
01843         preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
01844         $orig_vals = $match = $match[0];
01845         $this->_parse_vars_props($match);
01846         $replace = array();
01847         for ($i = 0, $count = count($match); $i < $count; $i++) {
01848             $replace[$orig_vals[$i]] = $match[$i];
01849         }
01850         return strtr($parenth_args, $replace);
01851     }
01852 
01858     function _parse_conf_var($conf_var_expr)
01859     {
01860         $parts = explode('|', $conf_var_expr, 2);
01861         $var_ref = $parts[0];
01862         $modifiers = isset($parts[1]) ? $parts[1] : '';
01863 
01864         $var_name = substr($var_ref, 1, -1);
01865 
01866         $output = "\$this->_config[0]['vars']['$var_name']";
01867 
01868         $this->_parse_modifiers($output, $modifiers);
01869 
01870         return $output;
01871     }
01872 
01879     function _parse_section_prop($section_prop_expr)
01880     {
01881         $parts = explode('|', $section_prop_expr, 2);
01882         $var_ref = $parts[0];
01883         $modifiers = isset($parts[1]) ? $parts[1] : '';
01884 
01885         preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
01886         $section_name = $match[1];
01887         $prop_name = $match[2];
01888 
01889         $output = "\$this->_sections['$section_name']['$prop_name']";
01890 
01891         $this->_parse_modifiers($output, $modifiers);
01892 
01893         return $output;
01894     }
01895 
01896 
01904     function _parse_modifiers(&$output, $modifier_string)
01905     {
01906         preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
01907         list(, $_modifiers, $modifier_arg_strings) = $_match;
01908 
01909         for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
01910             $_modifier_name = $_modifiers[$_i];
01911 
01912             if($_modifier_name == 'smarty') {
01913                 // skip smarty modifier
01914                 continue;
01915             }
01916 
01917             preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
01918             $_modifier_args = $_match[1];
01919 
01920             if (substr($_modifier_name, 0, 1) == '@') {
01921                 $_map_array = false;
01922                 $_modifier_name = substr($_modifier_name, 1);
01923             } else {
01924                 $_map_array = true;
01925             }
01926 
01927             if (empty($this->_plugins['modifier'][$_modifier_name])
01928                 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
01929                 && function_exists($_modifier_name)) {
01930                 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
01931                     $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
01932                 } else {
01933                     $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name,  null, null, false);
01934                 }
01935             }
01936             $this->_add_plugin('modifier', $_modifier_name);
01937 
01938             $this->_parse_vars_props($_modifier_args);
01939 
01940             if($_modifier_name == 'default') {
01941                 // supress notifications of default modifier vars and args
01942                 if(substr($output, 0, 1) == '$') {
01943                     $output = '@' . $output;
01944                 }
01945                 if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
01946                     $_modifier_args[0] = '@' . $_modifier_args[0];
01947                 }
01948             }
01949             if (count($_modifier_args) > 0)
01950                 $_modifier_args = ', '.implode(', ', $_modifier_args);
01951             else
01952                 $_modifier_args = '';
01953 
01954             if ($_map_array) {
01955                 $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))";
01956 
01957             } else {
01958 
01959                 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
01960 
01961             }
01962         }
01963     }
01964 
01965 
01973     function _add_plugin($type, $name, $delayed_loading = null)
01974     {
01975         if (!isset($this->_plugin_info[$type])) {
01976             $this->_plugin_info[$type] = array();
01977         }
01978         if (!isset($this->_plugin_info[$type][$name])) {
01979             $this->_plugin_info[$type][$name] = array($this->_current_file,
01980                                                       $this->_current_line_no,
01981                                                       $delayed_loading);
01982         }
01983     }
01984 
01985 
01992     function _compile_smarty_ref(&$indexes)
01993     {
01994         /* Extract the reference name. */
01995         $_ref = substr($indexes[0], 1);
01996         foreach($indexes as $_index_no=>$_index) {
01997             if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
01998                 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
01999             }
02000         }
02001 
02002         switch ($_ref) {
02003             case 'now':
02004                 $compiled_ref = 'time()';
02005                 $_max_index = 1;
02006                 break;
02007 
02008             case 'foreach':
02009                 array_shift($indexes);
02010                 $_var = $this->_parse_var_props(substr($indexes[0], 1));
02011                 $_propname = substr($indexes[1], 1);
02012                 $_max_index = 1;
02013                 switch ($_propname) {
02014                     case 'index':
02015                         array_shift($indexes);
02016                         $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
02017                         break;
02018                         
02019                     case 'first':
02020                         array_shift($indexes);
02021                         $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
02022                         break;
02023 
02024                     case 'last':
02025                         array_shift($indexes);
02026                         $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
02027                         break;
02028                         
02029                     case 'show':
02030                         array_shift($indexes);
02031                         $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
02032                         break;
02033                         
02034                     default:
02035                         unset($_max_index);
02036                         $compiled_ref = "\$this->_foreach[$_var]";
02037                 }
02038                 break;
02039 
02040             case 'section':
02041                 array_shift($indexes);
02042                 $_var = $this->_parse_var_props(substr($indexes[0], 1));
02043                 $compiled_ref = "\$this->_sections[$_var]";
02044                 break;
02045 
02046             case 'get':
02047                 $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
02048                 break;
02049 
02050             case 'post':
02051                 $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
02052                 break;
02053 
02054             case 'cookies':
02055                 $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
02056                 break;
02057 
02058             case 'env':
02059                 $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
02060                 break;
02061 
02062             case 'server':
02063                 $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
02064                 break;
02065 
02066             case 'session':
02067                 $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
02068                 break;
02069 
02070             /*
02071              * These cases are handled either at run-time or elsewhere in the
02072              * compiler.
02073              */
02074             case 'request':
02075                 if ($this->request_use_auto_globals) {
02076                     $compiled_ref = '$_REQUEST';
02077                     break;
02078                 } else {
02079                     $this->_init_smarty_vars = true;
02080                 }
02081                 return null;
02082 
02083             case 'capture':
02084                 return null;
02085 
02086             case 'template':
02087                 $compiled_ref = "'$this->_current_file'";
02088                 $_max_index = 1;
02089                 break;
02090 
02091             case 'version':
02092                 $compiled_ref = "'$this->_version'";
02093                 $_max_index = 1;
02094                 break;
02095 
02096             case 'const':
02097                 if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
02098                     $this->_syntax_error("(secure mode) constants not permitted",
02099                                          E_USER_WARNING, __FILE__, __LINE__);
02100                     return;
02101                 }
02102                 array_shift($indexes);
02103                 if (preg_match('!^\.\w+$!', $indexes[0])) {
02104                     $compiled_ref = '@' . substr($indexes[0], 1);
02105                 } else {
02106                     $_val = $this->_parse_var_props(substr($indexes[0], 1));
02107                     $compiled_ref = '@constant(' . $_val . ')';
02108                 }
02109                 $_max_index = 1;
02110                 break;
02111 
02112             case 'config':
02113                 $compiled_ref = "\$this->_config[0]['vars']";
02114                 $_max_index = 3;
02115                 break;
02116 
02117             case 'ldelim':
02118                 $compiled_ref = "'$this->left_delimiter'";
02119                 break;
02120 
02121             case 'rdelim':
02122                 $compiled_ref = "'$this->right_delimiter'";
02123                 break;
02124                 
02125             default:
02126                 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
02127                 break;
02128         }
02129 
02130         if (isset($_max_index) && count($indexes) > $_max_index) {
02131             $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
02132         }
02133 
02134         array_shift($indexes);
02135         return $compiled_ref;
02136     }
02137 
02148     function _compile_plugin_call($type, $name) {
02149         if (isset($this->_plugins[$type][$name])) {
02150             /* plugin loaded */
02151             if (is_array($this->_plugins[$type][$name][0])) {
02152                 return ((is_object($this->_plugins[$type][$name][0][0])) ?
02153                         "\$this->_plugins['$type']['$name'][0][0]->"    /* method callback */
02154                         : (string)($this->_plugins[$type][$name][0][0]).'::'    /* class callback */
02155                        ). $this->_plugins[$type][$name][0][1];
02156 
02157             } else {
02158                 /* function callback */
02159                 return $this->_plugins[$type][$name][0];
02160 
02161             }
02162         } else {
02163             /* plugin not loaded -> auto-loadable-plugin */
02164             return 'smarty_'.$type.'_'.$name;
02165 
02166         }
02167     }
02168 
02172     function _load_filters()
02173     {
02174         if (count($this->_plugins['prefilter']) > 0) {
02175             foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
02176                 if ($prefilter === false) {
02177                     unset($this->_plugins['prefilter'][$filter_name]);
02178                     $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
02179                     require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
02180                     smarty_core_load_plugins($_params, $this);
02181                 }
02182             }
02183         }
02184         if (count($this->_plugins['postfilter']) > 0) {
02185             foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
02186                 if ($postfilter === false) {
02187                     unset($this->_plugins['postfilter'][$filter_name]);
02188                     $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
02189                     require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
02190                     smarty_core_load_plugins($_params, $this);
02191                 }
02192             }
02193         }
02194     }
02195 
02196 
02203     function _quote_replace($string)
02204     {
02205         return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
02206     }
02207 
02216     function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
02217     {
02218         $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
02219     }
02220 
02221 
02228     function _push_cacheable_state($type, $name) {
02229         $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
02230         if ($_cacheable
02231             || 0<$this->_cacheable_state++) return '';
02232         if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
02233         $_ret = 'if ($this->caching && !$this->_cache_including): echo \'{nocache:'
02234             . $this->_cache_serial . '#' . $this->_nocache_count
02235             . '}\'; endif;';
02236         return $_ret;
02237     }
02238 
02239 
02246     function _pop_cacheable_state($type, $name) {
02247         $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
02248         if ($_cacheable
02249             || --$this->_cacheable_state>0) return '';
02250         return 'if ($this->caching && !$this->_cache_including): echo \'{/nocache:'
02251             . $this->_cache_serial . '#' . ($this->_nocache_count++)
02252             . '}\'; endif;';
02253     }
02254 
02255 
02260     function _push_tag($open_tag)
02261     {
02262         array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
02263     }
02264 
02271     function _pop_tag($close_tag)
02272     {
02273         $message = '';
02274         if (count($this->_tag_stack)>0) {
02275             list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
02276             if ($close_tag == $_open_tag) {
02277                 return $_open_tag;
02278             }
02279             if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
02280                 return $this->_pop_tag($close_tag);
02281             }
02282             if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
02283                 $this->_pop_tag($close_tag);
02284                 return $_open_tag;
02285             }
02286             if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
02287                 $this->_pop_tag($close_tag);
02288                 return $_open_tag;
02289             }
02290             if ($_open_tag == 'else' || $_open_tag == 'elseif') {
02291                 $_open_tag = 'if';
02292             } elseif ($_open_tag == 'sectionelse') {
02293                 $_open_tag = 'section';
02294             } elseif ($_open_tag == 'foreachelse') {
02295                 $_open_tag = 'foreach';
02296             }
02297             $message = " expected {/$_open_tag} (opened line $_line_no).";
02298         }
02299         $this->_syntax_error("mismatched tag {/$close_tag}.$message",
02300                              E_USER_ERROR, __FILE__, __LINE__);
02301     }
02302 
02303 }
02304 
02313 function _smarty_sort_length($a, $b)
02314 {
02315     if($a == $b)
02316         return 0;
02317 
02318     if(strlen($a) == strlen($b))
02319         return ($a > $b) ? -1 : 1;
02320 
02321     return (strlen($a) > strlen($b)) ? -1 : 1;
02322 }
02323 
02324 
02325 /* vim: set et: */
02326 
02327 ?>

Generated on Sun Dec 14 14:20:25 2008 for CMSMS by  doxygen 1.5.1