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