Overview

Packages

  • Yaml

Classes

  • Horde_Yaml
  • Horde_Yaml_Dumper
  • Horde_Yaml_Exception
  • Horde_Yaml_Loader
  • Horde_Yaml_Node
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde YAML package
  4:  *
  5:  * This package is heavily inspired by the Spyc PHP YAML
  6:  * implementation (http://spyc.sourceforge.net/), and portions are
  7:  * copyright 2005-2006 Chris Wanstrath.
  8:  *
  9:  * @author   Chris Wanstrath <chris@ozmm.org>
 10:  * @author   Chuck Hagenbuch <chuck@horde.org>
 11:  * @author   Mike Naberezny <mike@maintainable.com>
 12:  * @license  http://www.horde.org/licenses/bsd BSD
 13:  * @category Horde
 14:  * @package  Yaml
 15:  */
 16: 
 17: /**
 18:  * Parse YAML strings into PHP data structures
 19:  *
 20:  * @category Horde
 21:  * @package  Yaml
 22:  */
 23: class Horde_Yaml_Loader
 24: {
 25:     /**
 26:      * List of nodes with references
 27:      * @var array
 28:      */
 29:     protected $_haveRefs = array();
 30: 
 31:     /**
 32:      * All nodes
 33:      * @var array
 34:      */
 35:     protected $_allNodes = array();
 36: 
 37:     /**
 38:      * Array of node parents
 39:      * @var array
 40:      */
 41:     protected $_allParent = array();
 42: 
 43:     /**
 44:      * Last indent level
 45:      * @var integer
 46:      */
 47:     protected $_lastIndent = 0;
 48: 
 49:     /**
 50:      * Last node id
 51:      * @var integer
 52:      */
 53:     protected $_lastNode = null;
 54: 
 55:     /**
 56:      * Is the parser inside a block?
 57:      * @var boolean
 58:      */
 59:     protected $_inBlock = false;
 60: 
 61:     /**
 62:      * @var boolean
 63:      */
 64:     protected $_isInline = false;
 65: 
 66:     /**
 67:      * Next node id to use
 68:      * @var integer
 69:      */
 70:     protected $_nodeId = 1;
 71: 
 72:     /**
 73:      * Last line number parsed.
 74:      * @var integer
 75:      */
 76:     protected $_lineNumber = 0;
 77: 
 78:     /**
 79:      * Create a new YAML parser.
 80:      */
 81:     public function __construct()
 82:     {
 83:         $base = new Horde_Yaml_Node($this->_nodeId++);
 84:         $base->indent = 0;
 85:         $this->_lastNode = $base->id;
 86:     }
 87: 
 88:     /**
 89:      * Return the PHP built from all YAML parsed so far.
 90:      *
 91:      * @return array PHP version of parsed YAML
 92:      */
 93:     public function toArray()
 94:     {
 95:         // Here we travel through node-space and pick out references
 96:         // (& and *).
 97:         $this->_linkReferences();
 98: 
 99:         // Build the PHP array out of node-space.
100:         return $this->_buildArray();
101:     }
102: 
103:     /**
104:      * Parse a line of a YAML file.
105:      *
106:      * @param  string           $line  The line of YAML to parse.
107:      * @return Horde_Yaml_Node         YAML Node
108:      */
109:     public function parse($line)
110:     {
111:         // Keep track of how many lines we've parsed for friendlier
112:         // error messages.
113:         ++$this->_lineNumber;
114: 
115:         $trimmed = trim($line);
116: 
117:         // If the line starts with a tab (instead of a space), throw a fit.
118:         if (preg_match('/^ *(\t) *[^\t ]/', $line)) {
119:             $msg = "Line {$this->_lineNumber} indent contains a tab.  "
120:                  . 'YAML only allows spaces for indentation.';
121:             throw new Horde_Yaml_Exception($msg);
122:         }
123: 
124:         if (!$this->_inBlock && empty($trimmed)) {
125:             return;
126:         } elseif ($this->_inBlock && empty($trimmed)) {
127:             $last =& $this->_allNodes[$this->_lastNode];
128:             $last->data[key($last->data)] .= "\n";
129:         } elseif ($trimmed[0] != '#' && substr($trimmed, 0, 3) != '---') {
130:             // Create a new node and get its indent
131:             $node = new Horde_Yaml_Node($this->_nodeId++);
132:             $node->indent = $this->_getIndent($line);
133: 
134:             // Check where the node lies in the hierarchy
135:             if ($this->_lastIndent == $node->indent) {
136:                 // If we're in a block, add the text to the parent's data
137:                 if ($this->_inBlock) {
138:                     $parent =& $this->_allNodes[$this->_lastNode];
139:                     $parent->data[key($parent->data)] .= trim($line) . $this->_blockEnd;
140:                 } else {
141:                     // The current node's parent is the same as the previous node's
142:                     if (isset($this->_allNodes[$this->_lastNode])) {
143:                         $node->parent = $this->_allNodes[$this->_lastNode]->parent;
144:                     }
145:                 }
146:             } elseif ($this->_lastIndent < $node->indent) {
147:                 if ($this->_inBlock) {
148:                     $parent =& $this->_allNodes[$this->_lastNode];
149:                     $parent->data[key($parent->data)] .= trim($line) . $this->_blockEnd;
150:                 } elseif (!$this->_inBlock) {
151:                     // The current node's parent is the previous node
152:                     $node->parent = $this->_lastNode;
153: 
154:                     // If the value of the last node's data was > or |
155:                     // we need to start blocking i.e. taking in all
156:                     // lines as a text value until we drop our indent.
157:                     $parent =& $this->_allNodes[$node->parent];
158:                     $this->_allNodes[$node->parent]->children = true;
159:                     if (is_array($parent->data)) {
160:                         if (isset($parent->data[key($parent->data)])) {
161:                             $chk = $parent->data[key($parent->data)];
162:                             if ($chk === '>') {
163:                                 $this->_inBlock = true;
164:                                 $this->_blockEnd = '';
165:                                 $parent->data[key($parent->data)] =
166:                                     str_replace('>', '', $parent->data[key($parent->data)]);
167:                                 $parent->data[key($parent->data)] .= trim($line) . ' ';
168:                                 $this->_allNodes[$node->parent]->children = false;
169:                                 $this->_lastIndent = $node->indent;
170:                             } elseif ($chk === '|') {
171:                                 $this->_inBlock = true;
172:                                 $this->_blockEnd = "\n";
173:                                 $parent->data[key($parent->data)] =
174:                                     str_replace('|', '', $parent->data[key($parent->data)]);
175:                                 $parent->data[key($parent->data)] .= trim($line) . "\n";
176:                                 $this->_allNodes[$node->parent]->children = false;
177:                                 $this->_lastIndent = $node->indent;
178:                             }
179:                         }
180:                     }
181:                 }
182:             } elseif ($this->_lastIndent > $node->indent) {
183:                 // Any block we had going is dead now
184:                 if ($this->_inBlock) {
185:                     $this->_inBlock = false;
186:                     if ($this->_blockEnd == "\n") {
187:                         $last =& $this->_allNodes[$this->_lastNode];
188:                         $last->data[key($last->data)] =
189:                             trim($last->data[key($last->data)]);
190:                     }
191:                 }
192: 
193:                 // We don't know the parent of the node so we have to
194:                 // find it
195:                 foreach ($this->_indentSort[$node->indent] as $n) {
196:                     if ($n->indent == $node->indent) {
197:                         $node->parent = $n->parent;
198:                     }
199:                 }
200:             }
201: 
202:             if (!$this->_inBlock) {
203:                 // Set these properties with information from our
204:                 // current node
205:                 $this->_lastIndent = $node->indent;
206: 
207:                 // Set the last node
208:                 $this->_lastNode = $node->id;
209: 
210:                 // Parse the YAML line and return its data
211:                 $node->data = $this->_parseLine($line);
212: 
213:                 // Add the node to the master list
214:                 $this->_allNodes[$node->id] = $node;
215: 
216:                 // Add a reference to the parent list
217:                 $this->_allParent[intval($node->parent)][] = $node->id;
218: 
219:                 // Add a reference to the node in an indent array
220:                 $this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id];
221: 
222:                 // Add a reference to the node in a References array
223:                 // if this node has a YAML reference in it.
224:                 $is_array = is_array($node->data);
225:                 $key = key($node->data);
226:                 $isset = isset($node->data[$key]);
227:                 if ($isset) {
228:                     $nodeval = $node->data[$key];
229:                 }
230:                 if (($is_array && $isset && !is_array($nodeval) && !is_object($nodeval))
231:                     && (strlen($nodeval) && ($nodeval[0] == '&' || $nodeval[0] == '*') && $nodeval[1] != ' ')) {
232:                     $this->_haveRefs[] =& $this->_allNodes[$node->id];
233:                 } elseif ($is_array && $isset && is_array($nodeval)) {
234:                     // Incomplete reference making code. Needs to be
235:                     // cleaned up.
236:                     foreach ($node->data[$key] as $d) {
237:                         if (!is_array($d) && strlen($d) && (($d[0] == '&' || $d[0] == '*') && $d[1] != ' ')) {
238:                             $this->_haveRefs[] =& $this->_allNodes[$node->id];
239:                         }
240:                     }
241:                 }
242:             }
243:         }
244:     }
245: 
246:     /**
247:      * Finds and returns the indentation of a YAML line
248:      *
249:      * @param  string  $line  A line from the YAML file
250:      * @return int            Indentation level
251:      */
252:     protected function _getIndent($line)
253:     {
254:         if (preg_match('/^\s+/', $line, $match)) {
255:             return strlen($match[0]);
256:         } else {
257:             return 0;
258:         }
259:     }
260: 
261:     /**
262:      * Parses YAML code and returns an array for a node
263:      *
264:      * @param  string  $line  A line from the YAML file
265:      * @return array
266:      */
267:     protected function _parseLine($line)
268:     {
269:         $array = array();
270: 
271:         $line = trim($line);
272:         if (preg_match('/^-(.*):$/', $line)) {
273:             // It's a mapped sequence
274:             $key = trim(substr(substr($line, 1), 0, -1));
275:             $array[$key] = '';
276:         } elseif ($line[0] == '-' && substr($line, 0, 3) != '---') {
277:             // It's a list item but not a new stream
278:             if (strlen($line) > 1) {
279:                 // Set the type of the value. Int, string, etc
280:                 $array[] = $this->_toType(trim(substr($line, 1)));
281:             } else {
282:                 $array[] = array();
283:             }
284:         } elseif (preg_match('/^(.+):/', $line, $key)) {
285:             // It's a key/value pair most likely
286:             // If the key is in double quotes pull it out
287:             if (preg_match('/^(["\'](.*)["\'](\s)*:)/', $line, $matches)) {
288:                 $value = trim(str_replace($matches[1], '', $line));
289:                 $key = $matches[2];
290:             } else {
291:                 // Do some guesswork as to the key and the value
292:                 $explode = explode(':', $line);
293:                 $key = trim(array_shift($explode));
294:                 $value = trim(implode(':', $explode));
295:             }
296: 
297:             // Set the type of the value. Int, string, etc
298:             $value = $this->_toType($value);
299:             if (empty($key)) {
300:                 $array[] = $value;
301:             } else {
302:                 $array[$key] = $value;
303:             }
304:         }
305: 
306:         return $array;
307:     }
308: 
309:     /**
310:      * Finds the type of the passed value, returns the value as the new type.
311:      *
312:      * @param  string   $value
313:      * @return mixed
314:      */
315:     protected function _toType($value)
316:     {
317:         // Check for PHP specials
318:         self::_unserialize($value);
319:         if (!is_scalar($value)) {
320:             return $value;
321:         }
322: 
323:         // Used in a lot of cases.
324:         $lower_value = strtolower($value);
325: 
326:         if (preg_match('/^("(.*)"|\'(.*)\')/', $value, $matches)) {
327:             $value = (string)str_replace(array('\'\'', '\\\''), "'", end($matches));
328:             $value = str_replace('\\"', '"', $value);
329:         } elseif (preg_match('/^\\[(\s*)\\]$/', $value)) {
330:             // empty inline mapping
331:             $value = array();
332:         } elseif (preg_match('/^\\[(.+)\\]$/', $value, $matches)) {
333:             // Inline Sequence
334: 
335:             // Take out strings sequences and mappings
336:             $explode = $this->_inlineEscape($matches[1]);
337: 
338:             // Propogate value array
339:             $value  = array();
340:             foreach ($explode as $v) {
341:                 $value[] = $this->_toType($v);
342:             }
343:         } elseif (preg_match('/^\\{(\s*)\\}$/', $value)) {
344:             // empty inline mapping
345:             $value = array();
346:         } elseif (strpos($value, ': ') !== false && !preg_match('/^{(.+)/', $value)) {
347:             // inline mapping
348:             $array = explode(': ', $value);
349:             $key = trim($array[0]);
350:             array_shift($array);
351:             $value = trim(implode(': ', $array));
352:             $value = $this->_toType($value);
353:             $value = array($key => $value);
354:         } elseif (preg_match("/{(.+)}$/", $value, $matches)) {
355:             // Inline Mapping
356: 
357:             // Take out strings sequences and mappings
358:             $explode = $this->_inlineEscape($matches[1]);
359: 
360:             // Propogate value array
361:             $array = array();
362:             foreach ($explode as $v) {
363:                 $array = $array + $this->_toType($v);
364:             }
365:             $value = $array;
366:         } elseif ($lower_value == 'null' || $value == '' || $value == '~') {
367:             $value = null;
368:         } elseif ($lower_value == '.nan') {
369:             $value = NAN;
370:         } elseif ($lower_value == '.inf') {
371:             $value = INF;
372:         } elseif ($lower_value == '-.inf') {
373:             $value = -INF;
374:         } elseif (ctype_digit($value)) {
375:             $value = (int)$value;
376:         } elseif (in_array($lower_value,
377:                            array('true', 'on', '+', 'yes', 'y'))) {
378:             $value = true;
379:         } elseif (in_array($lower_value,
380:                            array('false', 'off', '-', 'no', 'n'))) {
381:             $value = false;
382:         } elseif (is_numeric($value)) {
383:             $value = (float)$value;
384:         } else {
385:             // Just a normal string, right?
386:             if (($pos = strpos($value, '#')) !== false) {
387:                 $value = substr($value, 0, $pos);
388:             }
389:             $value = trim($value);
390:         }
391: 
392:         return $value;
393:     }
394: 
395:     /**
396:      * Handle PHP serialized data.
397:      *
398:      * @param string &$data Data to check for serialized PHP types.
399:      */
400:     protected function _unserialize(&$data)
401:     {
402:         if (substr($data, 0, 5) != '!php/') {
403:             return;
404:         }
405: 
406:         $first_space = strpos($data, ' ');
407:         $type = substr($data, 5, $first_space - 5);
408:         $class = null;
409:         if (strpos($type, '::') !== false) {
410:             list($type, $class) = explode('::', $type);
411: 
412:             if (!in_array($class, Horde_Yaml::$allowedClasses)) {
413:                 throw new Horde_Yaml_Exception("$class is not in the list of allowed classes");
414:             }
415:         }
416: 
417:         switch ($type) {
418:         case 'object':
419:             if (!class_exists($class)) {
420:                 throw new Horde_Yaml_Exception("$class is not defined");
421:             }
422: 
423:             $reflector = new ReflectionClass($class);
424:             if (!$reflector->implementsInterface('Serializable')) {
425:                 throw new Horde_Yaml_Exception("$class does not implement Serializable");
426:             }
427: 
428:             $class_data = substr($data, $first_space + 1);
429:             $serialized = 'C:' . strlen($class) . ':"' . $class . '":' . strlen($class_data) . ':{' . $class_data . '}';
430:             $data = unserialize($serialized);
431:             break;
432: 
433:         case 'array':
434:         case 'hash':
435:             $array_data = substr($data, $first_space + 1);
436:             $array_data = Horde_Yaml::load('a: ' . $array_data);
437: 
438:             if (is_null($class)) {
439:                 $data = $array_data['a'];
440:             } else {
441:                 if (!class_exists($class)) {
442:                     throw new Horde_Yaml_Exception("$class is not defined");
443:                 }
444: 
445:                 $array = new $class;
446:                 if (!$array instanceof ArrayAccess) {
447:                     throw new Horde_Yaml_Exception("$class does not implement ArrayAccess");
448:                 }
449: 
450:                 foreach ($array_data['a'] as $key => $val) {
451:                     $array[$key] = $val;
452:                 }
453: 
454:                 $data = $array;
455:             }
456:             break;
457:         }
458:     }
459: 
460:     /**
461:      * Used in inlines to check for more inlines or quoted strings
462:      *
463:      * @todo  There should be a cleaner way to do this.  While
464:      *        pure sequences seem to be nesting just fine,
465:      *        pure mappings and mappings with sequences inside
466:      *        can't go very deep.  This needs to be fixed.
467:      *
468:      * @param  string  $inline  Inline data
469:      * @return array
470:      */
471:     protected function _inlineEscape($inline)
472:     {
473:         $saved_strings = array();
474: 
475:         // Check for strings
476:         $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
477:         if (preg_match_all($regex, $inline, $strings)) {
478:             $saved_strings = $strings[0];
479:             $inline = preg_replace($regex, 'YAMLString', $inline);
480:         }
481: 
482:         // Check for sequences
483:         if (preg_match_all('/\[(.+)\]/U', $inline, $seqs)) {
484:             $inline = preg_replace('/\[(.+)\]/U', 'YAMLSeq', $inline);
485:             $seqs = $seqs[0];
486:         }
487: 
488:         // Check for mappings
489:         if (preg_match_all('/{(.+)}/U', $inline, $maps)) {
490:             $inline = preg_replace('/{(.+)}/U', 'YAMLMap', $inline);
491:             $maps = $maps[0];
492:         }
493: 
494:         $explode = explode(', ', $inline);
495: 
496:         // Re-add the sequences
497:         if (!empty($seqs)) {
498:             $i = 0;
499:             foreach ($explode as $key => $value) {
500:                 if (strpos($value, 'YAMLSeq') !== false) {
501:                     $explode[$key] = str_replace('YAMLSeq', $seqs[$i], $value);
502:                     ++$i;
503:                 }
504:             }
505:         }
506: 
507:         // Re-add the mappings
508:         if (!empty($maps)) {
509:             $i = 0;
510:             foreach ($explode as $key => $value) {
511:                 if (strpos($value, 'YAMLMap') !== false) {
512:                     $explode[$key] = str_replace('YAMLMap', $maps[$i], $value);
513:                     ++$i;
514:                 }
515:             }
516:         }
517: 
518:         // Re-add the strings
519:         if (!empty($saved_strings)) {
520:             $i = 0;
521:             foreach ($explode as $key => $value) {
522:                 while (strpos($value, 'YAMLString') !== false) {
523:                     $explode[$key] = preg_replace('/YAMLString/', $saved_strings[$i], $value, 1);
524:                     ++$i;
525:                     $value = $explode[$key];
526:                 }
527:             }
528:         }
529: 
530:         return $explode;
531:     }
532: 
533:     /**
534:      * Builds the PHP array from all the YAML nodes we've gathered
535:      *
536:      * @return array
537:      */
538:     protected function _buildArray()
539:     {
540:         $trunk = array();
541:         if (!isset($this->_indentSort[0])) {
542:             return $trunk;
543:         }
544: 
545:         foreach ($this->_indentSort[0] as $n) {
546:             if (empty($n->parent)) {
547:                 $this->_nodeArrayizeData($n);
548: 
549:                 // Check for references and copy the needed data to complete them.
550:                 $this->_makeReferences($n);
551: 
552:                 // Merge our data with the big array we're building
553:                 $trunk = $this->_array_kmerge($trunk, $n->data);
554:             }
555:         }
556: 
557:         return $trunk;
558:     }
559: 
560:     /**
561:      * Traverses node-space and sets references (& and *) accordingly
562:      *
563:      * @return bool
564:      */
565:     protected function _linkReferences()
566:     {
567:         if (is_array($this->_haveRefs)) {
568:             foreach ($this->_haveRefs as $node) {
569:                 if (!empty($node->data)) {
570:                     $key = key($node->data);
571:                     // If it's an array, don't check.
572:                     if (is_array($node->data[$key])) {
573:                         foreach ($node->data[$key] as $k => $v) {
574:                             $this->_linkRef($node, $key, $k, $v);
575:                         }
576:                     } else {
577:                         $this->_linkRef($node, $key);
578:                     }
579:                 }
580:             }
581:         }
582: 
583:         return true;
584:     }
585: 
586:     /**
587:      * Helper for _linkReferences()
588:      *
589:      * @param  Horde_Yaml_Node  $n   Node
590:      * @param  string           $k   Key
591:      * @param  mixed            $v   Value
592:      * @return void
593:      */
594:     function _linkRef(&$n, $key, $k = null, $v = null)
595:     {
596:         if (empty($k) && empty($v)) {
597:             // Look for &refs
598:             if (preg_match('/^&([^ ]+)/', $n->data[$key], $matches)) {
599:                 // Flag the node so we know it's a reference
600:                 $this->_allNodes[$n->id]->ref = substr($matches[0], 1);
601:                 $this->_allNodes[$n->id]->data[$key] =
602:                     substr($n->data[$key], strlen($matches[0]) + 1);
603:                 // Look for *refs
604:             } elseif (preg_match('/^\*([^ ]+)/', $n->data[$key], $matches)) {
605:                 $ref = substr($matches[0], 1);
606:                 // Flag the node as having a reference
607:                 $this->_allNodes[$n->id]->refKey = $ref;
608:             }
609:         } elseif (!empty($k) && !empty($v)) {
610:             if (preg_match('/^&([^ ]+)/', $v, $matches)) {
611:                 // Flag the node so we know it's a reference
612:                 $this->_allNodes[$n->id]->ref = substr($matches[0], 1);
613:                 $this->_allNodes[$n->id]->data[$key][$k] =
614:                     substr($v, strlen($matches[0]) + 1);
615:                 // Look for *refs
616:             } elseif (preg_match('/^\*([^ ]+)/', $v, $matches)) {
617:                 $ref = substr($matches[0], 1);
618:                 // Flag the node as having a reference
619:                 $this->_allNodes[$n->id]->refKey = $ref;
620:             }
621:         }
622:     }
623: 
624:     /**
625:      * Finds the children of a node and aids in the building of the PHP array
626:      *
627:      * @param  int    $nid   The id of the node whose children we're gathering
628:      * @return array
629:      */
630:     protected function _gatherChildren($nid)
631:     {
632:         $return = array();
633:         $node =& $this->_allNodes[$nid];
634:         if (is_array ($this->_allParent[$node->id])) {
635:             foreach ($this->_allParent[$node->id] as $nodeZ) {
636:                 $z =& $this->_allNodes[$nodeZ];
637:                 // We found a child
638:                 $this->_nodeArrayizeData($z);
639: 
640:                 // Check for references
641:                 $this->_makeReferences($z);
642: 
643:                 // Merge with the big array we're returning, the big
644:                 // array being all the data of the children of our
645:                 // parent node
646:                 $return = $this->_array_kmerge($return, $z->data);
647:             }
648:         }
649:         return $return;
650:     }
651: 
652:     /**
653:      * Turns a node's data and its children's data into a PHP array
654:      *
655:      * @param  array    $node  The node which you want to arrayize
656:      * @return boolean
657:      */
658:     protected function _nodeArrayizeData(&$node)
659:     {
660:         if ($node->children == true) {
661:             if (is_array($node->data)) {
662:                 // This node has children, so we need to find them
663:                 $children = $this->_gatherChildren($node->id);
664: 
665:                 // We've gathered all our children's data and are ready to use it
666:                 $key = key($node->data);
667:                 $key = empty($key) ? 0 : $key;
668:                 // If it's an array, add to it of course
669:                 if (isset($node->data[$key])) {
670:                     if (is_array($node->data[$key])) {
671:                         $node->data[$key] = $this->_array_kmerge($node->data[$key], $children);
672:                     } else {
673:                         $node->data[$key] = $children;
674:                     }
675:                 } else {
676:                     $node->data[$key] = $children;
677:                 }
678:             } else {
679:                 // Same as above, find the children of this node
680:                 $children = $this->_gatherChildren($node->id);
681:                 $node->data = array();
682:                 $node->data[] = $children;
683:             }
684:         } else {
685:             // The node is a single string. See if we need to unserialize it.
686:             if (is_array($node->data)) {
687:                 $key = key($node->data);
688:                 $key = empty($key) ? 0 : $key;
689: 
690:                 if (!isset($node->data[$key]) || is_array($node->data[$key]) || is_object($node->data[$key])) {
691:                     return true;
692:                 }
693: 
694:                 self::_unserialize($node->data[$key]);
695:             } elseif (is_string($node->data)) {
696:                 self::_unserialize($node->data);
697:             }
698:         }
699: 
700:         // We edited $node by reference, so just return true
701:         return true;
702:     }
703: 
704:     /**
705:      * Traverses node-space and copies references to / from this object.
706:      *
707:      * @param  Horde_Yaml_Node  $z  A node whose references we wish to make real
708:      * @return bool
709:      */
710:     protected function _makeReferences(&$z)
711:     {
712:         // It is a reference
713:         if (isset($z->ref)) {
714:             $key = key($z->data);
715:             // Copy the data to this object for easy retrieval later
716:             $this->ref[$z->ref] =& $z->data[$key];
717:             // It has a reference
718:         } elseif (isset($z->refKey)) {
719:             if (isset($this->ref[$z->refKey])) {
720:                 $key = key($z->data);
721:                 // Copy the data from this object to make the node a real reference
722:                 $z->data[$key] =& $this->ref[$z->refKey];
723:             }
724:         }
725: 
726:         return true;
727:     }
728: 
729:     /**
730:      * Merges two arrays, maintaining numeric keys. If two numeric
731:      * keys clash, the second one will be appended to the resulting
732:      * array. If string keys clash, the last one wins.
733:      *
734:      * @param  array  $arr1
735:      * @param  array  $arr2
736:      * @return array
737:      */
738:     protected function _array_kmerge($arr1, $arr2)
739:     {
740:         while (list($key, $val) = each($arr2)) {
741:             if (isset($arr1[$key]) && is_int($key)) {
742:                 $arr1[] = $val;
743:             } else {
744:                 $arr1[$key] = $val;
745:             }
746:         }
747: 
748:         return $arr1;
749:     }
750: 
751: }
752: 
API documentation generated by ApiGen