[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/util/ -> Spyc.class.php (source)

   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  }


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7