[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /*************************************************************************** 3 * template.php 4 * ------------------- 5 * begin : Saturday, Feb 13, 2001 6 * copyright : (C) 2001 The phpBB Group 7 * email : support@phpbb.com 8 * 9 * $Id: template.php 5142 2005-05-06 20:50:13Z acydburn $ 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 $this->set_rootdir($root); 61 } 62 63 /** 64 * Destroys this template object. Should be called when you're done with it, in order 65 * to clear out the template data so you can load/parse a new template set. 66 */ 67 function destroy() 68 { 69 $this->_tpldata = array(); 70 } 71 72 /** 73 * Sets the template root directory for this Template object. 74 */ 75 function set_rootdir($dir) 76 { 77 if (!is_dir($dir)) 78 { 79 return false; 80 } 81 82 $this->root = $dir; 83 return true; 84 } 85 86 /** 87 * Sets the template filenames for handles. $filename_array 88 * should be a hash of handle => filename pairs. 89 */ 90 function set_filenames($filename_array) 91 { 92 if (!is_array($filename_array)) 93 { 94 return false; 95 } 96 97 reset($filename_array); 98 while(list($handle, $filename) = each($filename_array)) 99 { 100 $this->files[$handle] = $this->make_filename($filename); 101 } 102 103 return true; 104 } 105 106 107 /** 108 * Load the file for the handle, compile the file, 109 * and run the compiled code. This will print out 110 * the results of executing the template. 111 */ 112 function pparse($handle) 113 { 114 if (!$this->loadfile($handle)) 115 { 116 die("Template->pparse(): Couldn't load template file for handle $handle"); 117 } 118 119 // actually compile the template now. 120 if (!isset($this->compiled_code[$handle]) || empty($this->compiled_code[$handle])) 121 { 122 // Actually compile the code now. 123 $this->compiled_code[$handle] = $this->compile($this->uncompiled_code[$handle]); 124 } 125 126 // Run the compiled code. 127 eval($this->compiled_code[$handle]); 128 return true; 129 } 130 131 /** 132 * Inserts the uncompiled code for $handle as the 133 * value of $varname in the root-level. This can be used 134 * to effectively include a template in the middle of another 135 * template. 136 * Note that all desired assignments to the variables in $handle should be done 137 * BEFORE calling this function. 138 */ 139 function assign_var_from_handle($varname, $handle) 140 { 141 if (!$this->loadfile($handle)) 142 { 143 die("Template->assign_var_from_handle(): Couldn't load template file for handle $handle"); 144 } 145 146 // Compile it, with the "no echo statements" option on. 147 $_str = ""; 148 $code = $this->compile($this->uncompiled_code[$handle], true, '_str'); 149 150 // evaluate the variable assignment. 151 eval($code); 152 // assign the value of the generated variable to the given varname. 153 $this->assign_var($varname, $_str); 154 155 return true; 156 } 157 158 /** 159 * Block-level variable assignment. Adds a new block iteration with the given 160 * variable assignments. Note that this should only be called once per block 161 * iteration. 162 */ 163 function assign_block_vars($blockname, $vararray) 164 { 165 if (strstr($blockname, '.')) 166 { 167 // Nested block. 168 $blocks = explode('.', $blockname); 169 $blockcount = sizeof($blocks) - 1; 170 $str = '$this->_tpldata'; 171 for ($i = 0; $i < $blockcount; $i++) 172 { 173 $str .= '[\'' . $blocks[$i] . '.\']'; 174 eval('$lastiteration = sizeof(' . $str . ') - 1;'); 175 $str .= '[' . $lastiteration . ']'; 176 } 177 // Now we add the block that we're actually assigning to. 178 // We're adding a new iteration to this block with the given 179 // variable assignments. 180 $str .= '[\'' . $blocks[$blockcount] . '.\'][] = $vararray;'; 181 182 // Now we evaluate this assignment we've built up. 183 eval($str); 184 } 185 else 186 { 187 // Top-level block. 188 // Add a new iteration to this block with the variable assignments 189 // we were given. 190 $this->_tpldata[$blockname . '.'][] = $vararray; 191 } 192 193 return true; 194 } 195 196 /** 197 * Root-level variable assignment. Adds to current assignments, overriding 198 * any existing variable assignment with the same name. 199 */ 200 function assign_vars($vararray) 201 { 202 reset ($vararray); 203 while (list($key, $val) = each($vararray)) 204 { 205 $this->_tpldata['.'][0][$key] = $val; 206 } 207 208 return true; 209 } 210 211 /** 212 * Root-level variable assignment. Adds to current assignments, overriding 213 * any existing variable assignment with the same name. 214 */ 215 function assign_var($varname, $varval) 216 { 217 $this->_tpldata['.'][0][$varname] = $varval; 218 219 return true; 220 } 221 222 223 /** 224 * Generates a full path+filename for the given filename, which can either 225 * be an absolute name, or a name relative to the rootdir for this Template 226 * object. 227 */ 228 function make_filename($filename) 229 { 230 // Check if it's an absolute or relative path. 231 if (substr($filename, 0, 1) != '/') 232 { 233 $filename = ($rp_filename = phpbb_realpath($this->root . '/' . $filename)) ? $rp_filename : $filename; 234 } 235 236 if (!file_exists($filename)) 237 { 238 die("Template->make_filename(): Error - file $filename does not exist"); 239 } 240 241 return $filename; 242 } 243 244 245 /** 246 * If not already done, load the file for the given handle and populate 247 * the uncompiled_code[] hash with its code. Do not compile. 248 */ 249 function loadfile($handle) 250 { 251 // If the file for this handle is already loaded and compiled, do nothing. 252 if (isset($this->uncompiled_code[$handle]) && !empty($this->uncompiled_code[$handle])) 253 { 254 return true; 255 } 256 257 // If we don't have a file assigned to this handle, die. 258 if (!isset($this->files[$handle])) 259 { 260 die("Template->loadfile(): No file specified for handle $handle"); 261 } 262 263 $filename = $this->files[$handle]; 264 265 $str = implode("", @file($filename)); 266 if (empty($str)) 267 { 268 die("Template->loadfile(): File $filename for handle $handle is empty"); 269 } 270 271 $this->uncompiled_code[$handle] = $str; 272 273 return true; 274 } 275 276 277 278 /** 279 * Compiles the given string of code, and returns 280 * the result in a string. 281 * If "do_not_echo" is true, the returned code will not be directly 282 * executable, but can be used as part of a variable assignment 283 * for use in assign_code_from_handle(). 284 */ 285 function compile($code, $do_not_echo = false, $retvar = '') 286 { 287 // replace \ with \\ and then ' with \'. 288 $code = str_replace('\\', '\\\\', $code); 289 $code = str_replace('\'', '\\\'', $code); 290 291 // change template varrefs into PHP varrefs 292 293 // This one will handle varrefs WITH namespaces 294 $varrefs = array(); 295 preg_match_all('#\{(([a-z0-9\-_]+?\.)+?)([a-z0-9\-_]+?)\}#is', $code, $varrefs); 296 $varcount = sizeof($varrefs[1]); 297 for ($i = 0; $i < $varcount; $i++) 298 { 299 $namespace = $varrefs[1][$i]; 300 $varname = $varrefs[3][$i]; 301 $new = $this->generate_block_varref($namespace, $varname); 302 303 $code = str_replace($varrefs[0][$i], $new, $code); 304 } 305 306 // This will handle the remaining root-level varrefs 307 $code = preg_replace('#\{([a-z0-9\-_]*?)\}#is', '\' . ( ( isset($this->_tpldata[\'.\'][0][\'\1\']) ) ? $this->_tpldata[\'.\'][0][\'\1\'] : \'\' ) . \'', $code); 308 309 // Break it up into lines. 310 $code_lines = explode("\n", $code); 311 312 $block_nesting_level = 0; 313 $block_names = array(); 314 $block_names[0] = "."; 315 316 // Second: prepend echo ', append ' . "\n"; to each line. 317 $line_count = sizeof($code_lines); 318 for ($i = 0; $i < $line_count; $i++) 319 { 320 $code_lines[$i] = chop($code_lines[$i]); 321 if (preg_match('#<!-- BEGIN (.*?) -->#', $code_lines[$i], $m)) 322 { 323 $n[0] = $m[0]; 324 $n[1] = $m[1]; 325 326 // Added: dougk_ff7-Keeps templates from bombing if begin is on the same line as end.. I think. :) 327 if ( preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $n) ) 328 { 329 $block_nesting_level++; 330 $block_names[$block_nesting_level] = $m[1]; 331 if ($block_nesting_level < 2) 332 { 333 // Block is not nested. 334 $code_lines[$i] = '$_' . $n[1] . '_count = ( isset($this->_tpldata[\'' . $n[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $n[1] . '.\']) : 0;'; 335 $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)'; 336 $code_lines[$i] .= "\n" . '{'; 337 } 338 else 339 { 340 // This block is nested. 341 342 // Generate a namespace string for this block. 343 $namespace = implode('.', $block_names); 344 // strip leading period from root level.. 345 $namespace = substr($namespace, 2); 346 // Get a reference to the data array for this block that depends on the 347 // current indices of all parent blocks. 348 $varref = $this->generate_block_data_ref($namespace, false); 349 // Create the for loop code to iterate over this block. 350 $code_lines[$i] = '$_' . $n[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;'; 351 $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)'; 352 $code_lines[$i] .= "\n" . '{'; 353 } 354 355 // We have the end of a block. 356 unset($block_names[$block_nesting_level]); 357 $block_nesting_level--; 358 $code_lines[$i] .= '} // END ' . $n[1]; 359 $m[0] = $n[0]; 360 $m[1] = $n[1]; 361 } 362 else 363 { 364 // We have the start of a block. 365 $block_nesting_level++; 366 $block_names[$block_nesting_level] = $m[1]; 367 if ($block_nesting_level < 2) 368 { 369 // Block is not nested. 370 $code_lines[$i] = '$_' . $m[1] . '_count = ( isset($this->_tpldata[\'' . $m[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $m[1] . '.\']) : 0;'; 371 $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)'; 372 $code_lines[$i] .= "\n" . '{'; 373 } 374 else 375 { 376 // This block is nested. 377 378 // Generate a namespace string for this block. 379 $namespace = implode('.', $block_names); 380 // strip leading period from root level.. 381 $namespace = substr($namespace, 2); 382 // Get a reference to the data array for this block that depends on the 383 // current indices of all parent blocks. 384 $varref = $this->generate_block_data_ref($namespace, false); 385 // Create the for loop code to iterate over this block. 386 $code_lines[$i] = '$_' . $m[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;'; 387 $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)'; 388 $code_lines[$i] .= "\n" . '{'; 389 } 390 } 391 } 392 else if (preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $m)) 393 { 394 // We have the end of a block. 395 unset($block_names[$block_nesting_level]); 396 $block_nesting_level--; 397 $code_lines[$i] = '} // END ' . $m[1]; 398 } 399 else 400 { 401 // We have an ordinary line of code. 402 if (!$do_not_echo) 403 { 404 $code_lines[$i] = 'echo \'' . $code_lines[$i] . '\' . "\\n";'; 405 } 406 else 407 { 408 $code_lines[$i] = '$' . $retvar . '.= \'' . $code_lines[$i] . '\' . "\\n";'; 409 } 410 } 411 } 412 413 // Bring it back into a single string of lines of code. 414 $code = implode("\n", $code_lines); 415 return $code ; 416 417 } 418 419 420 /** 421 * Generates a reference to the given variable inside the given (possibly nested) 422 * block namespace. This is a string of the form: 423 * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . ' 424 * It's ready to be inserted into an "echo" line in one of the templates. 425 * NOTE: expects a trailing "." on the namespace. 426 */ 427 function generate_block_varref($namespace, $varname) 428 { 429 // Strip the trailing period. 430 $namespace = substr($namespace, 0, strlen($namespace) - 1); 431 432 // Get a reference to the data block for this namespace. 433 $varref = $this->generate_block_data_ref($namespace, true); 434 // Prepend the necessary code to stick this in an echo line. 435 436 // Append the variable reference. 437 $varref .= '[\'' . $varname . '\']'; 438 439 $varref = '\' . ( ( isset(' . $varref . ') ) ? ' . $varref . ' : \'\' ) . \''; 440 441 return $varref; 442 443 } 444 445 446 /** 447 * Generates a reference to the array of data values for the given 448 * (possibly nested) block namespace. This is a string of the form: 449 * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] 450 * 451 * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. 452 * NOTE: does not expect a trailing "." on the blockname. 453 */ 454 function generate_block_data_ref($blockname, $include_last_iterator) 455 { 456 // Get an array of the blocks involved. 457 $blocks = explode(".", $blockname); 458 $blockcount = sizeof($blocks) - 1; 459 $varref = '$this->_tpldata'; 460 // Build up the string with everything but the last child. 461 for ($i = 0; $i < $blockcount; $i++) 462 { 463 $varref .= '[\'' . $blocks[$i] . '.\'][$_' . $blocks[$i] . '_i]'; 464 } 465 // Add the block reference for the last child. 466 $varref .= '[\'' . $blocks[$blockcount] . '.\']'; 467 // Add the iterator for the last child if requried. 468 if ($include_last_iterator) 469 { 470 $varref .= '[$_' . $blocks[$blockcount] . '_i]'; 471 } 472 473 return $varref; 474 } 475 476 } 477 478 ?>
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 |