| [ Index ] |
krapohl.info |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * Project: Smarty: the PHP compiling template engine 5 * File: Smarty_Compiler.class.php 6 * Author: Monte Ohrt <monte@ispi.net> 7 * Andrei Zmievski <andrei@php.net> 8 * 9 * Version: 2.3.1 10 * Copyright: 2001,2002 ispi of Lincoln, Inc. 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Lesser General Public 14 * License as published by the Free Software Foundation; either 15 * version 2.1 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Lesser General Public License for more details. 21 * 22 * You should have received a copy of the GNU Lesser General Public 23 * License along with this library; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 * 26 * You may contact the authors of Smarty by e-mail at: 27 * monte@ispi.net 28 * andrei@php.net 29 * 30 * Or, write to: 31 * Monte Ohrt 32 * Director of Technology, ispi 33 * 237 S. 70th suite 220 34 * Lincoln, NE 68510 35 * 36 * The latest version of Smarty can be obtained from: 37 * http://www.phpinsider.com/ 38 * 39 */ 40 41 class Smarty_Compiler extends Smarty { 42 43 // internal vars 44 var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part 45 var $_foreachelse_stack = array(); // keeps track of whether foreach had 'else' part 46 var $_literal_blocks = array(); // keeps literal template blocks 47 var $_php_blocks = array(); // keeps php code blocks 48 var $_current_file = null; // the current template being compiled 49 var $_current_line_no = 1; // line number for error messages 50 var $_capture_stack = array(); // keeps track of nested capture buffers 51 var $_plugin_info = array(); // keeps track of plugins to load 52 var $_init_smarty_vars = false; 53 54 55 /*======================================================================*\ 56 Function: _compile_file() 57 Input: compile a template file 58 \*======================================================================*/ 59 function _compile_file($tpl_file, $template_source, &$template_compiled) 60 { 61 if ($this->security) { 62 // do not allow php syntax to be executed unless specified 63 if ($this->php_handling == SMARTY_PHP_ALLOW && 64 !$this->security_settings['PHP_HANDLING']) { 65 $this->php_handling = SMARTY_PHP_PASSTHRU; 66 } 67 } 68 69 $this->_load_filters(); 70 71 $this->_current_file = $tpl_file; 72 $this->_current_line_no = 1; 73 $ldq = preg_quote($this->left_delimiter, '!'); 74 $rdq = preg_quote($this->right_delimiter, '!'); 75 76 // run template source through prefilter functions 77 if (count($this->_plugins['prefilter']) > 0) { 78 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) { 79 if ($prefilter === false) continue; 80 if ($prefilter[3] || function_exists($prefilter[0])) { 81 $template_source = $prefilter[0]($template_source, $this); 82 $this->_plugins['prefilter'][$filter_name][3] = true; 83 } else { 84 $this->_trigger_plugin_error("Smarty plugin error: prefilter '$filter_name' is not implemented"); 85 } 86 } 87 } 88 89 /* Annihilate the comments. */ 90 $template_source = preg_replace("!({$ldq})\*(.*?)\*({$rdq})!se", 91 "'\\1*'.str_repeat(\"\n\", substr_count('\\2', \"\n\")) .'*\\3'", 92 $template_source); 93 94 /* Pull out the literal blocks. */ 95 preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_source, $match); 96 $this->_literal_blocks = $match[1]; 97 $template_source = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", 98 $this->quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $template_source); 99 100 /* Pull out the php code blocks. */ 101 preg_match_all("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", $template_source, $match); 102 $this->_php_blocks = $match[1]; 103 $template_source = preg_replace("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", 104 $this->quote_replace($this->left_delimiter.'php'.$this->right_delimiter), $template_source); 105 106 /* Gather all template tags. */ 107 preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_source, $match); 108 $template_tags = $match[1]; 109 /* Split content by template tags to obtain non-template content. */ 110 $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_source); 111 112 /* loop through text blocks */ 113 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) { 114 /* match anything within <? ?> */ 115 if (preg_match_all('!(<\?[^?]*?\?>|<script\s+language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $text_blocks[$curr_tb], $sp_match)) { 116 /* found at least one match, loop through each one */ 117 for ($curr_sp = 0, $for_max2 = count($sp_match[0]); $curr_sp < $for_max2; $curr_sp++) { 118 if (preg_match('!^(<\?(php\s|\s|=\s)|<script\s*language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $sp_match[0][$curr_sp])) { 119 /* php tag */ 120 if ($this->php_handling == SMARTY_PHP_PASSTHRU) { 121 /* echo php contents */ 122 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]); 123 } else if ($this->php_handling == SMARTY_PHP_QUOTE) { 124 /* quote php tags */ 125 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], htmlspecialchars($sp_match[0][$curr_sp]), $text_blocks[$curr_tb]); 126 } else if ($this->php_handling == SMARTY_PHP_REMOVE) { 127 /* remove php tags */ 128 if (substr($sp_match[0][$curr_sp], 0, 2) == '<?') 129 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '', $text_blocks[$curr_tb]); 130 else 131 /* attempt to remove everything between <script ...> and </script> */ 132 $text_blocks[$curr_tb] = preg_replace('!'.preg_quote($sp_match[0][$curr_sp], '!').'.*?</script\s*>!is', '', $text_blocks[$curr_tb]); 133 } 134 } else 135 /* echo the non-php tags */ 136 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]); 137 } 138 } 139 } 140 141 /* Compile the template tags into PHP code. */ 142 $compiled_tags = array(); 143 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) { 144 $this->_current_line_no += substr_count($text_blocks[$i], "\n"); 145 $compiled_tags[] = $this->_compile_tag($template_tags[$i]); 146 $this->_current_line_no += substr_count($template_tags[$i], "\n"); 147 } 148 149 $template_compiled = ''; 150 151 /* Interleave the compiled contents and text blocks to get the final result. */ 152 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { 153 $template_compiled .= $text_blocks[$i].$compiled_tags[$i]; 154 } 155 $template_compiled .= $text_blocks[$i]; 156 157 /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */ 158 if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $template_compiled, $match)) { 159 $strip_tags = $match[0]; 160 $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags); 161 $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified); 162 for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++) 163 $template_compiled = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", 164 $this->quote_replace($strip_tags_modified[$i]), 165 $template_compiled, 1); 166 } 167 168 // remove \n from the end of the file, if any 169 if ($template_compiled{strlen($template_compiled) - 1} == "\n" ) { 170 $template_compiled = substr($template_compiled, 0, -1); 171 } 172 173 // run compiled template through postfilter functions 174 if (count($this->_plugins['postfilter']) > 0) { 175 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) { 176 if ($postfilter === false) continue; 177 if ($postfilter[3] || function_exists($postfilter[0])) { 178 $template_compiled = $postfilter[0]($template_compiled, $this); 179 $this->_plugins['postfilter'][$filter_name][3] = true; 180 } else { 181 $this->_trigger_plugin_error("Smarty plugin error: postfilter '$filter_name' is not implemented"); 182 } 183 } 184 } 185 186 // put header at the top of the compiled template 187 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; 188 $template_header .= " compiled from ".$tpl_file." */ ?>\n"; 189 190 /* Emit code to load needed plugins. */ 191 if (count($this->_plugin_info)) { 192 $plugins_code = '<?php $this->_load_plugins(array('; 193 foreach ($this->_plugin_info as $plugin_type => $plugins) { 194 foreach ($plugins as $plugin_name => $plugin_info) { 195 $plugins_code .= "\narray('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], "; 196 $plugins_code .= $plugin_info[2] ? 'true),' : 'false),'; 197 } 198 } 199 $plugins_code .= ")); ?>"; 200 $template_header .= $plugins_code; 201 $this->_plugin_info = array(); 202 } 203 204 if ($this->_init_smarty_vars) { 205 $template_header .= "<?php \$this->_assign_smarty_interface(); ?>\n"; 206 $this->_init_smarty_vars = false; 207 } 208 209 $template_compiled = $template_header . $template_compiled; 210 211 return true; 212 } 213 214 215 /*======================================================================*\ 216 Function: _compile_tag 217 Purpose: Compile a template tag 218 \*======================================================================*/ 219 function _compile_tag($template_tag) 220 { 221 /* Matched comment. */ 222 if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*') 223 return ''; 224 225 $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; 226 227 /* Split tag into two parts: command and the arguments. */ 228 preg_match('/^( 229 (?: ' . $qstr_regexp . ' | (?>[^"\'\s]+))+ 230 ) 231 (?:\s+(.*))? 232 /xs', $template_tag, $match); 233 $tag_command = $match[1]; 234 $tag_args = isset($match[2]) ? $match[2] : ''; 235 236 /* If the tag name matches a variable or section property definition, 237 we simply process it. */ 238 if (preg_match('!^\$\w+(?>(\[(\d+|\$\w+|\w+(\.\w+)?)\])|((\.|->)\$?\w+))*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // if a variable 239 preg_match('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // or a configuration variable 240 preg_match('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command)) { // or a section property 241 settype($tag_command, 'array'); 242 $this->_parse_vars_props($tag_command); 243 return "<?php echo $tag_command[0]; ?>\n"; 244 } 245 246 switch ($tag_command) { 247 case 'include': 248 return $this->_compile_include_tag($tag_args); 249 250 case 'include_php': 251 return $this->_compile_include_php_tag($tag_args); 252 253 case 'if': 254 return $this->_compile_if_tag($tag_args); 255 256 case 'else': 257 return '<?php else: ?>'; 258 259 case 'elseif': 260 return $this->_compile_if_tag($tag_args, true); 261 262 case '/if': 263 return '<?php endif; ?>'; 264 265 case 'capture': 266 return $this->_compile_capture_tag(true, $tag_args); 267 268 case '/capture': 269 return $this->_compile_capture_tag(false); 270 271 case 'ldelim': 272 return $this->left_delimiter; 273 274 case 'rdelim': 275 return $this->right_delimiter; 276 277 case 'section': 278 array_push($this->_sectionelse_stack, false); 279 return $this->_compile_section_start($tag_args); 280 281 case 'sectionelse': 282 $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true; 283 return "<?php endfor; else: ?>"; 284 285 case '/section': 286 if (array_pop($this->_sectionelse_stack)) 287 return "<?php endif; ?>"; 288 else 289 return "<?php endfor; endif; ?>"; 290 291 case 'foreach': 292 array_push($this->_foreachelse_stack, false); 293 return $this->_compile_foreach_start($tag_args); 294 break; 295 296 case 'foreachelse': 297 $this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true; 298 return "<?php endforeach; else: ?>"; 299 300 case '/foreach': 301 if (array_pop($this->_foreachelse_stack)) 302 return "<?php endif; ?>"; 303 else 304 return "<?php endforeach; endif; ?>"; 305 306 case 'config_load': 307 return $this->_compile_config_load_tag($tag_args); 308 309 case 'strip': 310 case '/strip': 311 return $this->left_delimiter.$tag_command.$this->right_delimiter; 312 313 case 'literal': 314 list (,$literal_block) = each($this->_literal_blocks); 315 $this->_current_line_no += substr_count($literal_block, "\n"); 316 return "<?php echo '".str_replace("'", "\'", str_replace("\\", "\\\\", $literal_block))."'; ?>\n"; 317 318 case 'php': 319 if ($this->security && !$this->security_settings['PHP_TAGS']) { 320 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING); 321 return; 322 } 323 list (,$php_block) = each($this->_php_blocks); 324 $this->_current_line_no += substr_count($php_block, "\n"); 325 return '<?php '.$php_block.' ?>'; 326 327 case 'insert': 328 return $this->_compile_insert_tag($tag_args); 329 330 default: 331 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) { 332 return $output; 333 } else if ($this->_compile_block_tag($tag_command, $tag_args, $output)) { 334 return $output; 335 } else { 336 return $this->_compile_custom_tag($tag_command, $tag_args); 337 } 338 } 339 } 340 341 342 /*======================================================================*\ 343 Function: _compile_compiler_tag 344 Purpose: compile the custom compiler tag 345 \*======================================================================*/ 346 function _compile_compiler_tag($tag_command, $tag_args, &$output) 347 { 348 $found = false; 349 $have_function = true; 350 351 /* 352 * First we check if the compiler function has already been registered 353 * or loaded from a plugin file. 354 */ 355 if (isset($this->_plugins['compiler'][$tag_command])) { 356 $found = true; 357 $plugin_func = $this->_plugins['compiler'][$tag_command][0]; 358 if (!function_exists($plugin_func)) { 359 $message = "compiler function '$tag_command' is not implemented"; 360 $have_function = false; 361 } 362 } 363 /* 364 * Otherwise we need to load plugin file and look for the function 365 * inside it. 366 */ 367 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) { 368 $found = true; 369 370 include_once $plugin_file; 371 372 $plugin_func = 'smarty_compiler_' . $tag_command; 373 if (!function_exists($plugin_func)) { 374 $message = "plugin function $plugin_func() not found in $plugin_file\n"; 375 $have_function = false; 376 } else { 377 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null); 378 } 379 } 380 381 /* 382 * True return value means that we either found a plugin or a 383 * dynamically registered function. False means that we didn't and the 384 * compiler should now emit code to load custom function plugin for this 385 * tag. 386 */ 387 if ($found) { 388 if ($have_function) { 389 $output = '<?php ' . $plugin_func($tag_args, $this) . ' ?>'; 390 } else { 391 $this->_syntax_error($message, E_USER_WARNING); 392 } 393 return true; 394 } else { 395 return false; 396 } 397 } 398 399 400 /*======================================================================*\ 401 Function: _compile_block_tag 402 Purpose: compile block function tag 403 \*======================================================================*/ 404 function _compile_block_tag($tag_command, $tag_args, &$output) 405 { 406 if ($tag_command{0} == '/') { 407 $start_tag = false; 408 $tag_command = substr($tag_command, 1); 409 } else 410 $start_tag = true; 411 412 $found = false; 413 $have_function = true; 414 415 /* 416 * First we check if the block function has already been registered 417 * or loaded from a plugin file. 418 */ 419 if (isset($this->_plugins['block'][$tag_command])) { 420 $found = true; 421 $plugin_func = $this->_plugins['block'][$tag_command][0]; 422 if (!function_exists($plugin_func)) { 423 $message = "block function '