[ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 /** 3 * Spyc -- A Simple PHP YAML Class 4 * @version 0.2.3 -- 2006-02-04 5 * @author Chris Wanstrath <chris@ozmm.org> 6 * @link http://spyc.sourceforge.net/ 7 * @copyright Copyright 2005-2006 Chris Wanstrath 8 * @license http://www.opensource.org/licenses/mit-license.php MIT License 9 * @package Spyc 10 */ 11 12 /** 13 * A node, used by Spyc for parsing YAML. 14 * @package Spyc 15 */ 16 class YAMLNode 17 { 18 public $parent; 19 public $id; 20 public $data; 21 public $indent; 22 public $children = false; 23 24 static protected $lastNodeId = 0; 25 26 /** 27 * The constructor assigns the node a unique ID. 28 * 29 * @return void 30 */ 31 public function __construct() 32 { 33 $this->id = ++self::$lastNodeId; 34 } 35 } 36 37 /** 38 * The Simple PHP YAML Class. 39 * 40 * This class can be used to read a YAML file and convert its contents 41 * into a PHP array. It currently supports a very limited subsection of 42 * the YAML spec. 43 * 44 * Usage: 45 * <code> 46 * $parser = new Spyc; 47 * $array = $parser->load($file); 48 * </code> 49 * @package Spyc 50 */ 51 class Spyc 52 { 53 /** 54 * Load YAML into a PHP array statically 55 * 56 * The load method, when supplied with a YAML stream (string or file), 57 * will do its best to convert YAML in a file into a PHP array. Pretty 58 * simple. 59 * Usage: 60 * <code> 61 * $array = Spyc::YAMLLoad('lucky.yml'); 62 * print_r($array); 63 * </code> 64 * 65 * @return array 66 * @param string $input Path of YAML file or string containing YAML 67 */ 68 public static function YAMLLoad($input) 69 { 70 $spyc = new Spyc(); 71 72 return $spyc->load($input); 73 } 74 75 /** 76 * Dump YAML from PHP array statically 77 * 78 * The dump method, when supplied with an array, will do its best 79 * to convert the array into friendly YAML. Pretty simple. Feel free to 80 * save the returned string as nothing.yml and pass it around. 81 * 82 * Oh, and you can decide how big the indent is and what the wordwrap 83 * for folding is. Pretty cool -- just pass in 'false' for either if 84 * you want to use the default. 85 * 86 * Indent's default is 2 spaces, wordwrap's default is 40 characters. And 87 * you can turn off wordwrap by passing in 0. 88 * 89 * @return string 90 * @param array $array PHP array 91 * @param int $indent Pass in false to use the default, which is 2 92 * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) 93 */ 94 public static function YAMLDump($array, $indent = false, $wordwrap = false) 95 { 96 $spyc = new Spyc(); 97 98 return $spyc->dump($array, $indent, $wordwrap); 99 } 100 101 /** 102 * Load YAML into a PHP array from an instantiated object 103 * 104 * The load method, when supplied with a YAML stream (string or file path), 105 * will do its best to convert the YAML into a PHP array. Pretty simple. 106 * Usage: 107 * <code> 108 * $parser = new Spyc; 109 * $array = $parser->load('lucky.yml'); 110 * print_r($array); 111 * </code> 112 * 113 * @return array 114 * @param string $input Path of YAML file or string containing YAML 115 */ 116 public function load($input) 117 { 118 // See what type of input we're talking about 119 // If it's not a file, assume it's a string 120 if (!empty($input) && (strpos($input, "\n") === false) && file_exists($input)) 121 { 122 $file = $input; 123 $yaml = file($input); 124 } 125 else 126 { 127 $file = null; 128 $yaml = explode("\n", $input); 129 } 130 131 // Initiate some objects and values 132 $base = new YAMLNode(); 133 $base->indent = 0; 134 $this->_lastIndent = 0; 135 $this->_lastNode = $base->id; 136 $this->_inBlock = false; 137 $this->_isInline = false; 138 139 foreach ($yaml as $linenum => $line) 140 { 141 $ifchk = trim($line); 142 143 // If the line starts with a tab (instead of a space), throw a fit. 144 if (preg_match('/^(\t)+(\w+)/', $line)) 145 { 146 $error = sprintf('ERROR: %sLine %d in your input YAML begins with a tab. YAML only recognizes spaces. Please reformat.', ($file ? "File $file " : ''), $linenum + 1); 147 148 throw new Exception($error); 149 } 150 151 if ($this->_inBlock === false && empty($ifchk)) 152 { 153 continue; 154 } 155 else if ($this->_inBlock == true && empty($ifchk)) 156 { 157 $last =& $this->_allNodes[$this->_lastNode]; 158 $last->data[key($last->data)] .= "\n"; 159 } 160 else if ($ifchk{0} != '#' && substr($ifchk, 0, 3) != '---') 161 { 162 // Create a new node and get its indent 163 $node = new YAMLNode(); 164 $node->indent = $this->_getIndent($line); 165 166 // Check where the node lies in the hierarchy 167 if ($this->_lastIndent == $node->indent) 168 { 169 // If we're in a block, add the text to the parent's data 170 if ($this->_inBlock === true) 171 { 172 $parent =& $this->_allNodes[$this->_lastNode]; 173 $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; 174 } 175 else 176 { 177 // The current node's parent is the same as the previous node's 178 if (isset($this->_allNodes[$this->_lastNode])) 179 { 180 $node->parent = $this->_allNodes[$this->_lastNode]->parent; 181 } 182 } 183 } 184 else if ($this->_lastIndent < $node->indent) 185 { 186 if ($this->_inBlock === true) 187 { 188 $parent =& $this->_allNodes[$this->_lastNode]; 189 $parent->data[key($parent->data)] .= substr($line, $this->_lastIndent).$this->_blockEnd; 190 } 191 else if ($this->_inBlock === false) 192 { 193 // The current node's parent is the previous node 194 $node->parent = $this->_lastNode; 195 196 // If the value of the last node's data was > or | we need to 197 // start blocking i.e. taking in all lines as a text value until 198 // we drop our indent. 199 $parent =& $this->_allNodes[$node->parent]; 200 $this->_allNodes[$node->parent]->children = true; 201 if (is_array($parent->data)) 202 { 203 $chk = $parent->data[key($parent->data)]; 204 if ($chk === '>') 205 { 206 $this->_inBlock = true; 207 $this->_blockEnd = ' '; 208 $parent->data[key($parent->data)] = str_replace('>', '', $parent->data[key($parent->data)]); 209 $parent->data[key($parent->data)] .= trim($line).' '; 210 $this->_allNodes[$node->parent]->children = false; 211 $this->_lastIndent = $node->indent; 212 } 213 else if ($chk === '|') 214 { 215 $this->_inBlock = true; 216 $this->_blockEnd = "\n"; 217 $parent->data[key($parent->data)] = str_replace('|', '', $parent->data[key($parent->data)]); 218 $parent->data[key($parent->data)] .= trim($line)."\n"; 219 $this->_allNodes[$node->parent]->children = false; 220 $this->_lastIndent = $node->indent; 221 } 222 } 223 } 224 } 225 else if ($this->_lastIndent > $node->indent) 226 { 227 // Any block we had going is dead now 228 if ($this->_inBlock === true) 229 { 230 $this->_inBlock = false; 231 if ($this->_blockEnd = "\n") 232 { 233 $last =& $this->_allNodes[$this->_lastNode]; 234 $last->data[key($last->data)] = trim($last->data[key($last->data)]); 235 } 236 } 237 238 // We don't know the parent of the node so we have to find it 239 // foreach ($this->_allNodes as $n) { 240 foreach ($this->_indentSort[$node->indent] as $n) 241 { 242 if ($n->indent == $node->indent) 243 { 244 $node->parent = $n->parent; 245 } 246 } 247 } 248 249 if ($this->_inBlock === false) 250 { 251 // Set these properties with information from our current node 252 $this->_lastIndent = $node->indent; 253 // Set the last node 254 $this->_lastNode = $node->id; 255 // Parse the YAML line and return its data 256 $node->data = $this->_parseLine($line); 257 // Add the node to the master list 258 $this->_allNodes[$node->id] = $node; 259 // Add a reference to the node in an indent array 260 $this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id]; 261 // Add a reference to the node in a References array if this node 262 // has a YAML reference in it. 263 if ( 264 ((is_array($node->data)) && 265 isset($node->data[key($node->data)]) && 266 (!is_array($node->data[key($node->data)]))) 267 && 268 ((preg_match('/^&([^ ]+)/', $node->data[key($node->data)])) 269 || 270 (preg_match('/^\*([^ ]+)/', $node->data[key($node->data)]))) 271 ) 272 { 273 $this->_haveRefs[] =& $this->_allNodes[$node->id]; 274 } 275 else if ( 276 ((is_array($node->data)) && 277 isset($node->data[key($node->data)]) && 278 (is_array($node->data[key($node->data)]))) 279 ) 280 { 281 // Incomplete reference making code. Ugly, needs cleaned up. 282 foreach ($node->data[key($node->data)] as $d) 283 { 284 if (!is_array($d) && ((preg_match('/^&([^ ]+)/', $d)) || (preg_match('/^\*([^ ]+)/', $d)))) 285 { 286 $this->_haveRefs[] =& $this->_allNodes[$node->id]; 287 } 288 } 289 } 290 } 291 } 292 } 293 unset($node); 294 295 // Here we travel through node-space and pick out references (& and *) 296 $this->_linkReferences(); 297 298 // Build the PHP array out of node-space 299 $trunk = $this->_buildArray(); 300 301 return $trunk; 302 } 303 304 /** 305 * Dump PHP array to YAML 306 * 307 * The dump method, when supplied with an array, will do its best 308 * to convert the array into friendly YAML. Pretty simple. Feel free to 309 * save the returned string as tasteful.yml and pass it around. 310 * 311 * Oh, and you can decide how big the indent is and what the wordwrap 312 * for folding is. Pretty cool -- just pass in 'false' for either if 313 * you want to use the default. 314 * 315 * Indent's default is 2 spaces, wordwrap's default is 40 characters. And 316 * you can turn off wordwrap by passing in 0. 317 * 318 * @return string 319 * @param array $array PHP array 320 * @param int $indent Pass in false to use the default, which is 2 321 * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) 322 */ 323 public function dump($array, $indent = false, $wordwrap = false) 324 { 325 // Dumps to some very clean YAML. We'll have to add some more features 326 // and options soon. And better support for folding. 327 328 // New features and options. 329 if ($indent === false or !is_numeric($indent)) 330 { 331 $this->_dumpIndent = 2; 332 } 333 else 334 { 335 $this->_dumpIndent = $indent; 336 } 337 338 if ($wordwrap === false or !is_numeric($wordwrap)) 339 { 340 $this->_dumpWordWrap = 40; 341 } 342 else 343 { 344 $this->_dumpWordWrap = $wordwrap; 345 } 346 347 // New YAML document 348 $string = "---\n"; 349 350 // Start at the base of the array and move through it. 351 foreach ($array as $key => $value) 352 { 353 $string .= $this->_yamlize($key, $value, 0); 354 } 355 356 return $string; 357 } 358 359 protected $_haveRefs; 360 protected $_allNodes; 361 protected $_lastIndent; 362 protected $_lastNode; 363 protected $_inBlock; 364 protected $_isInline; 365 protected $_dumpIndent; 366 protected $_dumpWordWrap; 367 368 /** 369 * Attempts to convert a key / value array item to YAML 370 * 371 * @return string 372 * @param $key The name of the key 373 * @param $value The value of the item 374 * @param $indent The indent of the current node 375 */ 376 protected function _yamlize($key, $value, $indent) 377 { 378 if (is_array($value)) 379 { 380 // It has children. What to do? 381 // Make it the right kind of item 382 $string = $this->_dumpNode($key, null, $indent); 383 // Add the indent 384 $indent += $this->_dumpIndent; 385 // Yamlize the array 386 $string .= $this->_yamlizeArray($value, $indent); 387 } 388 else if (!is_array($value)) 389 { 390 // It doesn't have children. Yip. 391 $string = $this->_dumpNode($key, $value, $indent); 392 } 393 394 return $string; 395 } 396 397 /** 398 * Attempts to convert an array to YAML 399 * 400 * @return string 401 * @param $array The array you want to convert 402 * @param $indent The indent of the current level 403 */ 404 protected function _yamlizeArray($array, $indent) 405 { 406 if (is_array($array)) 407 { 408 $string = ''; 409 foreach ($array as $key => $value) 410 { 411 $string .= $this->_yamlize($key, $value, $indent); 412 } 413 414 return $string; 415 } 416 else 417 { 418 return false; 419 } 420 } 421 422 /** 423 * Returns YAML from a key and a value 424 * 425 * @return string 426 * @param $key The name of the key 427 * @param $value The value of the item 428 * @param $indent The indent of the current node 429 */ 430 protected function _dumpNode($key, $value, $indent) 431 { 432 if (is_object($value)) 433 { 434 if (method_exists($value, '__toString')) 435 { 436 $value = (string) $value; 437 } 438 else 439 { 440 $ref = new ReflectionObject($value); 441 $value = $ref->getName(); 442 } 443 } 444 445 // do some folding here, for blocks 446 if (strpos($value,"\n")) 447 { 448 $value = $this->_doLiteralBlock($value, $indent); 449 } 450 else 451 { 452 $value = $this->_doFolding($value, $indent); 453 } 454 455 $spaces = str_repeat(' ', $indent); 456 457 if (is_int($key)) 458 { 459 // It's a sequence 460 $string = $spaces.'- '.$value."\n"; 461 } 462 else 463 { 464 // It's mapped 465 $string = $spaces.$key.': '.$value."\n"; 466 } 467 468 return $string; 469 } 470 471 /** 472 * Creates a literal block for dumping 473 * 474 * @return string 475 * @param $value 476 * @param $indent int The value of the indent 477 */ 478 protected function _doLiteralBlock($value, $indent) 479 { 480 $exploded = explode("\n", $value); 481 $newValue = '|'; 482 $indent += $this->_dumpIndent; 483 $spaces = str_repeat(' ', $indent); 484 foreach ($exploded as $line) 485 { 486 $newValue .= "\n".$spaces.trim($line); 487 } 488 return $newValue; 489 } 490 491 /** 492 * Folds a string of text, if necessary 493 * 494 * @return string 495 * @param $value The string you wish to fold 496 */ 497 protected function _doFolding($value, $indent) 498 { 499 // Don't do anything if wordwrap is set to 0 500 if ($this->_dumpWordWrap === 0) 501 { 502 return $value; 503 } 504 505 if (strlen($value) > $this->_dumpWordWrap) 506 { 507 $indent += $this->_dumpIndent; 508 $indent = str_repeat(' ', $indent); 509 $wrapped = wordwrap($value, $this->_dumpWordWrap, "\n$indent"); 510 $value = ">\n".$indent.$wrapped; 511 } 512 513 return $value; 514 } 515 516 /* Methods used in loading */ 517 518 /** 519 * Finds and returns the indentation of a YAML line 520 * 521 * @return int 522 * @param string $line A line from the YAML file 523 */ 524 protected function _getIndent($line) 525 { 526 preg_match('/^\s{1,}/', $line, $match); 527 if (!empty($match[0])) 528 { 529 $indent = substr_count($match[0], ' '); 530 } 531 else 532 { 533 $indent = 0; 534 } 535 536 return $indent; 537 } 538 539 /** 540 * Parses YAML code and returns an array for a node 541 * 542 * @return array 543 * @param string $line A line from the YAML file 544 */ 545 protected function _parseLine($line) 546 { 547 $line = trim($line); 548 549 $array = array(); 550 551 if (preg_match('/^-(.*):$/', $line)) 552 { 553 // It's a mapped sequence 554 $key = trim(substr(substr($line,1), 0, -1)); 555 $array[$key] = ''; 556 } 557 else if ($line[0] == '-' && substr($line, 0, 3) != '---') 558 { 559 // It's a list item but not a new stream 560 if (strlen($line) > 1) 561 { 562 $value = trim(substr($line, 1)); 563 // Set the type of the value. Int, string, etc 564 $value = $this->_toType($value); 565 $array[] = $value; 566 } 567 else 568 { 569 $array[] = array(); 570 } 571 } 572 else if (preg_match('/^(.+):/', $line, $key)) 573 { 574 // It's a key/value pair most likely 575 // If the key is in double quotes pull it out 576 if (preg_match('/^(["\'](.*)["\'](\s)*:)/', $line, $matches)) 577 { 578 $value = trim(str_replace($matches[1], '', $line)); 579 $key = $matches[2]; 580 } 581 else 582 { 583 // Do some guesswork as to the key and the value 584 $explode = explode(':', $line); 585 $key = trim($explode[0]); 586 array_shift($explode); 587 $value = trim(implode(':', $explode)); 588 } 589 590 // Set the type of the value. Int, string, etc 591 $value = $this->_toType($value); 592 if (empty($key)) 593 { 594 $array[] = $value; 595 } 596 else 597 { 598 $array[$key] = $value; 599 } 600 } 601 602 return $array; 603 } 604 605 /** 606 * Finds the type of the passed value, returns the value as the new type. 607 * 608 * @param string $value 609 * @return mixed 610 */ 611 protected function _toType($value) 612 { 613 $value = trim($value); 614 if ($value && !('"' == $value[0] || "'" == $value[0])) 615 { 616 $value = preg_replace('/\s*#(.+)$/', '', $value); 617 } 618 619 if (preg_match('/^("(.*)"|\'(.*)\')/', $value, $matches)) 620 { 621 $value = (string) preg_replace('/(\'\'|\\\\\')/', "'", end($matches)); 622 $value = preg_replace('/\\\\"/', '"', $value); 623 } 624 else if (preg_match('/^\\[\s*\\]$/', $value, $matches)) 625 { 626 $value = array(); 627 } 628 else if (preg_match('/^{}$/', $value, $matches)) 629 { 630 $value = array(); 631 } 632 else if (preg_match('/^\\[(.+)\\]$/', $value, $matches)) 633 { 634 // Inline Sequence 635 636 // Take out strings sequences and mappings 637 $explode = $this->_inlineEscape($matches[1]); 638 639 // Propogate value array 640 $value = array(); 641 foreach ($explode as $v) 642 { 643 $value[] = $this->_toType($v); 644 } 645 } 646 else if (strpos($value,': ') !== false && !preg_match('/^{(.+)/', $value)) 647 { 648 // It's a map 649 $array = explode(': ', $value); 650 $key = trim($array[0]); 651 array_shift($array); 652 $value = trim(implode(': ', $array)); 653 $value = $this->_toType($value); 654 $value = array($key => $value); 655 } 656 else if (preg_match("/{(.+)}$/", $value, $matches)) 657 { 658 // Inline Mapping 659 660 // Take out strings sequences and mappings 661 $explode = $this->_inlineEscape($matches[1]); 662 663 // Propogate value array 664 $array = array(); 665 foreach ($explode as $v) 666 { 667 $array = $array + $this->_toType($v); 668 } 669 $value = $array; 670 } 671 else if (strtolower($value) == 'null' or $value == '' or $value == '~') 672 { 673 $value = null; 674 } 675 else if (ctype_digit($value)) 676 { 677 $value = (int) $value; 678 } 679 else if (in_array(strtolower($value), array('true', 'on', '+', 'yes', 'y'))) 680 { 681 $value = true; 682 } 683 else if (in_array(strtolower($value), array('false', 'off', '-', 'no', 'n'))) 684 { 685 $value = false; 686 } 687 else if (is_numeric($value)) 688 { 689 $value = (float) $value; 690 } 691 692 return $value; 693 } 694 695 /** 696 * Used in inlines to check for more inlines or quoted strings 697 * 698 * @return array 699 */ 700 protected function _inlineEscape($inline) 701 { 702 // There's gotta be a cleaner way to do this... 703 // While pure sequences seem to be nesting just fine, 704 // pure mappings and mappings with sequences inside can't go very 705 // deep. This needs to be fixed. 706 707 // Check for strings 708 $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; 709 if (preg_match_all($regex, $inline, $strings)) 710 { 711 foreach ($strings[0] as $string) 712 { 713 $saved_strings[] = $string; 714 } 715 $inline = preg_replace($regex, 'YAMLString', $inline); 716 } 717 unset($regex); 718 719 // Check for sequences 720 if (preg_match_all('/\[(.+)\]/U', $inline, $seqs)) 721 { 722 $inline = preg_replace('/\[(.+)\]/U', 'YAMLSeq', $inline); 723 $seqs = $seqs[0]; 724 } 725 726 // Check for mappings 727 if (preg_match_all('/{(.+)}/U', $inline, $maps)) 728 { 729 $inline = preg_replace('/{(.+)}/U', 'YAMLMap', $inline); 730 $maps = $maps[0]; 731 } 732 733 $explode = explode(', ', $inline); 734 735 // Re-add the strings 736 if (!empty($saved_strings)) 737 { 738 $i = 0; 739 foreach ($explode as $key => $value) 740 { 741 if (strpos($value,'YAMLString')) 742 { 743 $explode[$key] = str_replace('YAMLString', $saved_strings[$i], $value); 744 ++$i; 745 } 746 } 747 } 748 749 // Re-add the sequences 750 if (!empty($seqs)) 751 { 752 $i = 0; 753 foreach ($explode as $key => $value) 754 { 755 if (strpos($value,'YAMLSeq') !== false) 756 { 757 $explode[$key] = str_replace('YAMLSeq', $seqs[$i], $value); 758 ++$i; 759 } 760 } 761 } 762 763 // Re-add the mappings 764 if (!empty($maps)) 765 { 766 $i = 0; 767 foreach ($explode as $key => $value) 768 { 769 if (strpos($value,'YAMLMap') !== false) 770 { 771 $explode[$key] = str_replace('YAMLMap', $maps[$i], $value); 772 ++$i; 773 } 774 } 775 } 776 777 return $explode; 778 } 779 780 /** 781 * Builds the PHP array from all the YAML nodes we've gathered 782 * 783 * @return array 784 */ 785 protected function _buildArray() 786 { 787 $trunk = array(); 788 789 if (!isset($this->_indentSort[0])) 790 { 791 return $trunk; 792 } 793 794 foreach ($this->_indentSort[0] as $n) 795 { 796 if (empty($n->parent)) 797 { 798 $this->_nodeArrayizeData($n); 799 // Check for references and copy the needed data to complete them. 800 $this->_makeReferences($n); 801 // Merge our data with the big array we're building 802 $trunk = $this->_array_kmerge($trunk, $n->data); 803 } 804 } 805 806 return $trunk; 807 } 808 809 /** 810 * Traverses node-space and sets references (& and *) accordingly 811 * 812 * @return bool 813 */ 814 protected function _linkReferences() 815 { 816 if (is_array($this->_haveRefs)) 817 { 818 foreach ($this->_haveRefs as $node) 819 { 820 if (!empty($node->data)) 821 { 822 $key = key($node->data); 823 // If it's an array, don't check. 824 if (is_array($node->data[$key])) 825 { 826 foreach ($node->data[$key] as $k => $v) 827 { 828 $this->_linkRef($node, $key, $k, $v); 829 } 830 } 831 else 832 { 833 $this->_linkRef($node, $key); 834 } 835 } 836 } 837 } 838 839 return true; 840 } 841 842 function _linkRef(&$n, $key, $k = null, $v = null) 843 { 844 if (empty($k) && empty($v)) 845 { 846 // Look for &refs 847 if (preg_match('/^&([^ ]+)/', $n->data[$key], $matches)) 848 { 849 // Flag the node so we know it's a reference 850 $this->_allNodes[$n->id]->ref = substr($matches[0], 1); 851 $this->_allNodes[$n->id]->data[$key] = substr($n->data[$key], strlen($matches[0]) + 1); 852 // Look for *refs 853 } 854 else if (preg_match('/^\*([^ ]+)/', $n->data[$key], $matches)) 855 { 856 $ref = substr($matches[0], 1); 857 // Flag the node as having a reference 858 $this->_allNodes[$n->id]->refKey = $ref; 859 } 860 } 861 else if (!empty($k) && !empty($v)) 862 { 863 if (preg_match('/^&([^ ]+)/', $v, $matches)) 864 { 865 // Flag the node so we know it's a reference 866 $this->_allNodes[$n->id]->ref = substr($matches[0], 1); 867 $this->_allNodes[$n->id]->data[$key][$k] = substr($v, strlen($matches[0]) + 1); 868 // Look for *refs 869 } 870 else if (preg_match('/^\*([^ ]+)/', $v, $matches)) 871 { 872 $ref = substr($matches[0], 1); 873 // Flag the node as having a reference 874 $this->_allNodes[$n->id]->refKey = $ref; 875 } 876 } 877 } 878 879 /** 880 * Finds the children of a node and aids in the building of the PHP array 881 * 882 * @param int $nid The id of the node whose children we're gathering 883 * @return array 884 */ 885 protected function _gatherChildren($nid) 886 { 887 $return = array(); 888 $node =& $this->_allNodes[$nid]; 889 foreach ($this->_allNodes as $z) 890 { 891 if ($z->parent == $node->id) 892 { 893 // We found a child 894 $this->_nodeArrayizeData($z); 895 // Check for references 896 $this->_makeReferences($z); 897 // Merge with the big array we're returning 898 // The big array being all the data of the children of our parent node 899 $return = $this->_array_kmerge($return, $z->data); 900 } 901 } 902 903 return $return; 904 } 905 906 /** 907 * Turns a node's data and its children's data into a PHP array 908 * 909 * 910 * @param array $node The node which you want to arrayize 911 * @return boolean 912 */ 913 protected function _nodeArrayizeData(&$node) 914 { 915 if (is_array($node->data) && $node->children == true) 916 { 917 // This node has children, so we need to find them 918 $childs = $this->_gatherChildren($node->id); 919 // We've gathered all our children's data and are ready to use it 920 $key = key($node->data); 921 $key = empty($key) ? 0 : $key; 922 // If it's an array, add to it of course 923 if (is_array($node->data[$key])) 924 { 925 $node->data[$key] = $this->_array_kmerge($node->data[$key], $childs); 926 } 927 else 928 { 929 $node->data[$key] = $childs; 930 } 931 } 932 else if (!is_array($node->data) && $node->children == true) 933 { 934 // Same as above, find the children of this node 935 $childs = $this->_gatherChildren($node->id); 936 $node->data = array(); 937 $node->data[] = $childs; 938 } 939 940 // We edited $node by reference, so just return true 941 return true; 942 } 943 944 /** 945 * Traverses node-space and copies references to / from this object. 946 * 947 * @param object $z A node whose references we wish to make real 948 * @return bool 949 */ 950 protected function _makeReferences(&$z) 951 { 952 // It is a reference 953 if (isset($z->ref)) 954 { 955 $key = key($z->data); 956 // Copy the data to this object for easy retrieval later 957 $this->ref[$z->ref] =& $z->data[$key]; 958 // It has a reference 959 } 960 else if (isset($z->refKey)) 961 { 962 if (isset($this->ref[$z->refKey])) 963 { 964 $key = key($z->data); 965 // Copy the data from this object to make the node a real reference 966 $z->data[$key] =& $this->ref[$z->refKey]; 967 } 968 } 969 970 return true; 971 } 972 973 /** 974 * Merges arrays and maintains numeric keys. 975 * 976 * An ever-so-slightly modified version of the array_kmerge() function posted 977 * to php.net by mail at nospam dot iaindooley dot com on 2004-04-08. 978 * 979 * http://us3.php.net/manual/en/function.array-merge.php#41394 980 * 981 * @param array $arr1 982 * @param array $arr2 983 * @return array 984 */ 985 protected function _array_kmerge($arr1, $arr2) 986 { 987 if (!is_array($arr1)) 988 { 989 $arr1 = array(); 990 } 991 if (!is_array($arr2)) 992 { 993 $arr2 = array(); 994 } 995 996 $keys = array_merge(array_keys($arr1), array_keys($arr2)); 997 $vals = array_merge(array_values($arr1), array_values($arr2)); 998 $ret = array(); 999 foreach ($keys as $key) 1000 { 1001 list($unused, $val) = each($vals); 1002 if (isset($ret[$key]) && is_int($key)) 1003 { 1004 $ret[] = $val; 1005 } 1006 else 1007 { 1008 $ret[$key] = $val; 1009 } 1010 } 1011 1012 return $ret; 1013 } 1014 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |