| [ Index ] |
|
Code source de vtiger CRM 5.0.2 |
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.12 25 * @copyright 2001-2005 New Digital Group, Inc. 26 * @package Smarty 27 */ 28 29 /* $Id: Smarty_Compiler.class.php 5728 2006-05-03 21:19:46Z allanbush $ */ 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 (strlen($compiled_content) && (substr($compiled_content, -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 (substr($template_tag, 0, 1) == '*' && substr($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 (substr($tag_command, 0, 1)=='/') { 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 (substr($tag_command, 0, 1) == '/') { 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 $_cache_attrs=''; 730 $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs); 731 $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); '; 732 $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);'; 733 $output .= 'while ($_block_repeat) { ob_start(); ?>'; 734 } else { 735 $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); '; 736 $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)'; 737 if ($tag_modifier != '') { 738 $this->_parse_modifiers($_out_tag_text, $tag_modifier); 739 } 740 $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } '; 741 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>'; 742 } 743 744 return true; 745 } 746 747 748 /** 749 * compile custom function tag 750 * 751 * @param string $tag_command 752 * @param string $tag_args 753 * @param string $tag_modifier 754 * @return string 755 */ 756 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output) 757 { 758 $found = false; 759 $have_function = true; 760 761 /* 762 * First we check if the custom function has already been registered 763 * or loaded from a plugin file. 764 */ 765 if (isset($this->_plugins['function'][$tag_command])) { 766 $found = true; 767 $plugin_func = $this->_plugins['function'][$tag_command][0]; 768 if (!is_callable($plugin_func)) { 769 $message = "custom function '$tag_command' is not implemented"; 770 $have_function = false; 771 } 772 } 773 /* 774 * Otherwise we need to load plugin file and look for the function 775 * inside it. 776 */ 777 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) { 778 $found = true; 779 780 include_once $plugin_file; 781 782 $plugin_func = 'smarty_function_' . $tag_command; 783 if (!function_exists($plugin_func)) { 784 $message = "plugin function $plugin_func() not found in $plugin_file\n"; 785 $have_function = false; 786 } else { 787 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true); 788 789 } 790 } 791 792 if (!$found) { 793 return false; 794 } else if (!$have_function) { 795 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); 796 return true; 797 } 798 799 /* declare plugin to be loaded on display of the template that 800 we compile right now */ 801 $this->_add_plugin('function', $tag_command); 802 803 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command); 804 $attrs = $this->_parse_attrs($tag_args); 805 $_cache_attrs = ''; 806 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs); 807 808 $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)"; 809 if($tag_modifier != '') { 810 $this->_parse_modifiers($output, $tag_modifier); 811 } 812 813 if($output != '') { 814 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';' 815 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline; 816 } 817 818 return true; 819 } 820 821 /** 822 * compile a registered object tag 823 * 824 * @param string $tag_command 825 * @param array $attrs 826 * @param string $tag_modifier 827 * @return string 828 */ 829 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier) 830 { 831 if (substr($tag_command, 0, 1) == '/') { 832 $start_tag = false; 833 $tag_command = substr($tag_command, 1); 834 } else { 835 $start_tag = true; 836 } 837 838 list($object, $obj_comp) = explode('->', $tag_command); 839 840 $arg_list = array(); 841 if(count($attrs)) { 842 $_assign_var = false; 843 foreach ($attrs as $arg_name => $arg_value) { 844 if($arg_name == 'assign') { 845 $_assign_var = $arg_value; 846 unset($attrs['assign']); 847 continue; 848 } 849 if (is_bool($arg_value)) 850 $arg_value = $arg_value ? 'true' : 'false'; 851 $arg_list[] = "'$arg_name' => $arg_value"; 852 } 853 } 854 855 if($this->_reg_objects[$object][2]) { 856 // smarty object argument format 857 $args = "array(".implode(',', (array)$arg_list)."), \$this"; 858 } else { 859 // traditional argument format 860 $args = implode(',', array_values($attrs)); 861 if (empty($args)) { 862 $args = 'null'; 863 } 864 } 865 866 $prefix = ''; 867 $postfix = ''; 868 $newline = ''; 869 if(!is_object($this->_reg_objects[$object][0])) { 870 $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); 871 } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) { 872 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); 873 } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) { 874 // method 875 if(in_array($obj_comp, $this->_reg_objects[$object][3])) { 876 // block method 877 if ($start_tag) { 878 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); "; 879 $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); "; 880 $prefix .= "while (\$_block_repeat) { ob_start();"; 881 $return = null; 882 $postfix = ''; 883 } else { 884 $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); "; 885 $return = "\$_block_repeat=false; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)"; 886 $postfix = "} array_pop(\$this->_tag_stack);"; 887 } 888 } else { 889 // non-block method 890 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)"; 891 } 892 } else { 893 // property 894 $return = "\$this->_reg_objects['$object'][0]->$obj_comp"; 895 } 896 897 if($return != null) { 898 if($tag_modifier != '') { 899 $this->_parse_modifiers($return, $tag_modifier); 900 } 901 902 if(!empty($_assign_var)) { 903 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);"; 904 } else { 905 $output = 'echo ' . $return . ';'; 906 $newline = $this->_additional_newline; 907 } 908 } else { 909 $output = ''; 910 } 911 912 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline; 913 } 914 915 /** 916 * Compile {insert ...} tag 917 * 918 * @param string $tag_args 919 * @return string 920 */ 921 function _compile_insert_tag($tag_args) 922 { 923 $attrs = $this->_parse_attrs($tag_args); 924 $name = $this->_dequote($attrs['name']); 925 926 if (empty($name)) { 927 $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__); 928 } 929 930 if (!empty($attrs['script'])) { 931 $delayed_loading = true; 932 } else { 933 $delayed_loading = false; 934 } 935 936 foreach ($attrs as $arg_name => $arg_value) { 937 if (is_bool($arg_value)) 938 $arg_value = $arg_value ? 'true' : 'false'; 939 $arg_list[] = "'$arg_name' => $arg_value"; 940 } 941 942 $this->_add_plugin('insert', $name, $delayed_loading); 943 944 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))"; 945 946 return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline; 947 } 948 949 /** 950 * Compile {include ...} tag 951 * 952 * @param string $tag_args 953 * @return string 954 */ 955 function _compile_include_tag($tag_args) 956 { 957 $attrs = $this->_parse_attrs($tag_args); 958 $arg_list = array(); 959 960 if (empty($attrs['file'])) { 961 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__); 962 } 963 964 foreach ($attrs as $arg_name => $arg_value) { 965 if ($arg_name == 'file') { 966 $include_file = $arg_value; 967 continue; 968 } else if ($arg_name == 'assign') { 969 $assign_var = $arg_value; 970 continue; 971 } 972 if (is_bool($arg_value)) 973 $arg_value = $arg_value ? 'true' : 'false'; 974 $arg_list[] = "'$arg_name' => $arg_value"; 975 } 976 977 $output = '<?php '; 978 979 if (isset($assign_var)) { 980 $output .= "ob_start();\n"; 981 } 982 983 $output .= 984 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n"; 985 986 987 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))"; 988 $output .= "\$this->_smarty_include($_params);\n" . 989 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" . 990 "unset(\$_smarty_tpl_vars);\n"; 991 992 if (isset($assign_var)) { 993 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n"; 994 } 995 996 $output .= ' ?>'; 997 998 return $output; 999 1000 } 1001 1002 /** 1003 * Compile {include ...} tag 1004 * 1005 * @param string $tag_args 1006 * @return string 1007 */ 1008 function _compile_include_php_tag($tag_args) 1009 { 1010 $attrs = $this->_parse_attrs($tag_args); 1011 1012 if (empty($attrs['file'])) { 1013 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__); 1014 } 1015 1016 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']); 1017 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true'; 1018 1019 $arg_list = array(); 1020 foreach($attrs as $arg_name => $arg_value) { 1021 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') { 1022 if(is_bool($arg_value)) 1023 $arg_value = $arg_value ? 'true' : 'false'; 1024 $arg_list[] = "'$arg_name' => $arg_value"; 1025 } 1026 } 1027 1028 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))"; 1029 1030 return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline; 1031 } 1032 1033 1034 /** 1035 * Compile {section ...} tag 1036 * 1037 * @param string $tag_args 1038 * @return string 1039 */ 1040 function _compile_section_start($tag_args) 1041 { 1042 $attrs = $this->_parse_attrs($tag_args); 1043 $arg_list = array(); 1044 1045 $output = '<?php '; 1046 $section_name = $attrs['name']; 1047 if (empty($section_name)) { 1048 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__); 1049 } 1050 1051 $output .= "unset(\$this->_sections[$section_name]);\n"; 1052 $section_props = "\$this->_sections[$section_name]"; 1053 1054 foreach ($attrs as $attr_name => $attr_value) { 1055 switch ($attr_name) { 1056 case 'loop': 1057 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n"; 1058 break; 1059 1060 case 'show': 1061 if (is_bool($attr_value)) 1062 $show_attr_value = $attr_value ? 'true' : 'false'; 1063 else 1064 $show_attr_value = "(bool)$attr_value"; 1065 $output .= "{$section_props}['show'] = $show_attr_value;\n"; 1066 break; 1067 1068 case 'name': 1069 $output .= "{$section_props}['$attr_name'] = $attr_value;\n"; 1070 break; 1071 1072 case 'max': 1073 case 'start': 1074 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n"; 1075 break; 1076 1077 case 'step': 1078 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n"; 1079 break; 1080 1081 default: 1082 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__); 1083 break; 1084 } 1085 } 1086 1087 if (!isset($attrs['show'])) 1088 $output .= "{$section_props}['show'] = true;\n"; 1089 1090 if (!isset($attrs['loop'])) 1091 $output .= "{$section_props}['loop'] = 1;\n"; 1092 1093 if (!isset($attrs['max'])) 1094 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n"; 1095 else 1096 $output .= "if ({$section_props}['max'] < 0)\n" . 1097 " {$section_props}['max'] = {$section_props}['loop'];\n"; 1098 1099 if (!isset($attrs['step'])) 1100 $output .= "{$section_props}['step'] = 1;\n"; 1101 1102 if (!isset($attrs['start'])) 1103 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n"; 1104 else { 1105 $output .= "if ({$section_props}['start'] < 0)\n" . 1106 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" . 1107 "else\n" . 1108 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n"; 1109 } 1110 1111 $output .= "if ({$section_props}['show']) {\n"; 1112 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) { 1113 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n"; 1114 } else { 1115 $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"; 1116 } 1117 $output .= " if ({$section_props}['total'] == 0)\n" . 1118 " {$section_props}['show'] = false;\n" . 1119 "} else\n" . 1120 " {$section_props}['total'] = 0;\n"; 1121 1122 $output .= "if ({$section_props}['show']):\n"; 1123 $output .= " 1124 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1; 1125 {$section_props}['iteration'] <= {$section_props}['total']; 1126 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n"; 1127 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n"; 1128 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n"; 1129 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n"; 1130 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n"; 1131 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n"; 1132 1133 $output .= "?>"; 1134 1135 return $output; 1136 } 1137 1138 1139 /** 1140 * Compile {foreach ...} tag. 1141 * 1142 * @param string $tag_args 1143 * @return string 1144 */ 1145 function _compile_foreach_start($tag_args) 1146 { 1147 $attrs = $this->_parse_attrs($tag_args); 1148 $arg_list = array(); 1149 1150 if (empty($attrs['from'])) { 1151 return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__); 1152 } 1153 $from = $attrs['from']; 1154 1155 if (empty($attrs['item'])) { 1156 return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__); 1157 } 1158 $item = $this->_dequote($attrs['item']); 1159 if (!preg_match('~^\w+$~', $item)) { 1160 return $this->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__); 1161 } 1162 1163 if (isset($attrs['key'])) { 1164 $key = $this->_dequote($attrs['key']); 1165 if (!preg_match('~^\w+$~', $key)) { 1166 return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__); 1167 } 1168 $key_part = "\$this->_tpl_vars['$key'] => "; 1169 } else { 1170 $key = null; 1171 $key_part = ''; 1172 } 1173 1174 if (isset($attrs['name'])) { 1175 $name = $attrs['name']; 1176 } else { 1177 $name = null; 1178 } 1179 1180 $output = '<?php '; 1181 $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }"; 1182 if (isset($name)) { 1183 $foreach_props = "\$this->_foreach[$name]"; 1184 $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n"; 1185 $output .= "if ({$foreach_props}['total'] > 0):\n"; 1186 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n"; 1187 $output .= " {$foreach_props}['iteration']++;\n"; 1188 } else { 1189 $output .= "if (count(\$_from)):\n"; 1190 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n"; 1191 } 1192 $output .= '?>'; 1193 1194 return $output; 1195 } 1196 1197 1198 /** 1199 * Compile {capture} .. {/capture} tags 1200 * 1201 * @param boolean $start true if this is the {capture} tag 1202 * @param string $tag_args 1203 * @return string 1204 */ 1205 1206 function _compile_capture_tag($start, $tag_args = '') 1207 { 1208 $attrs = $this->_parse_attrs($tag_args); 1209 1210 if ($start) { 1211 if (isset($attrs['name'])) 1212 $buffer = $attrs['name']; 1213 else 1214 $buffer = "'default'"; 1215 1216 if (isset($attrs['assign'])) 1217 $assign = $attrs['assign']; 1218 else 1219 $assign = null; 1220 $output = "<?php ob_start(); ?>"; 1221 $this->_capture_stack[] = array($buffer, $assign); 1222 } else { 1223 list($buffer, $assign) = array_pop($this->_capture_stack); 1224 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); "; 1225 if (isset($assign)) { 1226 $output .= " \$this->assign($assign, ob_get_contents());"; 1227 } 1228 $output .= "ob_end_clean(); ?>"; 1229 } 1230 1231 return $output; 1232 } 1233 1234 /** 1235 * Compile {if ...} tag 1236 * 1237 * @param string $tag_args 1238 * @param boolean $elseif if true, uses elseif instead of if 1239 * @return string 1240 */ 1241 function _compile_if_tag($tag_args, $elseif = false) 1242 { 1243 1244 /* Tokenize args for 'if' tag. */ 1245 preg_match_all('~(?> 1246 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call 1247 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string 1248 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token 1249 \b\w+\b | # valid word token 1250 \S+ # anything else 1251 )~x', $tag_args, $match); 1252 1253 $tokens = $match[0]; 1254 1255 if(empty($tokens)) { 1256 $_error_msg .= $elseif ? "'elseif'" : "'if'"; 1257 $_error_msg .= ' statement requires arguments'; 1258 $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__); 1259 } 1260 1261 1262 // make sure we have balanced parenthesis 1263 $token_count = array_count_values($tokens); 1264 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) { 1265 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__); 1266 } 1267 1268 $is_arg_stack = array(); 1269 1270 for ($i = 0; $i < count($tokens); $i++) { 1271 1272 $token = &$tokens[$i]; 1273 1274 switch (strtolower($token)) { 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 case '&': 1293 case '~': 1294 case ')': 1295 case ',': 1296 case '+': 1297 case '-': 1298 case '*': 1299 case '/': 1300 case '@': 1301 break; 1302 1303 case 'eq': 1304 $token = '=='; 1305 break; 1306 1307 case 'ne': 1308 case 'neq': 1309 $token = '!='; 1310 break; 1311 1312 case 'lt': 1313 $token = '<'; 1314 break; 1315 1316 case 'le': 1317 case 'lte': 1318 $token = '<='; 1319 break; 1320 1321 case 'gt': 1322 $token = '>'; 1323 break; 1324 1325 case 'ge': 1326 case 'gte': 1327 $token = '>='; 1328 break; 1329 1330 case 'and': 1331 $token = '&&'; 1332 break; 1333 1334 case 'or': 1335 $token = '||'; 1336 break; 1337 1338 case 'not': 1339 $token = '!'; 1340 break; 1341 1342 case 'mod': 1343 $token = '%'; 1344 break; 1345 1346 case '(': 1347 array_push($is_arg_stack, $i); 1348 break; 1349 1350 case 'is': 1351 /* If last token was a ')', we operate on the parenthesized 1352 expression. The start of the expression is on the stack. 1353 Otherwise, we operate on the last encountered token. */ 1354 if ($tokens[$i-1] == ')') 1355 $is_arg_start = array_pop($is_arg_stack); 1356 else 1357 $is_arg_start = $i-1; 1358 /* Construct the argument for 'is' expression, so it knows 1359 what to operate on. */ 1360 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start)); 1361 1362 /* Pass all tokens from next one until the end to the 1363 'is' expression parsing function. The function will 1364 return modified tokens, where the first one is the result 1365 of the 'is' expression and the rest are the tokens it 1366 didn't touch. */ 1367 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1)); 1368 1369 /* Replace the old tokens with the new ones. */ 1370 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens); 1371 1372 /* Adjust argument start so that it won't change from the 1373 current position for the next iteration. */ 1374 $i = $is_arg_start; 1375 break; 1376 1377 default: 1378 if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) { 1379 // function call 1380 if($this->security && 1381 !in_array($token, $this->security_settings['IF_FUNCS'])) { 1382 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__); 1383 } 1384 } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') { 1385 // variable function call 1386 $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__); 1387 } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) { 1388 // object or variable 1389 $token = $this->_parse_var_props($token); 1390 } elseif(is_numeric($token)) { 1391 // number, skip it 1392 } else { 1393 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__); 1394 } 1395 break; 1396 } 1397 } 1398 1399 if ($elseif) 1400 return '<?php elseif ('.implode(' ', $tokens).'): ?>'; 1401 else 1402 return '<?php if ('.implode(' ', $tokens).'): ?>'; 1403 } 1404 1405 1406 function _compile_arg_list($type, $name, $attrs, &$cache_code) { 1407 $arg_list = array(); 1408 1409 if (isset($type) && isset($name) 1410 && isset($this->_plugins[$type]) 1411 && isset($this->_plugins[$type][$name]) 1412 && empty($this->_plugins[$type][$name][4]) 1413 && is_array($this->_plugins[$type][$name][5]) 1414 ) { 1415 /* we have a list of parameters that should be cached */ 1416 $_cache_attrs = $this->_plugins[$type][$name][5]; 1417 $_count = $this->_cache_attrs_count++; 1418 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');"; 1419 1420 } else { 1421 /* no parameters are cached */ 1422 $_cache_attrs = null; 1423 } 1424 1425 foreach ($attrs as $arg_name => $arg_value) { 1426 if (is_bool($arg_value)) 1427 $arg_value = $arg_value ? 'true' : 'false'; 1428 if (is_null($arg_value)) 1429 $arg_value = 'null'; 1430 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) { 1431 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)"; 1432 } else { 1433 $arg_list[] = "'$arg_name' => $arg_value"; 1434 } 1435 } 1436 return $arg_list; 1437 } 1438 1439 /** 1440 * Parse is expression 1441 * 1442 * @param string $is_arg 1443 * @param array $tokens 1444 * @return array 1445 */ 1446 function _parse_is_expr($is_arg, $tokens) 1447 { 1448 $expr_end = 0; 1449 $negate_expr = false; 1450 1451 if (($first_token = array_shift($tokens)) == 'not') { 1452 $negate_expr = true; 1453 $expr_type = array_shift($tokens); 1454 } else 1455 $expr_type = $first_token; 1456 1457 switch ($expr_type) { 1458 case 'even': 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 'odd': 1468 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') { 1469 $expr_end++; 1470 $expr_arg = $tokens[$expr_end++]; 1471 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))"; 1472 } else 1473 $expr = "(1 & $is_arg)"; 1474 break; 1475 1476 case 'div': 1477 if (@$tokens[$expr_end] == 'by') { 1478 $expr_end++; 1479 $expr_arg = $tokens[$expr_end++]; 1480 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")"; 1481 } else { 1482 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__); 1483 } 1484 break; 1485 1486 default: 1487 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__); 1488 break; 1489 } 1490 1491 if ($negate_expr) { 1492 $expr = "!($expr)"; 1493 } 1494 1495 array_splice($tokens, 0, $expr_end, $expr); 1496 1497 return $tokens; 1498 } 1499 1500 1501 /** 1502 * Parse attribute string 1503 * 1504 * @param string $tag_args 1505 * @return array 1506 */ 1507 function _parse_attrs($tag_args) 1508 { 1509 1510 /* Tokenize tag attributes. */ 1511 preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+) 1512 )+ | 1513 [=] 1514 ~x', $tag_args, $match); 1515 $tokens = $match[0]; 1516 1517 $attrs = array(); 1518 /* Parse state: 1519 0 - expecting attribute name 1520 1 - expecting '=' 1521 2 - expecting attribute value (not '=') */ 1522 $state = 0; 1523 1524 foreach ($tokens as $token) { 1525 switch ($state) { 1526 case 0: 1527 /* If the token is a valid identifier, we set attribute name 1528 and go to state 1. */ 1529 if (preg_match('~^\w+$~', $token)) { 1530 $attr_name = $token; 1531 $state = 1; 1532 } else 1533 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__); 1534 break; 1535 1536 case 1: 1537 /* If the token is '=', then we go to state 2. */ 1538 if ($token == '=') { 1539 $state = 2; 1540 } else 1541 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__); 1542 break; 1543 1544 case 2: 1545 /* If token is not '=', we set the attribute value and go to 1546 state 0. */ 1547 if ($token != '=') { 1548 /* We booleanize the token if it's a non-quoted possible 1549 boolean value. */ 1550 if (preg_match('~^(on|yes|true)$~', $token)) { 1551 $token = 'true'; 1552 } else if (preg_match('~^(off|no|false)$~', $token)) { 1553 $token = 'false'; 1554 } else if ($token == 'null') { 1555 $token = 'null'; 1556 } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) { 1557 /* treat integer literally */ 1558 } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) { 1559 /* treat as a string, double-quote it escaping quotes */ 1560 $token = '"'.addslashes($token).'"'; 1561 } 1562 1563 $attrs[$attr_name] = $token; 1564 $state = 0; 1565 } else 1566 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__); 1567 break; 1568 } 1569 $last_token = $token; 1570 } 1571 1572 if($state != 0) { 1573 if($state == 1) { 1574 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__); 1575 } else { 1576 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__); 1577 } 1578 } 1579 1580 $this->_parse_vars_props($attrs); 1581 1582 return $attrs; 1583 } 1584 1585 /** 1586 * compile multiple variables and section properties tokens into 1587 * PHP code 1588 * 1589 * @param array $tokens 1590 */ 1591 function _parse_vars_props(&$tokens) 1592 { 1593 foreach($tokens as $key => $val) { 1594 $tokens[$key] = $this->_parse_var_props($val); 1595 } 1596 } 1597 1598 /** 1599 * compile single variable and section properties token into 1600 * PHP code 1601 * 1602 * @param string $val 1603 * @param string $tag_attrs 1604 * @return string 1605 */ 1606 function _parse_var_props($val) 1607 { 1608 $val = trim($val); 1609 1610 if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) { 1611 // $ variable or object 1612 $return = $this->_parse_var($match[1]); 1613 $modifiers = $match[2]; 1614 if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) { 1615 $_default_mod_string = implode('|',(array)$this->default_modifiers); 1616 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers; 1617 } 1618 $this->_parse_modifiers($return, $modifiers); 1619 return $return; 1620 } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1621 // double quoted text 1622 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); 1623 $return = $this->_expand_quoted_text($match[1]); 1624 if($match[2] != '') { 1625 $this->_parse_modifiers($return, $match[2]); 1626 } 1627 return $return; 1628 } 1629 elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1630 // numerical constant 1631 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); 1632 if($match[2] != '') { 1633 $this->_parse_modifiers($match[1], $match[2]); 1634 return $match[1]; 1635 } 1636 } 1637 elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1638 // single quoted text 1639 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); 1640 if($match[2] != '') { 1641 $this->_parse_modifiers($match[1], $match[2]); 1642 return $match[1]; 1643 } 1644 } 1645 elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1646 // config var 1647 return $this->_parse_conf_var($val); 1648 } 1649 elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1650 // section var 1651 return $this->_parse_section_prop($val); 1652 } 1653 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) { 1654 // literal string 1655 return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"'); 1656 } 1657 return $val; 1658 } 1659 1660 /** 1661 * expand quoted text with embedded variables 1662 * 1663 * @param string $var_expr 1664 * @return string 1665 */ 1666 function _expand_quoted_text($var_expr) 1667 { 1668 // if contains unescaped $, expand it 1669 if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) { 1670 $_match = $_match[0]; 1671 rsort($_match); 1672 reset($_match); 1673 foreach($_match as $_var) { 1674 $var_expr = str_replace ($_var, '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."', $var_expr); 1675 } 1676 $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr); 1677 } else { 1678 $_return = $var_expr; 1679 } 1680 // replace double quoted literal string with single quotes 1681 $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return); 1682 return $_return; 1683 } 1684 1685 /** 1686 * parse variable expression into PHP code 1687 * 1688 * @param string $var_expr 1689 * @param string $output 1690 * @return string 1691 */ 1692 function _parse_var($var_expr) 1693 { 1694 $_has_math = false; 1695 $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE); 1696 1697 if(count($_math_vars) > 1) { 1698 $_first_var = ""; 1699 $_complete_var = ""; 1700 $_output = ""; 1701 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter) 1702 foreach($_math_vars as $_k => $_math_var) { 1703 $_math_var = $_math_vars[$_k]; 1704 1705 if(!empty($_math_var) || is_numeric($_math_var)) { 1706 // hit a math operator, so process the stuff which came before it 1707 if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) { 1708 $_has_math = true; 1709 if(!empty($_complete_var) || is_numeric($_complete_var)) { 1710 $_output .= $this->_parse_var($_complete_var); 1711 } 1712 1713 // just output the math operator to php 1714 $_output .= $_math_var; 1715 1716 if(empty($_first_var)) 1717 $_first_var = $_complete_var; 1718 1719 $_complete_var = ""; 1720 } else { 1721 $_complete_var .= $_math_var; 1722 } 1723 } 1724 } 1725 if($_has_math) { 1726 if(!empty($_complete_var) || is_numeric($_complete_var)) 1727 $_output .= $this->_parse_var($_complete_var); 1728 1729 // get the modifiers working (only the last var from math + modifier is left) 1730 $var_expr = $_complete_var; 1731 } 1732 } 1733 1734 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit) 1735 if(is_numeric(substr($var_expr, 0, 1))) 1736 $_var_ref = $var_expr; 1737 else 1738 $_var_ref = substr($var_expr, 1); 1739 1740 if(!$_has_math) { 1741 1742 // get [foo] and .foo and ->foo and (...) pieces 1743 preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match); 1744 1745 $_indexes = $match[0]; 1746 $_var_name = array_shift($_indexes); 1747 1748 /* Handle $smarty.* variable references as a special case. */ 1749 if ($_var_name == 'smarty') { 1750 /* 1751 * If the reference could be compiled, use the compiled output; 1752 * otherwise, fall back on the $smarty variable generated at 1753 * run-time. 1754 */ 1755 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) { 1756 $_output = $smarty_ref; 1757 } else { 1758 $_var_name = substr(array_shift($_indexes), 1); 1759 $_output = "\$this->_smarty_vars['$_var_name']"; 1760 } 1761 } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) { 1762 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers 1763 if(count($_indexes) > 0) 1764 { 1765 $_var_name .= implode("", $_indexes); 1766 $_indexes = array(); 1767 } 1768 $_output = $_var_name; 1769 } else { 1770 $_output = "\$this->_tpl_vars['$_var_name']"; 1771 } 1772 1773 foreach ($_indexes as $_index) { 1774 if (substr($_index, 0, 1) == '[') { 1775 $_index = substr($_index, 1, -1); 1776 if (is_numeric($_index)) { 1777 $_output .= "[$_index]"; 1778 } elseif (substr($_index, 0, 1) == '$') { 1779 if (strpos($_index, '.') !== false) { 1780 $_output .= '[' . $this->_parse_var($_index) . ']'; 1781 } else { 1782 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]"; 1783 } 1784 } else { 1785 $_var_parts = explode('.', $_index); 1786 $_var_section = $_var_parts[0]; 1787 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index'; 1788 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]"; 1789 } 1790 } else if (substr($_index, 0, 1) == '.') { 1791 if (substr($_index, 1, 1) == '$') 1792 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]"; 1793 else 1794 $_output .= "['" . substr($_index, 1) . "']"; 1795 } else if (substr($_index,0,2) == '->') { 1796 if(substr($_index,2,2) == '__') { 1797 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__); 1798 } elseif($this->security && substr($_index, 2, 1) == '_') { 1799 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__); 1800 } elseif (substr($_index, 2, 1) == '$') { 1801 if ($this->security) { 1802 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__); 1803 } else { 1804 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}'; 1805 } 1806 } else { 1807 $_output .= $_index; 1808 } 1809 } elseif (substr($_index, 0, 1) == '(') { 1810 $_index = $this->_parse_parenth_args($_index); 1811 $_output .= $_index; 1812 } else { 1813 $_output .= $_index; 1814 } 1815 } 1816 } 1817 1818 return $_output; 1819 } 1820 1821 /** 1822 * parse arguments in function call parenthesis 1823 * 1824 * @param string $parenth_args 1825 * @return string 1826 */ 1827 function _parse_parenth_args($parenth_args) 1828 { 1829 preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match); 1830 $orig_vals = $match = $match[0]; 1831 $this->_parse_vars_props($match); 1832 $replace = array(); 1833 for ($i = 0, $count = count($match); $i < $count; $i++) { 1834 $replace[$orig_vals[$i]] = $match[$i]; 1835 } 1836 return strtr($parenth_args, $replace); 1837 } 1838 1839 /** 1840 * parse configuration variable expression into PHP code 1841 * 1842 * @param string $conf_var_expr 1843 */ 1844 function _parse_conf_var($conf_var_expr) 1845 { 1846 $parts = explode('|', $conf_var_expr, 2); 1847 $var_ref = $parts[0]; 1848 $modifiers = isset($parts[1]) ? $parts[1] : ''; 1849 1850 $var_name = substr($var_ref, 1, -1); 1851 1852 $output = "\$this->_config[0]['vars']['$var_name']"; 1853 1854 $this->_parse_modifiers($output, $modifiers); 1855 1856 return $output; 1857 } 1858 1859 /** 1860 * parse section property expression into PHP code 1861 * 1862 * @param string $section_prop_expr 1863 * @return string 1864 */ 1865 function _parse_section_prop($section_prop_expr) 1866 { 1867 $parts = explode('|', $section_prop_expr, 2); 1868 $var_ref = $parts[0]; 1869 $modifiers = isset($parts[1]) ? $parts[1] : ''; 1870 1871 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match); 1872 $section_name = $match[1]; 1873 $prop_name = $match[2]; 1874 1875 $output = "\$this->_sections['$section_name']['$prop_name']"; 1876 1877 $this->_parse_modifiers($output, $modifiers); 1878 1879 return $output; 1880 } 1881 1882 1883 /** 1884 * parse modifier chain into PHP code 1885 * 1886 * sets $output to parsed modified chain 1887 * @param string $output 1888 * @param string $modifier_string 1889 */ 1890 function _parse_modifiers(&$output, $modifier_string) 1891 { 1892 preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match); 1893 list(, $_modifiers, $modifier_arg_strings) = $_match; 1894 1895 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) { 1896 $_modifier_name = $_modifiers[$_i]; 1897 1898 if($_modifier_name == 'smarty') { 1899 // skip smarty modifier 1900 continue; 1901 } 1902 1903 preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match); 1904 $_modifier_args = $_match[1]; 1905 1906 if (substr($_modifier_name, 0, 1) == '@') { 1907 $_map_array = false; 1908 $_modifier_name = substr($_modifier_name, 1); 1909 } else { 1910 $_map_array = true; 1911 } 1912 1913 if (empty($this->_plugins['modifier'][$_modifier_name]) 1914 && !$this->_get_plugin_filepath('modifier', $_modifier_name) 1915 && function_exists($_modifier_name)) { 1916 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) { 1917 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); 1918 } else { 1919 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false); 1920 } 1921 } 1922 $this->_add_plugin('modifier', $_modifier_name); 1923 1924 $this->_parse_vars_props($_modifier_args); 1925 1926 if($_modifier_name == 'default') { 1927 // supress notifications of default modifier vars and args 1928 if(substr($output, 0, 1) == '$') { 1929 $output = '@' . $output; 1930 } 1931 if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') { 1932 $_modifier_args[0] = '@' . $_modifier_args[0]; 1933 } 1934 } 1935 if (count($_modifier_args) > 0) 1936 $_modifier_args = ', '.implode(', ', $_modifier_args); 1937 else 1938 $_modifier_args = ''; 1939 1940 if ($_map_array) { 1941 $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))"; 1942 1943 } else { 1944 1945 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)"; 1946 1947 } 1948 } 1949 } 1950 1951 1952 /** 1953 * add plugin 1954 * 1955 * @param string $type 1956 * @param string $name 1957 * @param boolean? $delayed_loading 1958 */ 1959 function _add_plugin($type, $name, $delayed_loading = null) 1960 { 1961 if (!isset($this->_plugin_info[$type])) { 1962 $this->_plugin_info[$type] = array(); 1963 } 1964 if (!isset($this->_plugin_info[$type][$name])) { 1965 $this->_plugin_info[$type][$name] = array($this->_current_file, 1966 $this->_current_line_no, 1967 $delayed_loading); 1968 } 1969 } 1970 1971 1972 /** 1973 * Compiles references of type $smarty.foo 1974 * 1975 * @param string $indexes 1976 * @return string 1977 */ 1978 function _compile_smarty_ref(&$indexes) 1979 { 1980 /* Extract the reference name. */ 1981 $_ref = substr($indexes[0], 1); 1982 foreach($indexes as $_index_no=>$_index) { 1983 if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) { 1984 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__); 1985 } 1986 } 1987 1988 switch ($_ref) { 1989 case 'now': 1990 $compiled_ref = 'time()'; 1991 $_max_index = 1; 1992 break; 1993 1994 case 'foreach': 1995 array_shift($indexes); 1996 $_var = $this->_parse_var_props(substr($indexes[0], 1)); 1997 $_propname = substr($indexes[1], 1); 1998 $_max_index = 1; 1999 switch ($_propname) { 2000 case 'index': 2001 array_shift($indexes); 2002 $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)"; 2003 break; 2004 2005 case 'first': 2006 array_shift($indexes); 2007 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)"; 2008 break; 2009 2010 case 'last': 2011 array_shift($indexes); 2012 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])"; 2013 break; 2014 2015 case 'show': 2016 array_shift($indexes); 2017 $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)"; 2018 break; 2019 2020 default: 2021 unset($_max_index); 2022 $compiled_ref = "\$this->_foreach[$_var]"; 2023 } 2024 break; 2025 2026 case 'section': 2027 array_shift($indexes); 2028 $_var = $this->_parse_var_props(substr($indexes[0], 1)); 2029 $compiled_ref = "\$this->_sections[$_var]"; 2030 break; 2031 2032 case 'get': 2033 $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']"; 2034 break; 2035 2036 case 'post': 2037 $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']"; 2038 break; 2039 2040 case 'cookies': 2041 $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']"; 2042 break; 2043 2044 case 'env': 2045 $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']"; 2046 break; 2047 2048 case 'server': 2049 $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']"; 2050 break; 2051 2052 case 'session': 2053 $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']"; 2054 break; 2055 2056 /* 2057 * These cases are handled either at run-time or elsewhere in the 2058 * compiler. 2059 */ 2060 case 'request': 2061 if ($this->request_use_auto_globals) { 2062 $compiled_ref = '$_REQUEST'; 2063 break; 2064 } else { 2065 $this->_init_smarty_vars = true; 2066 } 2067 return null; 2068 2069 case 'capture': 2070 return null; 2071 2072 case 'template': 2073 $compiled_ref = "'$this->_current_file'"; 2074 $_max_index = 1; 2075 break; 2076 2077 case 'version': 2078 $compiled_ref = "'$this->_version'"; 2079 $_max_index = 1; 2080 break; 2081 2082 case 'const': 2083 if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) { 2084 $this->_syntax_error("(secure mode) constants not permitted", 2085 E_USER_WARNING, __FILE__, __LINE__); 2086 return; 2087 } 2088 array_shift($indexes); 2089 if (preg_match('!^\.\w+$!', $indexes[0])) { 2090 $compiled_ref = '@' . substr($indexes[0], 1); 2091 } else { 2092 $_val = $this->_parse_var_props(substr($indexes[0], 1)); 2093 $compiled_ref = '@constant(' . $_val . ')'; 2094 } 2095 $_max_index = 1; 2096 break; 2097 2098 case 'config': 2099 $compiled_ref = "\$this->_config[0]['vars']"; 2100 $_max_index = 3; 2101 break; 2102 2103 case 'ldelim': 2104 $compiled_ref = "'$this->left_delimiter'"; 2105 break; 2106 2107 case 'rdelim': 2108 $compiled_ref = "'$this->right_delimiter'"; 2109 break; 2110 2111 default: 2112 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__); 2113 break; 2114 } 2115 2116 if (isset($_max_index) && count($indexes) > $_max_index) { 2117 $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__); 2118 } 2119 2120 array_shift($indexes); 2121 return $compiled_ref; 2122 } 2123 2124 /** 2125 * compiles call to plugin of type $type with name $name 2126 * returns a string containing the function-name or method call 2127 * without the paramter-list that would have follow to make the 2128 * call valid php-syntax 2129 * 2130 * @param string $type 2131 * @param string $name 2132 * @return string 2133 */ 2134 function _compile_plugin_call($type, $name) { 2135 if (isset($this->_plugins[$type][$name])) { 2136 /* plugin loaded */ 2137 if (is_array($this->_plugins[$type][$name][0])) { 2138 return ((is_object($this->_plugins[$type][$name][0][0])) ? 2139 "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */ 2140 : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */ 2141 ). $this->_plugins[$type][$name][0][1]; 2142 2143 } else { 2144 /* function callback */ 2145 return $this->_plugins[$type][$name][0]; 2146 2147 } 2148 } else { 2149 /* plugin not loaded -> auto-loadable-plugin */ 2150 return 'smarty_'.$type.'_'.$name; 2151 2152 } 2153 } 2154 2155 /** 2156 * load pre- and post-filters 2157 */ 2158 function _load_filters() 2159 { 2160 if (count($this->_plugins['prefilter']) > 0) { 2161 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) { 2162 if ($prefilter === false) { 2163 unset($this->_plugins['prefilter'][$filter_name]); 2164 $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false))); 2165 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php'); 2166 smarty_core_load_plugins($_params, $this); 2167 } 2168 } 2169 } 2170 if (count($this->_plugins['postfilter']) > 0) { 2171 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) { 2172 if ($postfilter === false) { 2173 unset($this->_plugins['postfilter'][$filter_name]); 2174 $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false))); 2175 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php'); 2176 smarty_core_load_plugins($_params, $this); 2177 } 2178 } 2179 } 2180 } 2181 2182 2183 /** 2184 * Quote subpattern references 2185 * 2186 * @param string $string 2187 * @return string 2188 */ 2189 function _quote_replace($string) 2190 { 2191 return strtr($string, array('\\' => '\\\\', '$' => '\\$')); 2192 } 2193 2194 /** 2195 * display Smarty syntax error 2196 * 2197 * @param string $error_msg 2198 * @param integer $error_type 2199 * @param string $file 2200 * @param integer $line 2201 */ 2202 function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null) 2203 { 2204 $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type); 2205 } 2206 2207 2208 /** 2209 * check if the compilation changes from cacheable to 2210 * non-cacheable state with the beginning of the current 2211 * plugin. return php-code to reflect the transition. 2212 * @return string 2213 */ 2214 function _push_cacheable_state($type, $name) { 2215 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4]; 2216 if ($_cacheable 2217 || 0<$this->_cacheable_state++) return ''; 2218 if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty')); 2219 $_ret = 'if ($this->caching && !$this->_cache_including) { echo \'{nocache:' 2220 . $this->_cache_serial . '#' . $this->_nocache_count 2221 . '}\'; };'; 2222 return $_ret; 2223 } 2224 2225 2226 /** 2227 * check if the compilation changes from non-cacheable to 2228 * cacheable state with the end of the current plugin return 2229 * php-code to reflect the transition. 2230 * @return string 2231 */ 2232 function _pop_cacheable_state($type, $name) { 2233 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4]; 2234 if ($_cacheable 2235 || --$this->_cacheable_state>0) return ''; 2236 return 'if ($this->caching && !$this->_cache_including) { echo \'{/nocache:' 2237 . $this->_cache_serial . '#' . ($this->_nocache_count++) 2238 . '}\'; };'; 2239 } 2240 2241 2242 /** 2243 * push opening tag-name, file-name and line-number on the tag-stack 2244 * @param string the opening tag's name 2245 */ 2246 function _push_tag($open_tag) 2247 { 2248 array_push($this->_tag_stack, array($open_tag, $this->_current_line_no)); 2249 } 2250 2251 /** 2252 * pop closing tag-name 2253 * raise an error if this stack-top doesn't match with the closing tag 2254 * @param string the closing tag's name 2255 * @return string the opening tag's name 2256 */ 2257 function _pop_tag($close_tag) 2258 { 2259 $message = ''; 2260 if (count($this->_tag_stack)>0) { 2261 list($_open_tag, $_line_no) = array_pop($this->_tag_stack); 2262 if ($close_tag == $_open_tag) { 2263 return $_open_tag; 2264 } 2265 if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) { 2266 return $this->_pop_tag($close_tag); 2267 } 2268 if ($close_tag == 'section' && $_open_tag == 'sectionelse') { 2269 $this->_pop_tag($close_tag); 2270 return $_open_tag; 2271 } 2272 if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') { 2273 $this->_pop_tag($close_tag); 2274 return $_open_tag; 2275 } 2276 if ($_open_tag == 'else' || $_open_tag == 'elseif') { 2277 $_open_tag = 'if'; 2278 } elseif ($_open_tag == 'sectionelse') { 2279 $_open_tag = 'section'; 2280 } elseif ($_open_tag == 'foreachelse') { 2281 $_open_tag = 'foreach'; 2282 } 2283 $message = " expected {/$_open_tag} (opened line $_line_no)."; 2284 } 2285 $this->_syntax_error("mismatched tag {/$close_tag}.$message", 2286 E_USER_ERROR, __FILE__, __LINE__); 2287 } 2288 2289 } 2290 2291 /** 2292 * compare to values by their string length 2293 * 2294 * @access private 2295 * @param string $a 2296 * @param string $b 2297 * @return 0|-1|1 2298 */ 2299 function _smarty_sort_length($a, $b) 2300 { 2301 if($a == $b) 2302 return 0; 2303 2304 if(strlen($a) == strlen($b)) 2305 return ($a > $b) ? -1 : 1; 2306 2307 return (strlen($a) > strlen($b)) ? -1 : 1; 2308 } 2309 2310 2311 /* vim: set et: */ 2312 2313 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Feb 25 10:22:19 2007 | par Balluche grâce à PHPXref 0.7 |