[ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 /** 3 * Horde Template system. Adapted from bTemplate by Brian Lozier 4 * <brian@massassi.net>. 5 * 6 * $Horde: framework/Template/Template.php,v 1.38.10.10 2006/06/13 03:54:03 chuck Exp $ 7 * 8 * Copyright 2002-2006 Chuck Hagenbuch <chuck@horde.org> 9 * 10 * See the enclosed file COPYING for license information (LGPL). If you 11 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 12 * 13 * @author Chuck Hagenbuch <chuck@horde.org> 14 * @since Horde 3.0 15 * @package Horde_Template 16 */ 17 class Horde_Template { 18 19 /** 20 * Option values. 21 * 22 * @var array 23 */ 24 var $_options = array(); 25 26 /** 27 * Directory that templates should be read from. 28 * 29 * @var string 30 */ 31 var $_basepath = ''; 32 33 /** 34 * Reset template variables after parsing? 35 * 36 * @var boolean 37 */ 38 var $_resetVars = true; 39 40 /** 41 * Tag (scalar) values. 42 * 43 * @var array 44 */ 45 var $_scalars = array(); 46 47 /** 48 * Loop tag values. 49 * 50 * @var array 51 */ 52 var $_arrays = array(); 53 54 /** 55 * Cloop tag values. 56 * 57 * @var array 58 */ 59 var $_carrays = array(); 60 61 /** 62 * If tag values. 63 * 64 * @var array 65 */ 66 var $_ifs = array(); 67 68 /** 69 * Name of cached template file. 70 * 71 * @var string 72 */ 73 var $_templateFile = null; 74 75 /** 76 * Cached source of template file. 77 * 78 * @var string 79 */ 80 var $_template = null; 81 82 /** 83 * Constructor. Can set the template base path and whether or not 84 * to drop template variables after a parsing a template. 85 * 86 * @param string $basepath The directory where templates are read from. 87 * @param boolean $resetVars Drop template vars after parsing a template? 88 */ 89 function Horde_Template($basepath = null, $resetVars = true) 90 { 91 if (!is_null($basepath)) { 92 $this->_basepath = $basepath; 93 } 94 $this->_resetVars = (bool)$resetVars; 95 } 96 97 /** 98 * Sets an option. 99 * 100 * @param string $option The option name. 101 * @param mixed $val The option's value. 102 */ 103 function setOption($option, $val) 104 { 105 $this->_options[$option] = $val; 106 } 107 108 /** 109 * Set the template contents to a string. 110 * 111 * @param string $template The template text. 112 */ 113 function setTemplate($template) 114 { 115 $this->_template = $template; 116 $this->_templateFile = 'string'; 117 } 118 119 /** 120 * Returns an option's value. 121 * 122 * @param string $option The option name. 123 * 124 * @return mixed The option's value. 125 */ 126 function getOption($option) 127 { 128 return isset($this->_options[$option]) ? $this->_options[$option] : null; 129 } 130 131 /** 132 * Sets a tag, loop, or if variable. 133 * 134 * @param string|array $tag Either the tag name or a hash with tag names 135 * as keys and tag values as values. 136 * @param mixed $var The value to replace the tag with. 137 * @param boolean $isIf Is this for an <if:> tag? (Default: no). 138 */ 139 function set($tag, $var, $isIf = false) 140 { 141 if (is_array($tag)) { 142 foreach ($tag as $tTag => $tVar) { 143 $this->set($tTag, $tVar, $isIf); 144 } 145 } elseif (is_array($var) || is_object($var)) { 146 $this->_arrays[$tag] = $var; 147 if ($isIf) { 148 // Just store the same variable that we stored in 149 // $this->_arrays - if we don't modify it, PHP's 150 // reference counting ensures we're not using any 151 // additional memory here. 152 $this->_ifs[$tag] = $var; 153 } 154 } else { 155 $this->_scalars[$tag] = $var; 156 if ($isIf) { 157 // Just store the same variable that we stored in 158 // $this->_scalars - if we don't modify it, PHP's 159 // reference counting ensures we're not using any 160 // additional memory here. 161 $this->_ifs[$tag] = $var; 162 } 163 } 164 } 165 166 /** 167 * Returns the value of a tag or loop. 168 * 169 * @param string $tag The tag name. 170 * 171 * @return mixed The tag value or null if the tag hasn't been set yet. 172 */ 173 function get($tag) 174 { 175 if (isset($this->_arrays[$tag])) { 176 return $this->_arrays[$tag]; 177 } 178 if (isset($this->_scalars[$tag])) { 179 return $this->_scalars[$tag]; 180 } 181 return null; 182 } 183 184 /** 185 * Sets values for a cloop. 186 * 187 * @param string $tag The name of the cloop. 188 * @param array $array The values for the cloop. 189 * @param array $cases The cases (test values) for the cloops. 190 */ 191 function setCloop($tag, $array, $cases) 192 { 193 $this->_carrays[$tag] = array( 194 'array' => $array, 195 'cases' => $cases 196 ); 197 } 198 199 /** 200 * Resets the template variables. 201 * 202 * @param boolean $scalars Reset scalar (basic tag) variables? 203 * @param boolean $arrays Reset loop variables? 204 * @param boolean $carrays Reset cloop variables? 205 * @param boolean $ifs Reset if variables? 206 */ 207 function resetVars($scalars, $arrays, $carrays, $ifs) 208 { 209 if ($scalars === true) { 210 $this->_scalars = array(); 211 } 212 if ($arrays === true) { 213 $this->_arrays = array(); 214 } 215 if ($carrays === true) { 216 $this->_carrays = array(); 217 } 218 if ($ifs === true) { 219 $this->_ifs = array(); 220 } 221 } 222 223 /** 224 * Retrieve list of blocks present in the template source. 225 * 226 * @param string $filename The file to fetch the template from. 227 * @return mixed An array of applications, or PEAR_Error on failure. 228 */ 229 function listBlocks() 230 { 231 $blocks = array(); 232 if (preg_match_all('<block:([a-zA-Z0-9_]+:[a-zA-Z0-9_]+)>', 233 $this->_template, $matches, PREG_SET_ORDER)) { 234 foreach ($matches as $match) { 235 $blocks[] = $match[1]; 236 } 237 } 238 239 return $blocks; 240 } 241 242 /** 243 * Fetches a template from the specified file and return the parsed 244 * contents. 245 * 246 * @param string $filename The file to fetch the template from. 247 * 248 * @return string The parsed template. 249 */ 250 function fetch($filename) 251 { 252 $contents = $this->_getTemplate($filename); 253 if (is_a($contents, 'PEAR_Error')) { 254 return $contents; 255 } 256 257 // Parse and return the contents. 258 return $this->parse($contents); 259 } 260 261 /** 262 * Parses all variables/tags in the template. 263 * 264 * @param string $contents The unparsed template. 265 * 266 * @return string The parsed template. 267 */ 268 function parse($contents = null) 269 { 270 if (!$contents) { 271 $contents = $this->_template; 272 } 273 274 // Process ifs. 275 if (!empty($this->_ifs)) { 276 foreach ($this->_ifs as $tag => $value) { 277 $contents = $this->_parseIf($tag, $contents); 278 } 279 } 280 281 // Process tags. 282 $search = array(); 283 $replace = array(); 284 foreach ($this->_scalars as $key => $value) { 285 $search[] = $this->_getTag($key); 286 $replace[] = $value; 287 } 288 if (count($search)) { 289 $contents = str_replace($search, $replace, $contents); 290 } 291 292 // Process loops and arrays. 293 foreach ($this->_arrays as $key => $array) { 294 $contents = $this->_parseLoop($key, $array, $contents); 295 } 296 297 // Process cloops. 298 foreach ($this->_carrays as $key => $array) { 299 $contents = $this->_parseCloop($key, $array, $contents); 300 } 301 302 // Parse gettext tags, if the option is enabled. 303 if ($this->getOption('gettext')) { 304 $contents = $this->_parseGettext($contents); 305 } 306 307 // Parse embedded blocks 308 if ($this->getOption('blocks')) { 309 $contents = $this->_parseBlocks($contents); 310 } 311 312 // Reset template data unless we're supposed to keep it 313 // around. 314 if ($this->_resetVars) { 315 $this->resetVars(false, true, true, false); 316 } 317 318 // Return parsed template. 319 return $contents; 320 } 321 322 /** 323 * Returns full start and end tags for a named tag. 324 * 325 * @access private 326 * 327 * @param string $tag 328 * @param string $directive The kind of tag - tag, if, loop, cloop. 329 * 330 * @return array 'b' => Start tag. 331 * 'e' => End tag. 332 */ 333 function _getTags($tag, $directive) 334 { 335 return array('b' => '<' . $directive . ':' . $tag . '>', 336 'e' => '</' . $directive . ':' . $tag . '>'); 337 } 338 339 /** 340 * Formats a scalar tag (default format is <tag:name>). 341 * 342 * @access private 343 * 344 * @param string $tag The name of the tag. 345 * 346 * @return string The full tag with the current start/end delimiters. 347 */ 348 function _getTag($tag) 349 { 350 return '<tag:' . $tag . ' />'; 351 } 352 353 /** 354 * Extracts a portion of a template. 355 * 356 * @access private 357 * 358 * @param array $t The tag to extract. Hash format is: 359 * $t['b'] - The start tag 360 * $t['e'] - The end tag 361 * @param string &$contents The template to extract from. 362 */ 363 function _getStatement($t, &$contents) 364 { 365 // Locate the statement. 366 $pos = strpos($contents, $t['b']); 367 if ($pos === false) { 368 return false; 369 } 370 371 $tag_length = strlen($t['b']); 372 $fpos = $pos + $tag_length; 373 $lpos = strpos($contents, $t['e']); 374 $length = $lpos - $fpos; 375 376 // Extract & return the statement. 377 return substr($contents, $fpos, $length); 378 } 379 380 /** 381 * Parses gettext tags. 382 * 383 * @param string $contents The unparsed content of the file. 384 * 385 * @return string The parsed contents of the gettext blocks. 386 */ 387 function _parseGettext($contents) 388 { 389 // Get the tags & loop. 390 $t = array('b' => '<gettext>', 391 'e' => '</gettext>'); 392 while ($text = $this->_getStatement($t, $contents)) { 393 $contents = str_replace($t['b'] . $text . $t['e'], _($text), $contents); 394 } 395 return $contents; 396 } 397 398 /** 399 * Parses block tags. 400 * 401 * Here we parse block tags in the form: 402 * 403 * <block:app:name> 404 * <param:foo>foo parameter's value</param:foo> 405 * <param:bar>bar parameter's value</param:bar> 406 * <serial:baz>s:21:"baz parameter's value";</serial:baz> 407 * </block:app:name> 408 * 409 * ... and replace them with rendered application blocks. 410 * 411 * Block parameters are provided with the nested "param:foo" and 412 * "serial:foo" tags. The serial-style tags take a serialized PHP 413 * value, so you can use it to provide array, boolean, and complex 414 * type parameters. The param-style tags provide string parameters. 415 * 416 * @access private 417 * 418 * @param string $contents The unparsed contents of the file. 419 * 420 * @return string The parsed contents of the file. 421 */ 422 function _parseBlocks($contents) 423 { 424 return preg_replace('!<block:([a-zA-Z_0-9]+):([a-zA-Z_0-9]+)>(.*?)' . 425 '</block:([a-zA-Z_0-9]+):([a-zA-Z_0-9]+)>!se', 426 "\$this->_parseBlock('\\1', '\\2', '\\3')", 427 $contents); 428 } 429 430 /** 431 * Handle the contents of one block tag. 432 * 433 * @access private 434 */ 435 function _parseBlock($app, $name, $contents) 436 { 437 require_once 'Horde/Block.php'; 438 require_once 'Horde/Block/Collection.php'; 439 440 // Undo preg_replace() encoding strangeness. 441 $contents = str_replace('\\"', '"', $contents); 442 443 // Find block's parameters. 444 $params = array(); 445 if (preg_match_all('!<param:([a-zA-Z_0-9]+)>(.*?)' . 446 '</param:([a-zA-Z_0-9]+)>!s', $contents, $matches, 447 PREG_SET_ORDER)) { 448 foreach ($matches as $match) { 449 $params[$match[1]] = html_entity_decode($match[2]); 450 } 451 } 452 if (preg_match_all('!<serial:([a-zA-Z_0-9]+)>(.*?)' . 453 '</serial:([a-zA-Z_0-9]+)>!s', $contents, $matches, 454 PREG_SET_ORDER)) { 455 foreach ($matches as $match) { 456 $params[$match[1]] = unserialize(html_entity_decode($match[2])); 457 } 458 } 459 460 // Construct the block. 461 $block = &Horde_Block_Collection::getBlock($app, $name, $params); 462 if (is_a($block, 'PEAR_Error')) { 463 $result = '<strong>' . sprintf(_("Error creating block: %s"), 464 $block->getMessage()) . '</strong>'; 465 } else { 466 $result = $block->getContent(); 467 if (is_a($result, 'PEAR_Error')) { 468 $result = '<strong>' . sprintf(_("Error getting content: %s"), 469 $result->getMessage()) . '</strong>'; 470 } 471 } 472 473 return $result; 474 } 475 476 /** 477 * Parses a given if statement. 478 * 479 * @access private 480 * 481 * @param string $tag The name of the if block to parse. 482 * @param string $contents The unparsed contents of the if block. 483 * 484 * @return string The parsed contents of the if block. 485 */ 486 function _parseIf($tag, $contents, $key = null) 487 { 488 // Get the tags & if statement. 489 $t = $this->_getTags($tag, 'if'); 490 $et = $this->_getTags($tag, 'else'); 491 492 // explode the tag, so we have the correct keys for the array 493 if (isset($key)) { 494 list($tg, $k) = explode('.', $tag); 495 } 496 while (($if = $this->_getStatement($t, $contents)) !== false) { 497 // Check for else statement. 498 if ($else = $this->_getStatement($et, $if)) { 499 // Process the if statement. 500 if ((isset($key) && $this->_ifs[$tg][$key][$k]) || 501 (isset($this->_ifs[$tag]) && $this->_ifs[$tag])) { 502 $replace = str_replace($et['b'] . $else . $et['e'], '', $if); 503 } else { 504 $replace = $else; 505 } 506 } else { 507 // Process the if statement. 508 if (isset($key)) { 509 $replace = $this->_ifs[$tg][$key][$k] ? $if : null; 510 } else { 511 $replace = $this->_ifs[$tag] ? $if : null; 512 } 513 } 514 515 // Parse the template. 516 $contents = str_replace($t['b'] . $if . $t['e'], $replace, 517 $contents); 518 } 519 520 // Return parsed template. 521 return $contents; 522 } 523 524 /** 525 * Parses the given array for any loops or other uses of the array. 526 * 527 * @access private 528 * 529 * @param string $tag The name of the loop to parse. 530 * @param array $array The values for the loop. 531 * @param string $contents The unparsed contents of the loop. 532 * 533 * @return string The parsed contents of the loop. 534 */ 535 function _parseLoop($tag, $array, $contents) 536 { 537 // Get the tags & loop. 538 $t = $this->_getTags($tag, 'loop'); 539 $loop = $this->_getStatement($t, $contents); 540 541 // See if we have a divider. 542 $l = $this->_getTags($tag, 'divider'); 543 $divider = $this->_getStatement($l, $loop); 544 $contents = str_replace($l['b'] . $divider . $l['e'], '', $contents); 545 546 // Process the array. 547 do { 548 $parsed = ''; 549 $first = true; 550 foreach ($array as $key => $value) { 551 if (is_array($value) || is_object($value)) { 552 $i = $loop; 553 if (is_numeric($key)) { 554 foreach ($value as $key2 => $value2) { 555 if (!is_array($value2) && !is_object($value2)) { 556 // Replace associative array tags. 557 $aa_tag = $tag . '.' . $key2; 558 $i = str_replace($this->_getTag($aa_tag), $value2, $i); 559 $pos = strpos($tag, '.'); 560 if (($pos !== false) && 561 !empty($this->_ifs[substr($tag, 0, $pos)])) { 562 $this->_ifs[$aa_tag] = $value2; 563 $i = $this->_parseIf($aa_tag, $i); 564 unset($this->_ifs[$aa_tag]); 565 } 566 } else { 567 // Check to see if it's a nested loop. 568 $i = $this->_parseLoop($tag . '.' . $key2, $value2, $i); 569 } 570 } 571 } 572 $i = str_replace($this->_getTag($tag), $key, $i); 573 } elseif (is_string($key) && !is_array($value) && !is_object($value)) { 574 $contents = str_replace($this->_getTag($tag . '.' . $key), $value, $contents); 575 } elseif (!is_array($value) && !is_object($value)) { 576 $i = str_replace($this->_getTag($tag . ''), $value, $loop); 577 } else { 578 $i = null; 579 } 580 581 // Parse conditions in the array. 582 if (!empty($this->_ifs[$tag][$key]) && is_array($this->_ifs[$tag][$key]) && $this->_ifs[$tag][$key]) { 583 foreach ($this->_ifs[$tag][$key] as $cTag => $cValue) { 584 $i = $this->_parseIf($tag . '.' . $cTag, $i, $key); 585 } 586 } 587 588 // Add the parsed iteration. 589 if (isset($i)) { 590 // If it's not the first time through, prefix the 591 // loop divider, if there is one. 592 if (!$first) { 593 $i = $divider . $i; 594 } 595 $parsed .= rtrim($i); 596 } 597 598 // No longer the first time through. 599 $first = false; 600 } 601 602 // Replace the parsed pieces of the template. 603 $contents = str_replace($t['b'] . $loop . $t['e'], $parsed, $contents); 604 } while ($loop = $this->_getStatement($t, $contents)); 605 606 return $contents; 607 } 608 609 /** 610 * Parses the given case loop (cloop). 611 * 612 * @access private 613 * 614 * @param string $tag The name of the cloop to parse. 615 * @param array $array The values for the cloop. 616 * @param string $contents The unparsed contents of the cloop. 617 * 618 * @return string The parsed contents of the cloop. 619 */ 620 function _parseCloop($tag, $array, $contents) 621 { 622 // Get the tags & cloop. 623 $t = $this->_getTags($tag, 'cloop'); 624 625 while ($loop = $this->_getStatement($t, $contents)) { 626 // Set up the cases. 627 $array['cases'][] = 'default'; 628 $case_content = array(); 629 630 // Get the case strings. 631 foreach ($array['cases'] as $case) { 632 $ctags[$case] = $this->_getTags($case, 'case'); 633 $case_content[$case] = $this->_getStatement($ctags[$case], $loop); 634 } 635 636 // Process the cloop. 637 $parsed = ''; 638 foreach ($array['array'] as $key => $value) { 639 if (is_numeric($key) && (is_array($value) || is_object($value))) { 640 // Set up the cases. 641 if (isset($value['case'])) { 642 $current_case = $value['case']; 643 } else { 644 $current_case = 'default'; 645 } 646 unset($value['case']); 647 $i = $case_content[$current_case]; 648 649 // Loop through each value. 650 foreach ($value as $key2 => $value2) { 651 if (is_array($value2) || is_object($value2)) { 652 $i = $this->_parseLoop($tag . '.' . $key2, $value2, $i); 653 } else { 654 $i = str_replace($this->_getTag($tag . '.' . $key2), $value2, $i); 655 } 656 } 657 } 658 659 // Add the parsed iteration. 660 $parsed .= rtrim($i); 661 } 662 663 // Parse the cloop. 664 $contents = str_replace($t['b'] . $loop . $t['e'], $parsed, $contents); 665 } 666 667 return $contents; 668 } 669 670 /** 671 * Fetch the contents of a template into $this->_template; cache 672 * the filename in $this->_templateFile. 673 * 674 * @access private 675 * 676 * @param string $filename Location of template file on disk. 677 * 678 * @return string The loaded template content. 679 */ 680 function _getTemplate($filename = null) 681 { 682 if (!is_null($filename) && ($filename != $this->_templateFile)) { 683 $this->_template = null; 684 } 685 686 if (!is_null($this->_template)) { 687 return $this->_template; 688 } 689 690 // Get the contents of the file. 691 $file = $this->_basepath . $filename; 692 $contents = @file_get_contents($file); 693 if ($contents === false) { 694 require_once 'PEAR.php'; 695 return PEAR::raiseError(sprintf(_("Template \"%s\" not found."), $file)); 696 } 697 698 $this->_template = $contents; 699 $this->_templateFile = $filename; 700 701 return $this->_template; 702 } 703 704 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |