| [ Index ] |
XOOPS v2.2 Reference |
[Summary view] [Print] [Text view]
1 <?php 2 3 if (!defined('SMARTY_DIR')) { 4 exit(); 5 } 6 /** 7 * Project: Smarty: the PHP compiling template engine 8 * File: Smarty_Compiler.class.php 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 * 24 * @link http://smarty.php.net/ 25 * @author Monte Ohrt <monte@ispi.net> 26 * @author Andrei Zmievski <andrei@php.net> 27 * @version 2.6.5-dev 28 * @copyright 2001-2004 ispi of Lincoln, Inc. 29 * @package Smarty 30 */ 31 32 /* $Id: Smarty_Compiler.class.php,v 1.3.6.1 2005/08/14 19:17:50 skalpa Exp $ */ 33 34 /** 35 * Template compiling class 36 * @package Smarty 37 */ 38 class Smarty_Compiler extends Smarty { 39 40 // internal vars 41 /**#@+ 42 * @access private 43 */ 44 var $_folded_blocks = array(); // keeps folded template blocks 45 var $_current_file = null; // the current template being compiled 46 var $_current_line_no = 1; // line number for error messages 47 var $_capture_stack = array(); // keeps track of nested capture buffers 48 var $_plugin_info = array(); // keeps track of plugins to load 49 var $_init_smarty_vars = false; 50 var $_permitted_tokens = array('true','false','yes','no','on','off','null'); 51 var $_db_qstr_regexp = null; // regexps are setup in the constructor 52 var $_si_qstr_regexp = null; 53 var $_qstr_regexp = null; 54 var $_func_regexp = null; 55 var $_var_bracket_regexp = null; 56 var $_dvar_guts_regexp = null; 57 var $_dvar_regexp = null; 58 var $_cvar_regexp = null; 59 var $_svar_regexp = null; 60 var $_avar_regexp = null; 61 var $_mod_regexp = null; 62 var $_var_regexp = null; 63 var $_parenth_param_regexp = null; 64 var $_func_call_regexp = null; 65 var $_obj_ext_regexp = null; 66 var $_obj_start_regexp = null; 67 var $_obj_params_regexp = null; 68 var $_obj_call_regexp = null; 69 var $_cacheable_state = 0; 70 var $_cache_attrs_count = 0; 71 var $_nocache_count = 0; 72 var $_cache_serial = null; 73 var $_cache_include = null; 74 75 var $_strip_depth = 0; 76 var $_additional_newline = "\n"; 77 78 /**#@-*/ 79 /** 80 * The class constructor. 81 */ 82 function Smarty_Compiler() 83 { 84 // matches double quoted strings: 85 // "foobar" 86 // "foo\"bar" 87 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; 88 89 // matches single quoted strings: 90 // 'foobar' 91 // 'foo\'bar' 92 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; 93 94 // matches single or double quoted strings 95 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')'; 96 97 // matches bracket portion of vars 98 // [0] 99 // [foo] 100 // [$bar] 101 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]'; 102 103 // matches numerical constants 104 // 30 105 // -12 106 // 13.22 107 $this->_num_const_regexp = '\-?\d+(?:\.\d+)?'; 108 109 // matches $ vars (not objects): 110 // $foo 111 // $foo.bar 112 // $foo.bar.foobar 113 // $foo[0] 114 // $foo[$bar] 115 // $foo[5][blah] 116 // $foo[5].bar[$foobar][4] 117 $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))'; 118 $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]'; 119 $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp 120 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?'; 121 $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp; 122 123 // matches config vars: 124 // #foo# 125 // #foobar123_foo# 126 $this->_cvar_regexp = '\#\w+\#'; 127 128 // matches section vars: 129 // %foo.bar% 130 $this->_svar_regexp = '\%\w+\.\w+\%'; 131 132 // matches all valid variables (no quotes, no modifiers) 133 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|' 134 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')'; 135 136 // matches valid variable syntax: 137 // $foo 138 // $foo 139 // #foo# 140 // #foo# 141 // "text" 142 // "text" 143 $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_num_const_regexp . '|' . $this->_qstr_regexp . ')'; 144 145 // matches valid object call (one level of object nesting allowed in parameters): 146 // $foo->bar 147 // $foo->bar() 148 // $foo->bar("text") 149 // $foo->bar($foo, $bar, "text") 150 // $foo->bar($foo, "foo") 151 // $foo->bar->foo() 152 // $foo->bar->foo->bar() 153 // $foo->bar($foo->bar) 154 // $foo->bar($foo->bar()) 155 // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar)) 156 $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')'; 157 $this->_obj_restricted_param_regexp = '(?:' 158 . $this->_var_regexp . '(?:' . $this->_obj_ext_regexp . '(?:\((?:' . $this->_var_regexp 159 . '(?:\s*,\s*' . $this->_var_regexp . ')*)?\))?)*)'; 160 $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|' 161 . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)'; 162 $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp 163 . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)'; 164 $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)'; 165 $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 . ')*)?)'; 166 167 // matches valid modifier syntax: 168 // |foo 169 // |@foo 170 // |foo:"bar" 171 // |foo:$bar 172 // |foo:"bar":$foobar 173 // |foo|bar 174 // |foo:$foo->bar 175 $this->_mod_regexp = '(?:\|@?\w+(?::(?>-?\w+|' 176 . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)'; 177 178 // matches valid function name: 179 // foo123 180 // _foo_bar 181 $this->_func_regexp = '[a-zA-Z_]\w*'; 182 183 // matches valid registered object: 184 // foo->bar 185 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*'; 186 187 // matches valid parameter values: 188 // true 189 // $foo 190 // $foo|bar 191 // #foo# 192 // #foo#|bar 193 // "text" 194 // "text"|bar 195 // $foo->bar 196 $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|' 197 . $this->_var_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)'; 198 199 // matches valid parenthesised function parameters: 200 // 201 // "text" 202 // $foo, $bar, "text" 203 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar 204 $this->_parenth_param_regexp = '(?:\((?:\w+|' 205 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|' 206 . $this->_param_regexp . ')))*)?\))'; 207 208 // matches valid function call: 209 // foo() 210 // foo_bar($foo) 211 // _foo_bar($foo,"bar") 212 // foo123($foo,$foo->bar(),"foo") 213 $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:' 214 . $this->_parenth_param_regexp . '))'; 215 } 216 217 /** 218 * compile a resource 219 * 220 * sets $compiled_content to the compiled source 221 * @param string $resource_name 222 * @param string $source_content 223 * @param string $compiled_content 224 * @return true 225 */ 226 function _compile_file($resource_name, $source_content, &$compiled_content) 227 { 228 229 if ($this->security) { 230 // do not allow php syntax to be executed unless specified 231 if ($this->php_handling == SMARTY_PHP_ALLOW && 232 !$this->security_settings['PHP_HANDLING']) { 233 $this->php_handling = SMARTY_PHP_PASSTHRU; 234 } 235 } 236 237 $this->_load_filters(); 238 239 $this->_current_file = $resource_name; 240 $this->_current_line_no = 1; 241 $ldq = preg_quote($this->left_delimiter, '~'); 242 $rdq = preg_quote($this->right_delimiter, '~'); 243 244 // run template source through prefilter functions 245 if (count($this->_plugins['prefilter']) > 0) { 246 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) { 247 if ($prefilter === false) continue; 248 if ($prefilter[3] || is_callable($prefilter[0])) { 249 $source_content = call_user_func_array($prefilter[0], 250 array($source_content, &$this)); 251 $this->_plugins['prefilter'][$filter_name][3] = true; 252 } else { 253 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented"); 254 } 255 } 256 } 257 258 /* fetch all special blocks */ 259 $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s"; 260 261 preg_match_all($search, $source_content, $match, PREG_SET_ORDER); 262 $this->_folded_blocks = $match; 263 reset($this->_folded_blocks); 264 265 /* replace special blocks by "{php}" */ 266 $source_content = preg_replace($search.'e', "'" 267 . $this->_quote_replace($this->left_delimiter) . 'php' 268 . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'" 269 . $this->_quote_replace($this->right_delimiter) 270 . "'" 271 , $source_content); 272 273 /* Gather all template tags. */ 274 preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match); 275 $template_tags = $_match[1]; 276 /* Split content by template tags to obtain non-template content. */ 277 $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content); 278 279 /* loop through text blocks */ 280 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) { 281 /* match anything resembling php tags */ 282 if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) { 283 /* replace tags with placeholders to prevent recursive replacements */ 284 $sp_match[1] = array_unique($sp_match[1]); 285 usort($sp_match[1], '_smarty_sort_length'); 286 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) { 287 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]); 288 } 289 /* process each one */ 290 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) { 291 if ($this->php_handling == SMARTY_PHP_PASSTHRU) { 292 /* echo php contents */ 293 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]); 294 } else if ($this->php_handling == SMARTY_PHP_QUOTE) { 295 /* quote php tags */ 296 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]); 297 } else if ($this->php_handling == SMARTY_PHP_REMOVE) { 298 /* remove php tags */ 299 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]); 300 } else { 301 /* SMARTY_PHP_ALLOW, but echo non php starting tags */ 302 $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]); 303 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]); 304 } 305 } 306 } 307 } 308 309 /* Compile the template tags into PHP code. */ 310 $compiled_tags = array(); 311 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) { 312 $this->_current_line_no += substr_count($text_blocks[$i], "\n"); 313 $compiled_tags[] = $this->_compile_tag($template_tags[$i]); 314 $this->_current_line_no += substr_count($template_tags[$i], "\n"); 315 } 316 if (count($this->_tag_stack)>0) { 317 list($_open_tag, $_line_no) = end($this->_tag_stack); 318 $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__); 319 return; 320 } 321 322 $compiled_content = ''; 323 324 /* Interleave the compiled contents and text blocks to get the final result. */ 325 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { 326 if ($compiled_tags[$i] == '') { 327 // tag result empty, remove first newline from following text block 328 $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]); 329 } 330 $compiled_content .= $text_blocks[$i].$compiled_tags[$i]; 331 } 332 $compiled_content .= $text_blocks[$i]; 333 334 /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */ 335 if (preg_match_all("~{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}~s", $compiled_content, $_match)) { 336 $strip_tags = $_match[0]; 337 $strip_tags_modified = preg_replace("~{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+~m", '', $strip_tags); 338 $strip_tags_modified = preg_replace('~[\r\n]+~m', '', $strip_tags_modified); 339 for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++) 340 $compiled_content = preg_replace("~{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}~s", 341 $this->_quote_replace($strip_tags_modified[$i]), 342 $compiled_content, 1); 343 } 344 345 // remove \n from the end of the file, if any 346 if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) { 347 $compiled_content = substr($compiled_content, 0, -1); 348 } 349 350 if (!empty($this->_cache_serial)) { 351 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content; 352 } 353 354 // remove unnecessary close/open tags 355 $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content); 356 357 // run compiled template through postfilter functions 358 if (count($this->_plugins['postfilter']) > 0) { 359 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) { 360 if ($postfilter === false) continue; 361 if ($postfilter[3] || is_callable($postfilter[0])) { 362 $compiled_content = call_user_func_array($postfilter[0], 363 array($compiled_content, &$this)); 364 $this->_plugins['postfilter'][$filter_name][3] = true; 365 } else { 366 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented"); 367 } 368 } 369 } 370 371 // put header at the top of the compiled template 372 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; 373 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n"; 374 375 /* Emit code to load needed plugins. */ 376 $this->_plugins_code = ''; 377 if (count($this->_plugin_info)) { 378 $_plugins_params = "array('plugins' => array("; 379 foreach ($this->_plugin_info as $plugin_type => $plugins) { 380 foreach ($plugins as $plugin_name => $plugin_info) { 381 $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], "; 382 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),'; 383 } 384 } 385 $_plugins_params .= '))'; 386 $plugins_code = "<?php require_once (SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n"; 387 $template_header .= $plugins_code; 388 $this->_plugin_info = array(); 389 $this->_plugins_code = $plugins_code; 390 } 391 392 if ($this->_init_smarty_vars) { 393 $template_header .= "<?php require_once (SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n"; 394 $this->_init_smarty_vars = false; 395 } 396 397 $compiled_content = $template_header . $compiled_content; 398 return true;