Stylize.php on API
[lhc/web/wiklou.git] / includes / api / ApiFormatYaml_spyc.php
1 <?php
2 /**
3 * Spyc -- A Simple PHP YAML Class
4 * @version 0.4.5
5 * @author Vlad Andersen <vlad.andersen@gmail.com>
6 * @author Chris Wanstrath <chris@ozmm.org>
7 * @link http://code.google.com/p/spyc/
8 * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen
9 * @license http://www.opensource.org/licenses/mit-license.php MIT License
10 * @package Spyc
11 */
12 /**
13 * The Simple PHP YAML Class.
14 *
15 * This class can be used to read a YAML file and convert its contents
16 * into a PHP array. It currently supports a very limited subsection of
17 * the YAML spec.
18 */
19 class Spyc {
20
21 // SETTINGS
22
23 /**
24 * Setting this to true will force YAMLDump to enclose any string value in
25 * quotes. False by default.
26 *
27 * @var bool
28 */
29 public $setting_dump_force_quotes = false;
30
31 /**
32 * Setting this to true will forse YAMLLoad to use syck_load function when
33 * possible. False by default.
34 * @var bool
35 */
36 public $setting_use_syck_is_possible = false;
37
38
39
40 /**#@+
41 * @access private
42 * @var mixed
43 */
44 private $_dumpIndent;
45 private $_dumpWordWrap;
46 private $_containsGroupAnchor = false;
47 private $_containsGroupAlias = false;
48 private $path;
49 private $result;
50 private $LiteralPlaceHolder = '___YAML_Literal_Block___';
51 private $SavedGroups = array();
52 private $indent;
53 /**
54 * Path modifier that should be applied after adding current element.
55 * @var array
56 */
57 private $delayedPath = array();
58
59 /**#@+
60 * @access public
61 * @var mixed
62 */
63 public $_nodeId;
64
65 /**
66 * Load a valid YAML string to Spyc.
67 * @param string $input
68 * @return array
69 */
70 public function load ( $input ) {
71 return $this->__loadString( $input );
72 }
73
74 /**
75 * Load a valid YAML file to Spyc.
76 * @param string $file
77 * @return array
78 */
79 public function loadFile ( $file ) {
80 return $this->__load( $file );
81 }
82
83 /**
84 * Load YAML into a PHP array statically
85 *
86 * The load method, when supplied with a YAML stream (string or file),
87 * will do its best to convert YAML in a file into a PHP array. Pretty
88 * simple.
89 * Usage:
90 * <code>
91 * $array = Spyc::YAMLLoad('lucky.yaml');
92 * print_r($array);
93 * </code>
94 * @access public
95 * @return array
96 * @param string $input Path of YAML file or string containing YAML
97 */
98 public static function YAMLLoad( $input ) {
99 $Spyc = new Spyc;
100 return $Spyc->__load( $input );
101 }
102
103 /**
104 * Load a string of YAML into a PHP array statically
105 *
106 * The load method, when supplied with a YAML string, will do its best
107 * to convert YAML in a string into a PHP array. Pretty simple.
108 *
109 * Note: use this function if you don't want files from the file system
110 * loaded and processed as YAML. This is of interest to people concerned
111 * about security whose input is from a string.
112 *
113 * Usage:
114 * <code>
115 * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
116 * print_r($array);
117 * </code>
118 * @access public
119 * @return array
120 * @param string $input String containing YAML
121 */
122 public static function YAMLLoadString( $input ) {
123 $Spyc = new Spyc;
124 return $Spyc->__loadString( $input );
125 }
126
127 /**
128 * Dump YAML from PHP array statically
129 *
130 * The dump method, when supplied with an array, will do its best
131 * to convert the array into friendly YAML. Pretty simple. Feel free to
132 * save the returned string as nothing.yaml and pass it around.
133 *
134 * Oh, and you can decide how big the indent is and what the wordwrap
135 * for folding is. Pretty cool -- just pass in 'false' for either if
136 * you want to use the default.
137 *
138 * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
139 * you can turn off wordwrap by passing in 0.
140 *
141 * @access public
142 * @return string
143 * @param array $array PHP array
144 * @param int $indent Pass in false to use the default, which is 2
145 * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
146 */
147 public static function YAMLDump( $array, $indent = false, $wordwrap = false ) {
148 $spyc = new Spyc;
149 return $spyc->dump( $array, $indent, $wordwrap );
150 }
151
152
153 /**
154 * Dump PHP array to YAML
155 *
156 * The dump method, when supplied with an array, will do its best
157 * to convert the array into friendly YAML. Pretty simple. Feel free to
158 * save the returned string as tasteful.yaml and pass it around.
159 *
160 * Oh, and you can decide how big the indent is and what the wordwrap
161 * for folding is. Pretty cool -- just pass in 'false' for either if
162 * you want to use the default.
163 *
164 * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
165 * you can turn off wordwrap by passing in 0.
166 *
167 * @access public
168 * @return string
169 * @param array $array PHP array
170 * @param int $indent Pass in false to use the default, which is 2
171 * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
172 */
173 public function dump( $array, $indent = false, $wordwrap = false ) {
174 // Dumps to some very clean YAML. We'll have to add some more features
175 // and options soon. And better support for folding.
176
177 // New features and options.
178 if ( $indent === false or !is_numeric( $indent ) ) {
179 $this->_dumpIndent = 2;
180 } else {
181 $this->_dumpIndent = $indent;
182 }
183
184 if ( $wordwrap === false or !is_numeric( $wordwrap ) ) {
185 $this->_dumpWordWrap = 40;
186 } else {
187 $this->_dumpWordWrap = $wordwrap;
188 }
189
190 // New YAML document
191 $string = "---\n";
192
193 // Start at the base of the array and move through it.
194 if ( $array ) {
195 $array = (array)$array;
196 $first_key = key( $array );
197
198 $previous_key = - 1;
199 foreach ( $array as $key => $value ) {
200 $string .= $this->_yamlize( $key, $value, 0, $previous_key, $first_key );
201 $previous_key = $key;
202 }
203 }
204 return $string;
205 }
206
207 /**
208 * Attempts to convert a key / value array item to YAML
209 * @access private
210 * @return string
211 * @param $key The name of the key
212 * @param $value The value of the item
213 * @param $indent The indent of the current node
214 */
215 private function _yamlize( $key, $value, $indent, $previous_key = - 1, $first_key = 0 ) {
216 if ( is_array( $value ) ) {
217 if ( empty ( $value ) )
218 return $this->_dumpNode( $key, array(), $indent, $previous_key, $first_key );
219 // It has children. What to do?
220 // Make it the right kind of item
221 $string = $this->_dumpNode( $key, NULL, $indent, $previous_key, $first_key );
222 // Add the indent
223 $indent += $this->_dumpIndent;
224 // Yamlize the array
225 $string .= $this->_yamlizeArray( $value, $indent );
226 } elseif ( !is_array( $value ) ) {
227 // It doesn't have children. Yip.
228 $string = $this->_dumpNode( $key, $value, $indent, $previous_key, $first_key );
229 }
230 return $string;
231 }
232
233 /**
234 * Attempts to convert an array to YAML
235 * @access private
236 * @return string
237 * @param $array The array you want to convert
238 * @param $indent The indent of the current level
239 */
240 private function _yamlizeArray( $array, $indent ) {
241 if ( is_array( $array ) ) {
242 $string = '';
243 $previous_key = - 1;
244 $first_key = key( $array );
245 foreach ( $array as $key => $value ) {
246 $string .= $this->_yamlize( $key, $value, $indent, $previous_key, $first_key );
247 $previous_key = $key;
248 }
249 return $string;
250 } else {
251 return false;
252 }
253 }
254
255 /**
256 * Returns YAML from a key and a value
257 * @access private
258 * @return string
259 * @param $key The name of the key
260 * @param $value The value of the item
261 * @param $indent The indent of the current node
262 */
263 private function _dumpNode( $key, $value, $indent, $previous_key = - 1, $first_key = 0 ) {
264 // do some folding here, for blocks
265 if ( is_string ( $value ) && ( ( strpos( $value, "\n" ) !== false || strpos( $value, ": " ) !== false || strpos( $value, "- " ) !== false ||
266 strpos( $value, "*" ) !== false || strpos( $value, "#" ) !== false || strpos( $value, "<" ) !== false || strpos( $value, ">" ) !== false ||
267 strpos( $value, "[" ) !== false || strpos( $value, "]" ) !== false || strpos( $value, "{" ) !== false || strpos( $value, "}" ) !== false ) || substr ( $value, - 1, 1 ) == ':' ) ) {
268 $value = $this->_doLiteralBlock( $value, $indent );
269 } else {
270 $value = $this->_doFolding( $value, $indent );
271 if ( is_bool( $value ) ) {
272 $value = ( $value ) ? "true" : "false";
273 }
274 }
275
276 if ( $value === array() ) $value = '[ ]';
277
278 if ( is_int( $key ) ) {
279 // It's a sequence
280 if ( $value !== '' && !is_null( $value ) )
281 $string = $spaces . '- ' . $value . "\n";
282 else
283 $string = $spaces . "-\n";
284 } else {
285 if ( $key == '*' ) // bug 21922 - Quote asterix used as keys
286 $key = "'*'";
287
288 // It's mapped
289 if ( $value !== '' && !is_null( $value ) )
290 $string = $spaces . $key . ': ' . $value . "\n";
291 else
292 $string = $spaces . $key . ":\n";
293 }
294 return $string;
295 }
296
297 if ( is_int( $key ) && $key - 1 == $previous_key && $first_key === 0 ) {
298 // It's a sequence
299 $string = $spaces . '- ' . $value . "\n";
300 } else {
301 if ( $first_key === 0 ) throw new Exception( 'Keys are all screwy. The first one was zero, now it\'s "' . $key . '"' );
302 // It's mapped
303 if ( strpos( $key, ":" ) !== false ) { $key = '"' . $key . '"'; }
304 $string = $spaces . $key . ': ' . $value . "\n";
305 }
306 return $string;
307 }
308
309 /**
310 * Creates a literal block for dumping
311 * @access private
312 * @return string
313 * @param $value
314 * @param $indent int The value of the indent
315 */
316 private function _doLiteralBlock( $value, $indent ) {
317 if ( strpos( $value, "\n" ) === false && strpos( $value, "'" ) === false ) {
318 return sprintf ( "'%s'", $value );
319 }
320 if ( strpos( $value, "\n" ) === false && strpos( $value, '"' ) === false ) {
321 return sprintf ( '"%s"', $value );
322 }
323 $exploded = explode( "\n", $value );
324 $newValue = '|';
325 $indent += $this->_dumpIndent;
326 $spaces = str_repeat( ' ', $indent );
327 foreach ( $exploded as $line ) {
328 $newValue .= "\n" . $spaces . trim( $line );
329 }
330 return $newValue;
331 }
332
333 /**
334 * Folds a string of text, if necessary
335 * @access private
336 * @return string
337 * @param $value The string you wish to fold
338 */
339 private function _doFolding( $value, $indent ) {
340 // Don't do anything if wordwrap is set to 0
341
342 if ( $this->_dumpWordWrap !== 0 && is_string ( $value ) && strlen( $value ) > $this->_dumpWordWrap ) {
343 $indent += $this->_dumpIndent;
344 $indent = str_repeat( ' ', $indent );
345 $wrapped = wordwrap( $value, $this->_dumpWordWrap, "\n$indent" );
346 $value = ">\n" . $indent . $wrapped;
347 } else {
348 if ( $this->setting_dump_force_quotes && is_string ( $value ) )
349 $value = '"' . $value . '"';
350 }
351
352
353 return $value;
354 }
355
356 // LOADING FUNCTIONS
357
358 private function __load( $input ) {
359 $Source = $this->loadFromSource( $input );
360 return $this->loadWithSource( $Source );
361 }
362
363 private function __loadString( $input ) {
364 $Source = $this->loadFromString( $input );
365 return $this->loadWithSource( $Source );
366 }
367
368 private function loadWithSource( $Source ) {
369 if ( empty ( $Source ) ) return array();
370 if ( $this->setting_use_syck_is_possible && function_exists ( 'syck_load' ) ) {
371 $array = syck_load ( implode ( '', $Source ) );
372 return is_array( $array ) ? $array : array();
373 }
374
375 $this->path = array();
376 $this->result = array();
377
378 $cnt = count( $Source );
379 for ( $i = 0; $i < $cnt; $i++ ) {
380 $line = $Source[$i];
381
382 $this->indent = strlen( $line ) - strlen( ltrim( $line ) );
383 $tempPath = $this->getParentPathByIndent( $this->indent );
384 $line = self::stripIndent( $line, $this->indent );
385 if ( self::isComment( $line ) ) continue;
386 if ( self::isEmpty( $line ) ) continue;
387 $this->path = $tempPath;
388
389 $literalBlockStyle = self::startsLiteralBlock( $line );
390 if ( $literalBlockStyle ) {
391 $line = rtrim ( $line, $literalBlockStyle . " \n" );
392 $literalBlock = '';
393 $line .= $this->LiteralPlaceHolder;
394
395 while ( ++$i < $cnt && $this->literalBlockContinues( $Source[$i], $this->indent ) ) {
396 $literalBlock = $this->addLiteralLine( $literalBlock, $Source[$i], $literalBlockStyle );
397 }
398 $i--;
399 }
400
401 while ( ++$i < $cnt && self::greedilyNeedNextLine( $line ) ) {
402 $line = rtrim ( $line, " \n\t\r" ) . ' ' . ltrim ( $Source[$i], " \t" );
403 }
404 $i--;
405
406
407
408 if ( strpos ( $line, '#' ) ) {
409 if ( strpos ( $line, '"' ) === false && strpos ( $line, "'" ) === false )
410 $line = preg_replace( '/\s+#(.+)$/', '', $line );
411 }
412
413 $lineArray = $this->_parseLine( $line );
414
415 if ( $literalBlockStyle )
416 $lineArray = $this->revertLiteralPlaceHolder ( $lineArray, $literalBlock );
417
418 $this->addArray( $lineArray, $this->indent );
419
420 foreach ( $this->delayedPath as $indent => $delayedPath )
421 $this->path[$indent] = $delayedPath;
422
423 $this->delayedPath = array();
424
425 }
426 return $this->result;
427 }
428
429 private function loadFromSource ( $input ) {
430 if ( !empty( $input ) && strpos( $input, "\n" ) === false && file_exists( $input ) )
431 return file( $input );
432
433 return $this->loadFromString( $input );
434 }
435
436 private function loadFromString ( $input ) {
437 $lines = explode( "\n", $input );
438 foreach ( $lines as $k => $_ ) {
439 $lines[$k] = rtrim ( $_, "\r" );
440 }
441 return $lines;
442 }
443
444 /**
445 * Parses YAML code and returns an array for a node
446 * @access private
447 * @return array
448 * @param string $line A line from the YAML file
449 */
450 private function _parseLine( $line ) {
451 if ( !$line ) return array();
452 $line = trim( $line );
453
454 if ( !$line ) return array();
455 $array = array();
456
457 $group = $this->nodeContainsGroup( $line );
458 if ( $group ) {
459 $this->addGroup( $line, $group );
460 $line = $this->stripGroup ( $line, $group );
461 }
462
463 if ( $this->startsMappedSequence( $line ) )
464 return $this->returnMappedSequence( $line );
465
466 if ( $this->startsMappedValue( $line ) )
467 return $this->returnMappedValue( $line );
468
469 if ( $this->isArrayElement( $line ) )
470 return $this->returnArrayElement( $line );
471
472 if ( $this->isPlainArray( $line ) )
473 return $this->returnPlainArray( $line );
474
475
476 return $this->returnKeyValuePair( $line );
477
478 }
479
480 /**
481 * Finds the type of the passed value, returns the value as the new type.
482 * @access private
483 * @param string $value
484 * @return mixed
485 */
486 private function _toType( $value ) {
487 if ( $value === '' ) return null;
488 $first_character = $value[0];
489 $last_character = substr( $value, - 1, 1 );
490
491 $is_quoted = false;
492 do {
493 if ( !$value ) break;
494 if ( $first_character != '"' && $first_character != "'" ) break;
495 if ( $last_character != '"' && $last_character != "'" ) break;
496 $is_quoted = true;
497 } while ( 0 );
498
499 if ( $is_quoted )
500 return strtr( substr ( $value, 1, - 1 ), array ( '\\"' => '"', '\'\'' => '\'', '\\\'' => '\'' ) );
501
502 if ( strpos( $value, ' #' ) !== false )
503 $value = preg_replace( '/\s+#(.+)$/', '', $value );
504
505 if ( $first_character == '[' && $last_character == ']' ) {
506 // Take out strings sequences and mappings
507 $innerValue = trim( substr ( $value, 1, - 1 ) );
508 if ( $innerValue === '' ) return array();
509 $explode = $this->_inlineEscape( $innerValue );
510 // Propagate value array
511 $value = array();
512 foreach ( $explode as $v ) {
513 $value[] = $this->_toType( $v );
514 }
515 return $value;
516 }
517
518 if ( strpos( $value, ': ' ) !== false && $first_character != '{' ) {
519 $array = explode( ': ', $value );
520 $key = trim( $array[0] );
521 array_shift( $array );
522 $value = trim( implode( ': ', $array ) );
523 $value = $this->_toType( $value );
524 return array( $key => $value );
525 }
526
527 if ( $first_character == '{' && $last_character == '}' ) {
528 $innerValue = trim( substr ( $value, 1, - 1 ) );
529 if ( $innerValue === '' ) return array();
530 // Inline Mapping
531 // Take out strings sequences and mappings
532 $explode = $this->_inlineEscape( $innerValue );
533 // Propagate value array
534 $array = array();
535 foreach ( $explode as $v ) {
536 $SubArr = $this->_toType( $v );
537 if ( empty( $SubArr ) ) continue;
538 if ( is_array ( $SubArr ) ) {
539 $array[key( $SubArr )] = $SubArr[key( $SubArr )]; continue;
540 }
541 $array[] = $SubArr;
542 }
543 return $array;
544 }
545
546 if ( $value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~' ) {
547 return null;
548 }
549
550 if ( intval( $first_character ) > 0 && preg_match ( '/^[1-9]+[0-9]*$/', $value ) ) {
551 $intvalue = (int)$value;
552 if ( $intvalue != PHP_INT_MAX )
553 $value = $intvalue;
554 return $value;
555 }
556
557 if ( in_array( $value,
558 array( 'true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y' ) ) ) {
559 return true;
560 }
561
562 if ( in_array( strtolower( $value ),
563 array( 'false', 'off', '-', 'no', 'n' ) ) ) {
564 return false;
565 }
566
567 if ( is_numeric( $value ) ) {
568 if ( $value === '0' ) return 0;
569 if ( trim ( $value, 0 ) === $value )
570 $value = (float)$value;
571 return $value;
572 }
573
574 return $value;
575 }
576
577 /**
578 * Used in inlines to check for more inlines or quoted strings
579 * @access private
580 * @return array
581 */
582 private function _inlineEscape( $inline ) {
583 // There's gotta be a cleaner way to do this...
584 // While pure sequences seem to be nesting just fine,
585 // pure mappings and mappings with sequences inside can't go very
586 // deep. This needs to be fixed.
587
588 $seqs = array();
589 $maps = array();
590 $saved_strings = array();
591
592 // Check for strings
593 $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
594 if ( preg_match_all( $regex, $inline, $strings ) ) {
595 $saved_strings = $strings[0];
596 $inline = preg_replace( $regex, 'YAMLString', $inline );
597 }
598 unset( $regex );
599
600 $i = 0;
601 do {
602
603 // Check for sequences
604 while ( preg_match( '/\[([^{}\[\]]+)\]/U', $inline, $matchseqs ) ) {
605 $seqs[] = $matchseqs[0];
606 $inline = preg_replace( '/\[([^{}\[\]]+)\]/U', ( 'YAMLSeq' . ( count( $seqs ) - 1 ) . 's' ), $inline, 1 );
607 }
608
609 // Check for mappings
610 while ( preg_match( '/{([^\[\]{}]+)}/U', $inline, $matchmaps ) ) {
611 $maps[] = $matchmaps[0];
612 $inline = preg_replace( '/{([^\[\]{}]+)}/U', ( 'YAMLMap' . ( count( $maps ) - 1 ) . 's' ), $inline, 1 );
613 }
614
615 if ( $i++ >= 10 ) break;
616
617 } while ( strpos ( $inline, '[' ) !== false || strpos ( $inline, '{' ) !== false );
618
619 $explode = explode( ', ', $inline );
620 $stringi = 0; $i = 0;
621
622 while ( 1 ) {
623
624 // Re-add the sequences
625 if ( !empty( $seqs ) ) {
626 foreach ( $explode as $key => $value ) {
627 if ( strpos( $value, 'YAMLSeq' ) !== false ) {
628 foreach ( $seqs as $seqk => $seq ) {
629 $explode[$key] = str_replace( ( 'YAMLSeq' . $seqk . 's' ), $seq, $value );
630 $value = $explode[$key];
631 }
632 }
633 }
634 }
635
636 // Re-add the mappings
637 if ( !empty( $maps ) ) {
638 foreach ( $explode as $key => $value ) {
639 if ( strpos( $value, 'YAMLMap' ) !== false ) {
640 foreach ( $maps as $mapk => $map ) {
641 $explode[$key] = str_replace( ( 'YAMLMap' . $mapk . 's' ), $map, $value );
642 $value = $explode[$key];
643 }
644 }
645 }
646 }
647
648
649 // Re-add the strings
650 if ( !empty( $saved_strings ) ) {
651 foreach ( $explode as $key => $value ) {
652 while ( strpos( $value, 'YAMLString' ) !== false ) {
653 $explode[$key] = preg_replace( '/YAMLString/', $saved_strings[$stringi], $value, 1 );
654 unset( $saved_strings[$stringi] );
655 ++$stringi;
656 $value = $explode[$key];
657 }
658 }
659 }
660
661 $finished = true;
662 foreach ( $explode as $key => $value ) {
663 if ( strpos( $value, 'YAMLSeq' ) !== false ) {
664 $finished = false; break;
665 }
666 if ( strpos( $value, 'YAMLMap' ) !== false ) {
667 $finished = false; break;
668 }
669 if ( strpos( $value, 'YAMLString' ) !== false ) {
670 $finished = false; break;
671 }
672 }
673 if ( $finished ) break;
674
675 $i++;
676 if ( $i > 10 )
677 break; // Prevent infinite loops.
678 }
679
680 return $explode;
681 }
682
683 private function literalBlockContinues ( $line, $lineIndent ) {
684 if ( !trim( $line ) ) return true;
685 if ( strlen( $line ) - strlen( ltrim( $line ) ) > $lineIndent ) return true;
686 return false;
687 }
688
689 private function referenceContentsByAlias ( $alias ) {
690 do {
691 if ( !isset( $this->SavedGroups[$alias] ) ) { echo "Bad group name: $alias."; break; }
692 $groupPath = $this->SavedGroups[$alias];
693 $value = $this->result;
694 foreach ( $groupPath as $k ) {
695 $value = $value[$k];
696 }
697 } while ( false );
698 return $value;
699 }
700
701 private function addArrayInline ( $array, $indent ) {
702 $CommonGroupPath = $this->path;
703 if ( empty ( $array ) ) return false;
704
705 foreach ( $array as $k => $_ ) {
706 $this->addArray( array( $k => $_ ), $indent );
707 $this->path = $CommonGroupPath;
708 }
709 return true;
710 }
711
712 private function addArray ( $incoming_data, $incoming_indent ) {
713
714 // print_r ($incoming_data);
715
716 if ( count ( $incoming_data ) > 1 )
717 return $this->addArrayInline ( $incoming_data, $incoming_indent );
718
719 $key = key ( $incoming_data );
720 $value = isset( $incoming_data[$key] ) ? $incoming_data[$key] : null;
721 if ( $key === '__!YAMLZero' ) $key = '0';
722
723 if ( $incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor ) { // Shortcut for root-level values.
724 if ( $key || $key === '' || $key === '0' ) {
725 $this->result[$key] = $value;
726 } else {
727 $this->result[] = $value; end ( $this->result ); $key = key ( $this->result );
728 }
729 $this->path[$incoming_indent] = $key;
730 return;
731 }
732
733
734
735 $history = array();
736 // Unfolding inner array tree.
737 $history[] = $_arr = $this->result;
738 foreach ( $this->path as $k ) {
739 $history[] = $_arr = $_arr[$k];
740 }
741
742 if ( $this->_containsGroupAlias ) {
743 $value = $this->referenceContentsByAlias( $this->_containsGroupAlias );
744 $this->_containsGroupAlias = false;
745 }
746
747
748 // Adding string or numeric key to the innermost level or $this->arr.
749 if ( is_string( $key ) && $key == '<<' ) {
750 if ( !is_array ( $_arr ) ) { $_arr = array (); }
751
752 $_arr = array_merge ( $_arr, $value );
753 } else if ( $key || $key === '' || $key === '0' ) {
754 $_arr[$key] = $value;
755 } else {
756 if ( !is_array ( $_arr ) ) { $_arr = array ( $value ); $key = 0; }
757 else { $_arr[] = $value; end ( $_arr ); $key = key ( $_arr ); }
758 }
759
760 $reverse_path = array_reverse( $this->path );
761 $reverse_history = array_reverse ( $history );
762 $reverse_history[0] = $_arr;
763 $cnt = count( $reverse_history ) - 1;
764 for ( $i = 0; $i < $cnt; $i++ ) {
765 $reverse_history[$i + 1][$reverse_path[$i]] = $reverse_history[$i];
766 }
767 $this->result = $reverse_history[$cnt];
768
769 $this->path[$incoming_indent] = $key;
770
771 if ( $this->_containsGroupAnchor ) {
772 $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
773 if ( is_array ( $value ) ) {
774 $k = key ( $value );
775 if ( !is_int ( $k ) ) {
776 $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
777 }
778 }
779 $this->_containsGroupAnchor = false;
780 }
781
782 }
783
784 private static function startsLiteralBlock ( $line ) {
785 $lastChar = substr ( trim( $line ), - 1 );
786 if ( $lastChar != '>' && $lastChar != '|' ) return false;
787 if ( $lastChar == '|' ) return $lastChar;
788 // HTML tags should not be counted as literal blocks.
789 if ( preg_match ( '#<.*?>$#', $line ) ) return false;
790 return $lastChar;
791 }
792
793 private static function greedilyNeedNextLine( $line ) {
794 $line = trim ( $line );
795 if ( !strlen( $line ) ) return false;
796 if ( substr ( $line, - 1, 1 ) == ']' ) return false;
797 if ( $line[0] == '[' ) return true;
798 if ( preg_match ( '#^[^:]+?:\s*\[#', $line ) ) return true;
799 return false;
800 }
801
802 private function addLiteralLine ( $literalBlock, $line, $literalBlockStyle ) {
803 $line = self::stripIndent( $line );
804 $line = rtrim ( $line, "\r\n\t " ) . "\n";
805 if ( $literalBlockStyle == '|' ) {
806 return $literalBlock . $line;
807 }
808 if ( strlen( $line ) == 0 )
809 return rtrim( $literalBlock, ' ' ) . "\n";
810 if ( $line == "\n" && $literalBlockStyle == '>' ) {
811 return rtrim ( $literalBlock, " \t" ) . "\n";
812 }
813 if ( $line != "\n" )
814 $line = trim ( $line, "\r\n " ) . " ";
815 return $literalBlock . $line;
816 }
817
818 function revertLiteralPlaceHolder ( $lineArray, $literalBlock ) {
819 foreach ( $lineArray as $k => $_ ) {
820 if ( is_array( $_ ) )
821 $lineArray[$k] = $this->revertLiteralPlaceHolder ( $_, $literalBlock );
822 else if ( substr( $_, - 1 * strlen ( $this->LiteralPlaceHolder ) ) == $this->LiteralPlaceHolder )
823 $lineArray[$k] = rtrim ( $literalBlock, " \r\n" );
824 }
825 return $lineArray;
826 }
827
828 private static function stripIndent ( $line, $indent = - 1 ) {
829 if ( $indent == - 1 ) $indent = strlen( $line ) - strlen( ltrim( $line ) );
830 return substr ( $line, $indent );
831 }
832
833 private function getParentPathByIndent ( $indent ) {
834 if ( $indent == 0 ) return array();
835 $linePath = $this->path;
836 do {
837 end( $linePath ); $lastIndentInParentPath = key( $linePath );
838 if ( $indent <= $lastIndentInParentPath ) array_pop ( $linePath );
839 } while ( $indent <= $lastIndentInParentPath );
840 return $linePath;
841 }
842
843
844 private function clearBiggerPathValues ( $indent ) {
845
846
847 if ( $indent == 0 ) $this->path = array();
848 if ( empty ( $this->path ) ) return true;
849
850 foreach ( $this->path as $k => $_ ) {
851 if ( $k > $indent ) unset ( $this->path[$k] );
852 }
853
854 return true;
855 }
856
857
858 private static function isComment ( $line ) {
859 if ( !$line ) return false;
860 if ( $line[0] == '#' ) return true;
861 if ( trim( $line, " \r\n\t" ) == '---' ) return true;
862 return false;
863 }
864
865 private static function isEmpty ( $line ) {
866 return ( trim ( $line ) === '' );
867 }
868
869
870 private function isArrayElement ( $line ) {
871 if ( !$line ) return false;
872 if ( $line[0] != '-' ) return false;
873 if ( strlen ( $line ) > 3 )
874 if ( substr( $line, 0, 3 ) == '---' ) return false;
875
876 return true;
877 }
878
879 private function isHashElement ( $line ) {
880 return strpos( $line, ':' );
881 }
882
883 private function isLiteral ( $line ) {
884 if ( $this->isArrayElement( $line ) ) return false;
885 if ( $this->isHashElement( $line ) ) return false;
886 return true;
887 }
888
889
890 private static function unquote ( $value ) {
891 if ( !$value ) return $value;
892 if ( !is_string( $value ) ) return $value;
893 if ( $value[0] == '\'' ) return trim ( $value, '\'' );
894 if ( $value[0] == '"' ) return trim ( $value, '"' );
895 return $value;
896 }
897
898 private function startsMappedSequence ( $line ) {
899 return ( $line[0] == '-' && substr ( $line, - 1, 1 ) == ':' );
900 }
901
902 private function returnMappedSequence ( $line ) {
903 $array = array();
904 $key = self::unquote( trim( substr( $line, 1, - 1 ) ) );
905 $array[$key] = array();
906 $this->delayedPath = array( strpos ( $line, $key ) + $this->indent => $key );
907 return array( $array );
908 }
909
910 private function returnMappedValue ( $line ) {
911 $array = array();
912 $key = self::unquote ( trim( substr( $line, 0, - 1 ) ) );
913 $array[$key] = '';
914 return $array;
915 }
916
917 private function startsMappedValue ( $line ) {
918 return ( substr ( $line, - 1, 1 ) == ':' );
919 }
920
921 private function isPlainArray ( $line ) {
922 return ( $line[0] == '[' && substr ( $line, - 1, 1 ) == ']' );
923 }
924
925 private function returnPlainArray ( $line ) {
926 return $this->_toType( $line );
927 }
928
929 private function returnKeyValuePair ( $line ) {
930 $array = array();
931 $key = '';
932 if ( strpos ( $line, ':' ) ) {
933 // It's a key/value pair most likely
934 // If the key is in double quotes pull it out
935 if ( ( $line[0] == '"' || $line[0] == "'" ) && preg_match( '/^(["\'](.*)["\'](\s)*:)/', $line, $matches ) ) {
936 $value = trim( str_replace( $matches[1], '', $line ) );
937 $key = $matches[2];
938 } else {
939 // Do some guesswork as to the key and the value
940 $explode = explode( ':', $line );
941 $key = trim( $explode[0] );
942 array_shift( $explode );
943 $value = trim( implode( ':', $explode ) );
944 }
945 // Set the type of the value. Int, string, etc
946 $value = $this->_toType( $value );
947 if ( $key === '0' ) $key = '__!YAMLZero';
948 $array[$key] = $value;
949 } else {
950 $array = array ( $line );
951 }
952 return $array;
953
954 }
955
956
957 private function returnArrayElement ( $line ) {
958 if ( strlen( $line ) <= 1 ) return array( array() ); // Weird %)
959 $array = array();
960 $value = trim( substr( $line, 1 ) );
961 $value = $this->_toType( $value );
962 $array[] = $value;
963 return $array;
964 }
965
966
967 private function nodeContainsGroup ( $line ) {
968 $symbolsForReference = 'A-z0-9_\-';
969 if ( strpos( $line, '&' ) === false && strpos( $line, '*' ) === false ) return false; // Please die fast ;-)
970 if ( $line[0] == '&' && preg_match( '/^(&[' . $symbolsForReference . ']+)/', $line, $matches ) ) return $matches[1];
971 if ( $line[0] == '*' && preg_match( '/^(\*[' . $symbolsForReference . ']+)/', $line, $matches ) ) return $matches[1];
972 if ( preg_match( '/(&[' . $symbolsForReference . ']+)$/', $line, $matches ) ) return $matches[1];
973 if ( preg_match( '/(\*[' . $symbolsForReference . ']+$)/', $line, $matches ) ) return $matches[1];
974 if ( preg_match ( '#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches ) ) return $matches[1];
975 return false;
976
977 }
978
979 private function addGroup ( $line, $group ) {
980 if ( $group[0] == '&' ) $this->_containsGroupAnchor = substr ( $group, 1 );
981 if ( $group[0] == '*' ) $this->_containsGroupAlias = substr ( $group, 1 );
982 // print_r ($this->path);
983 }
984
985 private function stripGroup ( $line, $group ) {
986 $line = trim( str_replace( $group, '', $line ) );
987 return $line;
988 }
989 }