[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /*************************************************************************** 3 * template.inc 4 * ------------------- 5 * begin : Saturday, Feb 13, 2001 6 * copyright : (C) 2001 The phpBB Group 7 * email : support@phpbb.com 8 * 9 * $Id: template.php,v 1.7 2002/01/28 19:12:37 psotfx Exp $ 10 * 11 * 12 ***************************************************************************/ 13 14 /*************************************************************************** 15 * 16 * This program is free software; you can redistribute it and/or modify 17 * it under the terms of the GNU General Public License as published by 18 * the Free Software Foundation; either version 2 of the License, or 19 * (at your option) any later version. 20 * 21 ***************************************************************************/ 22 23 /** 24 * Template class. By Nathan Codding of the phpBB group. 25 * The interface was originally inspired by PHPLib templates, 26 * and the template file formats are quite similar. 27 * 28 */ 29 30 class Template { 31 var $classname = "Template"; 32 33 // variable that holds all the data we'll be substituting into 34 // the compiled templates. 35 // ... 36 // This will end up being a multi-dimensional array like this: 37 // $this->_tpldata[block.][iteration#][child.][iteration#][child2.][iteration#][variablename] == value 38 // if it's a root-level variable, it'll be like this: 39 // $this->_tpldata[.][0][varname] == value 40 var $_tpldata = array(); 41 42 // Hash of filenames for each template handle. 43 var $files = array(); 44 45 // Root template directory. 46 var $root = ""; 47 48 // this will hash handle names to the compiled code for that handle. 49 var $compiled_code = array(); 50 51 // This will hold the uncompiled code for that handle. 52 var $uncompiled_code = array(); 53 54 /** 55 * Constructor. Simply sets the root dir. 56 * 57 */ 58 function Template($root = '.') 59 { 60 global $db; 61 62 $this->set_rootdir($root); 63 $this->db = &$db; 64 65 $this->pparse_order = array(); 66 } 67 68 /** 69 * Destroys this template object. Should be called when you're done with it, in order 70 * to clear out the template data so you can load/parse a new template set. 71 */ 72 function destroy() 73 { 74 $this->_tpldata = array(); 75 } 76 77 /** 78 * Sets the template root directory for this Template object. 79 */ 80 function set_rootdir($dir) 81 { 82 if (!is_dir($dir)) 83 { 84 return false; 85 } 86 87 $this->root = $dir; 88 return true; 89 } 90 91 /** 92 * Sets the template filenames for handles. $filename_array 93 * should be a hash of handle => filename pairs. 94 */ 95 function set_filenames($filename_array) 96 { 97 global $table_prefix; 98 99 if ( !is_array($filename_array) ) 100 { 101 return false; 102 } 103 104 $template_names = ''; 105 @reset($filename_array); 106 while ( list($handle, $filename) = @each($filename_array) ) 107 { 108 $this->files[$handle] = $this->make_filename($filename); 109 $template_names .= ( $template_names != '' ) ? ", '" . addslashes($this->files[$handle]) . "'" : "'" . addslashes($this->files[$handle]) . "'"; 110 } 111 112 $sql = "SELECT * 113 FROM " . $table_prefix . "template_cache 114 WHERE template_name IN ($template_names)"; 115 if ( $result = $this->db->sql_query($sql) ) 116 { 117 while( $row = $this->db->sql_fetchrow($result) ) 118 { 119 if( $row['template_cached'] == filemtime($row['template_name']) ) 120 { 121 $this->compiled_code[$row['template_handle']] = $row['template_compile']; 122 $this->echo_compiled[$row['template_handle']] = $row['template_echo']; 123 } 124 } 125 } 126 127 $this->db->sql_freeresult(); 128 129 return true; 130 } 131 132 133 /** 134 * Load the file for the handle, compile the file, 135 * and run the compiled code. This will print out 136 * the results of executing the template. 137 */ 138 function pparse($handle) 139 { 140 global $table_prefix; 141 142 if( empty($this->compiled_code[$handle]) ) 143 { 144 if ( !$this->loadfile($handle) ) 145 { 146 die("Template->pparse(): Couldn't load template file for handle $handle"); 147 } 148 149 // 150 // Actually compile the code now. 151 // 152 $this->echo_compiled[$handle] = 1; 153 $this->compiled_code[$handle] = $this->compile($this->uncompiled_code[$handle]); 154 155 $sql = "REPLACE INTO " . $table_prefix . "template_cache (template_name, template_handle, template_cached, template_compile) VALUES ('" . addslashes($this->files[$handle]) . "', '" . addslashes($handle) . "', " . filemtime($this->files[$handle]) . ", '" . addslashes($this->compiled_code[$handle]) . "')"; 156 if ( !($result = $this->db->sql_query($sql)) ) 157 { 158 die("Couldn't insert template into cache!"); 159 } 160 161 } 162 163 $_str = ""; 164 eval($this->compiled_code[$handle]); 165 166 if( $_str != "" ) 167 { 168 echo $_str; 169 } 170 171 return true; 172 } 173 174 /** 175 * Inserts the uncompiled code for $handle as the 176 * value of $varname in the root-level. This can be used 177 * to effectively include a template in the middle of another 178 * template. 179 * Note that all desired assignments to the variables in $handle should be done 180 * BEFORE calling this function. 181 */ 182 function assign_var_from_handle($varname, $handle) 183 { 184 global $table_prefix; 185 186 if( empty($this->compiled_code[$handle]) ) 187 { 188 if ( !$this->loadfile($handle) ) 189 { 190 die("Template->pparse(): Couldn't load template file for handle $handle"); 191 } 192 193 $code = $this->compile($this->uncompiled_code[$handle], true, '_str'); 194 195 $sql = "REPLACE INTO " . $table_prefix . "template_cache (template_name, template_handle, template_echo, template_cached, template_compile) VALUES ('" . addslashes($this->files[$handle]) . "', '" . addslashes($handle) . "', 0, " . filemtime($this->files[$handle]) . ", '" . addslashes($code) . "')"; 196 if ( !($result = $this->db->sql_query($sql)) ) 197 { 198 die("Couldn't insert template into cache!"); 199 } 200 } 201 else 202 { 203 $code = $this->compiled_code[$handle]; 204 } 205 206 // Compile It, With The "no Echo Statements" Option On. 207 $_str = ""; 208 // evaluate the variable assignment. 209 eval($code); 210 // assign the value of the generated variable to the given varname. 211 $this->assign_var($varname, $_str); 212 213 return true; 214 } 215 216 /** 217 * Block-level variable assignment. Adds a new block iteration with the given 218 * variable assignments. Note that this should only be called once per block 219 * iteration. 220 */ 221 function assign_block_vars($blockname, $vararray) 222 { 223 if (strstr($blockname, '.')) 224 { 225 // Nested block. 226 $blocks = explode('.', $blockname); 227 $blockcount = sizeof($blocks) - 1; 228 $str = '$this->_tpldata'; 229 for ($i = 0; $i < $blockcount; $i++) 230 { 231 $str .= '[\'' . $blocks[$i] . '.\']'; 232 eval('$lastiteration = sizeof(' . $str . ') - 1;'); 233 $str .= '[' . $lastiteration . ']'; 234 } 235 // Now we add the block that we're actually assigning to. 236 // We're adding a new iteration to this block with the given 237 // variable assignments. 238 $str .= '[\'' . $blocks[$blockcount] . '.\'][] = $vararray;'; 239 240 // Now we evaluate this assignment we've built up. 241 eval($str); 242 } 243 else 244 { 245 // Top-level block. 246 // Add a new iteration to this block with the variable assignments 247 // we were given. 248 $this->_tpldata[$blockname . '.'][] = $vararray; 249 } 250 251 return true; 252 } 253 254 /** 255 * Root-level variable assignment. Adds to current assignments, overriding 256 * any existing variable assignment with the same name. 257 */ 258 function assign_vars($vararray) 259 { 260 reset ($vararray); 261 while (list($key, $val) = each($vararray)) 262 { 263 $this->_tpldata['.'][0][$key] = $val; 264 } 265 266 return true; 267 } 268 269 /** 270 * Root-level variable assignment. Adds to current assignments, overriding 271 * any existing variable assignment with the same name. 272 */ 273 function assign_var($varname, $varval) 274 { 275 $this->_tpldata['.'][0][$varname] = $varval; 276 277 return true; 278 } 279 280 281 /** 282 * Generates a full path+filename for the given filename, which can either 283 * be an absolute name, or a name relative to the rootdir for this Template 284 * object. 285 */ 286 function make_filename($filename) 287 { 288 // Check if it's an absolute or relative path. 289 if (substr($filename, 0, 1) != '/') 290 { 291 $filename = $this->root . '/' . $filename; 292 } 293 294 if (!file_exists($filename)) 295 { 296 die("Template->make_filename(): Error - file $filename does not exist"); 297 } 298 299 return $filename; 300 } 301 302 303 /** 304 * If not already done, load the file for the given handle and populate 305 * the uncompiled_code[] hash with its code. Do not compile. 306 */ 307 function loadfile($handle) 308 { 309 // If the file for this handle is already loaded and compiled, do nothing. 310 if ( !empty($this->uncompiled_code[$handle]) ) 311 { 312 return true; 313 } 314 315 // If we don't have a file assigned to this handle, die. 316 if (!isset($this->files[$handle])) 317 { 318 die("Template->loadfile(): No file specified for handle $handle"); 319 } 320 321 $filename = $this->files[$handle]; 322 323 $str = implode("", @file($filename)); 324 if (empty($str)) 325 { 326 die("Template->loadfile(): File $filename for handle $handle is empty"); 327 } 328 329 $this->uncompiled_code[$handle] = $str; 330 331 return true; 332 } 333 334 335 336 /** 337 * Compiles the given string of code, and returns 338 * the result in a string. 339 * If "do_not_echo" is true, the returned code will not be directly 340 * executable, but can be used as part of a variable assignment 341 * for use in assign_code_from_handle(). 342 */ 343 function compile($code, $do_not_echo = false, $retvar = '') 344 { 345 // replace \ with \\ and then ' with \'. 346 $code = str_replace('\\', '\\\\', $code); 347 $code = str_replace('\'', '\\\'', $code); 348 349 // change template varrefs into PHP varrefs 350 351 // This one will handle varrefs WITH namespaces 352 $varrefs = array(); 353 preg_match_all('#\{(([a-z0-9\-_]+?\.)+?)([a-z0-9\-_]+?)\}#is', $code, $varrefs); 354 $varcount = sizeof($varrefs[1]); 355 for ($i = 0; $i < $varcount; $i++) 356 { 357 $namespace = $varrefs[1][$i]; 358 $varname = $varrefs[3][$i]; 359 $new = $this->generate_block_varref($namespace, $varname); 360 361 $code = str_replace($varrefs[0][$i], $new, $code); 362 } 363 364 // This will handle the remaining root-level varrefs 365 $code = preg_replace('#\{([a-z0-9\-_]*?)\}#is', '\' . ( ( isset($this->_tpldata[\'.\'][0][\'\1\']) ) ? $this->_tpldata[\'.\'][0][\'\1\'] : \'\' ) . \'', $code); 366 367 // Break it up into lines. 368 $code_lines = explode("\n", $code); 369 370 $block_nesting_level = 0; 371 $block_names = array(); 372 $block_names[0] = "."; 373 374 // Second: prepend echo ', append ' . "\n"; to each line. 375 $line_count = sizeof($code_lines); 376 for ($i = 0; $i < $line_count; $i++) 377 { 378 $code_lines[$i] = chop($code_lines[$i]); 379 if (preg_match('#<!-- BEGIN (.*?) -->#', $code_lines[$i], $m)) 380 { 381 $n[0] = $m[0]; 382 $n[1] = $m[1]; 383 384 // Added: dougk_ff7-Keeps templates from bombing if begin is on the same line as end.. I think. :) 385 if ( preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $n) ) 386 { 387 $block_nesting_level++; 388 $block_names[$block_nesting_level] = $m[1]; 389 if ($block_nesting_level < 2) 390 { 391 // Block is not nested. 392 $code_lines[$i] = '$_' . $a[1] . '_count = ( isset($this->_tpldata[\'' . $n[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $n[1] . '.\']) : 0;'; 393 $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)'; 394 $code_lines[$i] .= "\n" . '{'; 395 } 396 else 397 { 398 // This block is nested. 399 400 // Generate a namespace string for this block. 401 $namespace = implode('.', $block_names); 402 // strip leading period from root level.. 403 $namespace = substr($namespace, 2); 404 // Get a reference to the data array for this block that depends on the 405 // current indices of all parent blocks. 406 $varref = $this->generate_block_data_ref($namespace, false); 407 // Create the for loop code to iterate over this block. 408 $code_lines[$i] = '$_' . $a[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;'; 409 $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)'; 410 $code_lines[$i] .= "\n" . '{'; 411 } 412 413 // We have the end of a block. 414 unset($block_names[$block_nesting_level]); 415 $block_nesting_level--; 416 $code_lines[$i] .= '} // END ' . $n[1]; 417 $m[0] = $n[0]; 418 $m[1] = $n[1]; 419 } 420 else 421 { 422 // We have the start of a block. 423 $block_nesting_level++; 424 $block_names[$block_nesting_level] = $m[1]; 425 if ($block_nesting_level < 2) 426 { 427 // Block is not nested. 428 $code_lines[$i] = '$_' . $m[1] . '_count = ( isset($this->_tpldata[\'' . $m[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $m[1] . '.\']) : 0;'; 429 $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)'; 430 $code_lines[$i] .= "\n" . '{'; 431 } 432 else 433 { 434 // This block is nested. 435 436 // Generate a namespace string for this block. 437 $namespace = implode('.', $block_names); 438 // strip leading period from root level.. 439 $namespace = substr($namespace, 2); 440 // Get a reference to the data array for this block that depends on the 441 // current indices of all parent blocks. 442 $varref = $this->generate_block_data_ref($namespace, false); 443 // Create the for loop code to iterate over this block. 444 $code_lines[$i] = '$_' . $m[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;'; 445 $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)'; 446 $code_lines[$i] .= "\n" . '{'; 447 } 448 } 449 } 450 else if (preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $m)) 451 { 452 // We have the end of a block. 453 unset($block_names[$block_nesting_level]); 454 $block_nesting_level--; 455 $code_lines[$i] = '} // END ' . $m[1]; 456 } 457 else 458 { 459 // We have an ordinary line of code. 460 if (!$do_not_echo) 461 { 462 $code_lines[$i] = 'echo \'' . $code_lines[$i] . '\' . "\\n";'; 463 } 464 else 465 { 466 $code_lines[$i] = '$' . $retvar . '.= \'' . $code_lines[$i] . '\' . "\\n";'; 467 } 468 } 469 } 470 471 // Bring it back into a single string of lines of code. 472 $code = implode("\n", $code_lines); 473 return $code ; 474 475 } 476 477 478 /** 479 * Generates a reference to the given variable inside the given (possibly nested) 480 * block namespace. This is a string of the form: 481 * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . ' 482 * It's ready to be inserted into an "echo" line in one of the templates. 483 * NOTE: expects a trailing "." on the namespace. 484 */ 485 function generate_block_varref($namespace, $varname) 486 { 487 // Strip the trailing period. 488 $namespace = substr($namespace, 0, strlen($namespace) - 1); 489 490 // Get a reference to the data block for this namespace. 491 $varref = $this->generate_block_data_ref($namespace, true); 492 // Prepend the necessary code to stick this in an echo line. 493 494 // Append the variable reference. 495 $varref .= '[\'' . $varname . '\']'; 496 497 $varref = '\' . ( ( isset(' . $varref . ') ) ? ' . $varref . ' : \'\' ) . \''; 498 499 return $varref; 500 501 } 502 503 504 /** 505 * Generates a reference to the array of data values for the given 506 * (possibly nested) block namespace. This is a string of the form: 507 * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] 508 * 509 * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. 510 * NOTE: does not expect a trailing "." on the blockname. 511 */ 512 function generate_block_data_ref($blockname, $include_last_iterator) 513 { 514 // Get an array of the blocks involved. 515 $blocks = explode(".", $blockname); 516 $blockcount = sizeof($blocks) - 1; 517 $varref = '$this->_tpldata'; 518 // Build up the string with everything but the last child. 519 for ($i = 0; $i < $blockcount; $i++) 520 { 521 $varref .= '[\'' . $blocks[$i] . '.\'][$_' . $blocks[$i] . '_i]'; 522 } 523 // Add the block reference for the last child. 524 $varref .= '[\'' . $blocks[$blockcount] . '.\']'; 525 // Add the iterator for the last child if requried. 526 if ($include_last_iterator) 527 { 528 $varref .= '[$_' . $blocks[$blockcount] . '_i]'; 529 } 530 531 return $varref; 532 } 533 534 } 535 536 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Jan 14 19:21:40 2013 | Cross-referenced by PHPXref 0.7.1 |