Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / symfony / yaml / Tests / ParserTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Yaml\Tests;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Yaml\Exception\ParseException;
16 use Symfony\Component\Yaml\Yaml;
17 use Symfony\Component\Yaml\Parser;
18 use Symfony\Component\Yaml\Tag\TaggedValue;
19
20 class ParserTest extends TestCase
21 {
22     /** @var Parser */
23     protected $parser;
24
25     protected function setUp()
26     {
27         $this->parser = new Parser();
28     }
29
30     protected function tearDown()
31     {
32         $this->parser = null;
33
34         chmod(__DIR__.'/Fixtures/not_readable.yml', 0644);
35     }
36
37     /**
38      * @dataProvider getDataFormSpecifications
39      */
40     public function testSpecifications($expected, $yaml, $comment, $deprecated)
41     {
42         $deprecations = array();
43
44         if ($deprecated) {
45             set_error_handler(function ($type, $msg) use (&$deprecations) {
46                 if (E_USER_DEPRECATED !== $type) {
47                     restore_error_handler();
48
49                     if (class_exists('PHPUnit_Util_ErrorHandler')) {
50                         return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args());
51                     }
52
53                     return call_user_func_array('PHPUnit\Util\ErrorHandler::handleError', func_get_args());
54                 }
55
56                 $deprecations[] = $msg;
57             });
58         }
59
60         $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
61
62         if ($deprecated) {
63             restore_error_handler();
64
65             $this->assertCount(1, $deprecations);
66             $this->assertContains(true !== $deprecated ? $deprecated : 'Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0 on line 1.', $deprecations[0]);
67         }
68     }
69
70     public function getDataFormSpecifications()
71     {
72         return $this->loadTestsFromFixtureFiles('index.yml');
73     }
74
75     /**
76      * @group legacy
77      * @expectedDeprecationMessage Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable
78      * @dataProvider getNonStringMappingKeysData
79      */
80     public function testNonStringMappingKeys($expected, $yaml, $comment)
81     {
82         $this->assertSame($expected, var_export($this->parser->parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS), true), $comment);
83     }
84
85     public function getNonStringMappingKeysData()
86     {
87         return $this->loadTestsFromFixtureFiles('nonStringKeys.yml');
88     }
89
90     /**
91      * @group legacy
92      * @dataProvider getLegacyNonStringMappingKeysData
93      */
94     public function testLegacyNonStringMappingKeys($expected, $yaml, $comment)
95     {
96         $this->assertSame($expected, var_export($this->parser->parse($yaml), true), $comment);
97     }
98
99     public function getLegacyNonStringMappingKeysData()
100     {
101         return $this->loadTestsFromFixtureFiles('legacyNonStringKeys.yml');
102     }
103
104     public function testTabsInYaml()
105     {
106         // test tabs in YAML
107         $yamls = array(
108             "foo:\n     bar",
109             "foo:\n     bar",
110             "foo:\n      bar",
111             "foo:\n      bar",
112         );
113
114         foreach ($yamls as $yaml) {
115             try {
116                 $content = $this->parser->parse($yaml);
117
118                 $this->fail('YAML files must not contain tabs');
119             } catch (\Exception $e) {
120                 $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs');
121                 $this->assertEquals('A YAML file cannot contain tabs as indentation at line 2 (near "'.strpbrk($yaml, "\t").'").', $e->getMessage(), 'YAML files must not contain tabs');
122             }
123         }
124     }
125
126     public function testEndOfTheDocumentMarker()
127     {
128         $yaml = <<<'EOF'
129 --- %YAML:1.0
130 foo
131 ...
132 EOF;
133
134         $this->assertEquals('foo', $this->parser->parse($yaml));
135     }
136
137     public function getBlockChompingTests()
138     {
139         $tests = array();
140
141         $yaml = <<<'EOF'
142 foo: |-
143     one
144     two
145 bar: |-
146     one
147     two
148
149 EOF;
150         $expected = array(
151             'foo' => "one\ntwo",
152             'bar' => "one\ntwo",
153         );
154         $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml);
155
156         $yaml = <<<'EOF'
157 foo: |-
158     one
159     two
160
161 bar: |-
162     one
163     two
164
165
166 EOF;
167         $expected = array(
168             'foo' => "one\ntwo",
169             'bar' => "one\ntwo",
170         );
171         $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
172
173         $yaml = <<<'EOF'
174 {}
175
176
177 EOF;
178         $expected = array();
179         $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = array($expected, $yaml);
180
181         $yaml = <<<'EOF'
182 foo: |-
183     one
184     two
185 bar: |-
186     one
187     two
188 EOF;
189         $expected = array(
190             'foo' => "one\ntwo",
191             'bar' => "one\ntwo",
192         );
193         $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml);
194
195         $yaml = <<<'EOF'
196 foo: |
197     one
198     two
199 bar: |
200     one
201     two
202
203 EOF;
204         $expected = array(
205             'foo' => "one\ntwo\n",
206             'bar' => "one\ntwo\n",
207         );
208         $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml);
209
210         $yaml = <<<'EOF'
211 foo: |
212     one
213     two
214
215 bar: |
216     one
217     two
218
219
220 EOF;
221         $expected = array(
222             'foo' => "one\ntwo\n",
223             'bar' => "one\ntwo\n",
224         );
225         $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
226
227         $yaml = <<<'EOF'
228 foo:
229 - bar: |
230     one
231
232     two
233 EOF;
234         $expected = array(
235             'foo' => array(
236                 array(
237                     'bar' => "one\n\ntwo",
238                 ),
239             ),
240         );
241         $tests['Literal block chomping clip with embedded blank line inside unindented collection'] = array($expected, $yaml);
242
243         $yaml = <<<'EOF'
244 foo: |
245     one
246     two
247 bar: |
248     one
249     two
250 EOF;
251         $expected = array(
252             'foo' => "one\ntwo\n",
253             'bar' => "one\ntwo",
254         );
255         $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml);
256
257         $yaml = <<<'EOF'
258 foo: |+
259     one
260     two
261 bar: |+
262     one
263     two
264
265 EOF;
266         $expected = array(
267             'foo' => "one\ntwo\n",
268             'bar' => "one\ntwo\n",
269         );
270         $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml);
271
272         $yaml = <<<'EOF'
273 foo: |+
274     one
275     two
276
277 bar: |+
278     one
279     two
280
281
282 EOF;
283         $expected = array(
284             'foo' => "one\ntwo\n\n",
285             'bar' => "one\ntwo\n\n",
286         );
287         $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
288
289         $yaml = <<<'EOF'
290 foo: |+
291     one
292     two
293 bar: |+
294     one
295     two
296 EOF;
297         $expected = array(
298             'foo' => "one\ntwo\n",
299             'bar' => "one\ntwo",
300         );
301         $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml);
302
303         $yaml = <<<'EOF'
304 foo: >-
305     one
306     two
307 bar: >-
308     one
309     two
310
311 EOF;
312         $expected = array(
313             'foo' => 'one two',
314             'bar' => 'one two',
315         );
316         $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml);
317
318         $yaml = <<<'EOF'
319 foo: >-
320     one
321     two
322
323 bar: >-
324     one
325     two
326
327
328 EOF;
329         $expected = array(
330             'foo' => 'one two',
331             'bar' => 'one two',
332         );
333         $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
334
335         $yaml = <<<'EOF'
336 foo: >-
337     one
338     two
339 bar: >-
340     one
341     two
342 EOF;
343         $expected = array(
344             'foo' => 'one two',
345             'bar' => 'one two',
346         );
347         $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml);
348
349         $yaml = <<<'EOF'
350 foo: >
351     one
352     two
353 bar: >
354     one
355     two
356
357 EOF;
358         $expected = array(
359             'foo' => "one two\n",
360             'bar' => "one two\n",
361         );
362         $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml);
363
364         $yaml = <<<'EOF'
365 foo: >
366     one
367     two
368
369 bar: >
370     one
371     two
372
373
374 EOF;
375         $expected = array(
376             'foo' => "one two\n",
377             'bar' => "one two\n",
378         );
379         $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
380
381         $yaml = <<<'EOF'
382 foo: >
383     one
384     two
385 bar: >
386     one
387     two
388 EOF;
389         $expected = array(
390             'foo' => "one two\n",
391             'bar' => 'one two',
392         );
393         $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml);
394
395         $yaml = <<<'EOF'
396 foo: >+
397     one
398     two
399 bar: >+
400     one
401     two
402
403 EOF;
404         $expected = array(
405             'foo' => "one two\n",
406             'bar' => "one two\n",
407         );
408         $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml);
409
410         $yaml = <<<'EOF'
411 foo: >+
412     one
413     two
414
415 bar: >+
416     one
417     two
418
419
420 EOF;
421         $expected = array(
422             'foo' => "one two\n\n",
423             'bar' => "one two\n\n",
424         );
425         $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
426
427         $yaml = <<<'EOF'
428 foo: >+
429     one
430     two
431 bar: >+
432     one
433     two
434 EOF;
435         $expected = array(
436             'foo' => "one two\n",
437             'bar' => 'one two',
438         );
439         $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);
440
441         return $tests;
442     }
443
444     /**
445      * @dataProvider getBlockChompingTests
446      */
447     public function testBlockChomping($expected, $yaml)
448     {
449         $this->assertSame($expected, $this->parser->parse($yaml));
450     }
451
452     /**
453      * Regression test for issue #7989.
454      *
455      * @see https://github.com/symfony/symfony/issues/7989
456      */
457     public function testBlockLiteralWithLeadingNewlines()
458     {
459         $yaml = <<<'EOF'
460 foo: |-
461
462
463     bar
464
465 EOF;
466         $expected = array(
467             'foo' => "\n\nbar",
468         );
469
470         $this->assertSame($expected, $this->parser->parse($yaml));
471     }
472
473     public function testObjectSupportEnabled()
474     {
475         $input = <<<'EOF'
476 foo: !php/object O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
477 bar: 1
478 EOF;
479         $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
480     }
481
482     /**
483      * @group legacy
484      */
485     public function testObjectSupportEnabledPassingTrue()
486     {
487         $input = <<<'EOF'
488 foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
489 bar: 1
490 EOF;
491         $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
492     }
493
494     /**
495      * @group legacy
496      * @dataProvider deprecatedObjectValueProvider
497      */
498     public function testObjectSupportEnabledWithDeprecatedTag($yaml)
499     {
500         $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($yaml, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
501     }
502
503     public function deprecatedObjectValueProvider()
504     {
505         return array(
506             array(
507                 <<<YAML
508 foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
509 bar: 1
510 YAML
511             ),
512             array(
513                 <<<YAML
514 foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
515 bar: 1
516 YAML
517             ),
518         );
519     }
520
521     /**
522      * @dataProvider invalidDumpedObjectProvider
523      */
524     public function testObjectSupportDisabledButNoExceptions($input)
525     {
526         $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
527     }
528
529     /**
530      * @dataProvider getObjectForMapTests
531      */
532     public function testObjectForMap($yaml, $expected)
533     {
534         $flags = Yaml::PARSE_OBJECT_FOR_MAP;
535
536         $this->assertEquals($expected, $this->parser->parse($yaml, $flags));
537     }
538
539     /**
540      * @group legacy
541      * @dataProvider getObjectForMapTests
542      */
543     public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected)
544     {
545         $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
546     }
547
548     public function getObjectForMapTests()
549     {
550         $tests = array();
551
552         $yaml = <<<'EOF'
553 foo:
554     fiz: [cat]
555 EOF;
556         $expected = new \stdClass();
557         $expected->foo = new \stdClass();
558         $expected->foo->fiz = array('cat');
559         $tests['mapping'] = array($yaml, $expected);
560
561         $yaml = '{ "foo": "bar", "fiz": "cat" }';
562         $expected = new \stdClass();
563         $expected->foo = 'bar';
564         $expected->fiz = 'cat';
565         $tests['inline-mapping'] = array($yaml, $expected);
566
567         $yaml = "foo: bar\nbaz: foobar";
568         $expected = new \stdClass();
569         $expected->foo = 'bar';
570         $expected->baz = 'foobar';
571         $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected);
572
573         $yaml = <<<'EOT'
574 array:
575   - key: one
576   - key: two
577 EOT;
578         $expected = new \stdClass();
579         $expected->array = array();
580         $expected->array[0] = new \stdClass();
581         $expected->array[0]->key = 'one';
582         $expected->array[1] = new \stdClass();
583         $expected->array[1]->key = 'two';
584         $tests['nest-map-and-sequence'] = array($yaml, $expected);
585
586         $yaml = <<<'YAML'
587 map:
588   1: one
589   2: two
590 YAML;
591         $expected = new \stdClass();
592         $expected->map = new \stdClass();
593         $expected->map->{1} = 'one';
594         $expected->map->{2} = 'two';
595         $tests['numeric-keys'] = array($yaml, $expected);
596
597         $yaml = <<<'YAML'
598 map:
599   '0': one
600   '1': two
601 YAML;
602         $expected = new \stdClass();
603         $expected->map = new \stdClass();
604         $expected->map->{0} = 'one';
605         $expected->map->{1} = 'two';
606         $tests['zero-indexed-numeric-keys'] = array($yaml, $expected);
607
608         return $tests;
609     }
610
611     /**
612      * @dataProvider invalidDumpedObjectProvider
613      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
614      */
615     public function testObjectsSupportDisabledWithExceptions($yaml)
616     {
617         $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
618     }
619
620     public function testCanParseContentWithTrailingSpaces()
621     {
622         $yaml = "items:  \n  foo: bar";
623
624         $expected = array(
625             'items' => array('foo' => 'bar'),
626         );
627
628         $this->assertSame($expected, $this->parser->parse($yaml));
629     }
630
631     /**
632      * @group legacy
633      * @dataProvider invalidDumpedObjectProvider
634      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
635      */
636     public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml)
637     {
638         $this->parser->parse($yaml, true);
639     }
640
641     public function invalidDumpedObjectProvider()
642     {
643         $yamlTag = <<<'EOF'
644 foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
645 bar: 1
646 EOF;
647         $localTag = <<<'EOF'
648 foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
649 bar: 1
650 EOF;
651
652         return array(
653             'yaml-tag' => array($yamlTag),
654             'local-tag' => array($localTag),
655         );
656     }
657
658     /**
659      * @requires extension iconv
660      */
661     public function testNonUtf8Exception()
662     {
663         $yamls = array(
664             iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"),
665             iconv('UTF-8', 'ISO-8859-15', "euro: '€'"),
666             iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"),
667         );
668
669         foreach ($yamls as $yaml) {
670             try {
671                 $this->parser->parse($yaml);
672
673                 $this->fail('charsets other than UTF-8 are rejected.');
674             } catch (\Exception $e) {
675                 $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
676             }
677         }
678     }
679
680     /**
681      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
682      */
683     public function testUnindentedCollectionException()
684     {
685         $yaml = <<<'EOF'
686
687 collection:
688 -item1
689 -item2
690 -item3
691
692 EOF;
693
694         $this->parser->parse($yaml);
695     }
696
697     /**
698      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
699      */
700     public function testShortcutKeyUnindentedCollectionException()
701     {
702         $yaml = <<<'EOF'
703
704 collection:
705 -  key: foo
706   foo: bar
707
708 EOF;
709
710         $this->parser->parse($yaml);
711     }
712
713     /**
714      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
715      * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/
716      */
717     public function testMultipleDocumentsNotSupportedException()
718     {
719         Yaml::parse(<<<'EOL'
720 # Ranking of 1998 home runs
721 ---
722 - Mark McGwire
723 - Sammy Sosa
724 - Ken Griffey
725
726 # Team ranking
727 ---
728 - Chicago Cubs
729 - St Louis Cardinals
730 EOL
731         );
732     }
733
734     /**
735      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
736      */
737     public function testSequenceInAMapping()
738     {
739         Yaml::parse(<<<'EOF'
740 yaml:
741   hash: me
742   - array stuff
743 EOF
744         );
745     }
746
747     public function testSequenceInMappingStartedBySingleDashLine()
748     {
749         $yaml = <<<'EOT'
750 a:
751 -
752   b:
753   -
754     bar: baz
755 - foo
756 d: e
757 EOT;
758         $expected = array(
759             'a' => array(
760                 array(
761                     'b' => array(
762                         array(
763                             'bar' => 'baz',
764                         ),
765                     ),
766                 ),
767                 'foo',
768             ),
769             'd' => 'e',
770         );
771
772         $this->assertSame($expected, $this->parser->parse($yaml));
773     }
774
775     public function testSequenceFollowedByCommentEmbeddedInMapping()
776     {
777         $yaml = <<<'EOT'
778 a:
779     b:
780         - c
781 # comment
782     d: e
783 EOT;
784         $expected = array(
785             'a' => array(
786                 'b' => array('c'),
787                 'd' => 'e',
788             ),
789         );
790
791         $this->assertSame($expected, $this->parser->parse($yaml));
792     }
793
794     public function testNonStringFollowedByCommentEmbeddedInMapping()
795     {
796         $yaml = <<<'EOT'
797 a:
798     b:
799         {}
800 # comment
801     d:
802         1.1
803 # another comment
804 EOT;
805         $expected = array(
806             'a' => array(
807                 'b' => array(),
808                 'd' => 1.1,
809             ),
810         );
811
812         $this->assertSame($expected, $this->parser->parse($yaml));
813     }
814
815     public function getParseExceptionNotAffectedMultiLineStringLastResortParsing()
816     {
817         $tests = array();
818
819         $yaml = <<<'EOT'
820 a
821     b:
822 EOT;
823         $tests['parse error on first line'] = array($yaml);
824
825         $yaml = <<<'EOT'
826 a
827
828 b
829     c:
830 EOT;
831         $tests['parse error due to inconsistent indentation'] = array($yaml);
832
833         $yaml = <<<'EOT'
834  &  *  !  |  >  '  "  %  @  ` #, { asd a;sdasd }-@^qw3
835 EOT;
836         $tests['symfony/symfony/issues/22967#issuecomment-322067742'] = array($yaml);
837
838         return $tests;
839     }
840
841     /**
842      * @dataProvider getParseExceptionNotAffectedMultiLineStringLastResortParsing
843      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
844      */
845     public function testParseExceptionNotAffectedByMultiLineStringLastResortParsing($yaml)
846     {
847         $this->parser->parse($yaml);
848     }
849
850     public function testMultiLineStringLastResortParsing()
851     {
852         $yaml = <<<'EOT'
853 test:
854   You can have things that don't look like strings here
855   true
856   yes you can
857 EOT;
858         $expected = array(
859             'test' => 'You can have things that don\'t look like strings here true yes you can',
860         );
861
862         $this->assertSame($expected, $this->parser->parse($yaml));
863
864         $yaml = <<<'EOT'
865 a:
866     b
867        c
868 EOT;
869         $expected = array(
870             'a' => 'b c',
871         );
872
873         $this->assertSame($expected, $this->parser->parse($yaml));
874     }
875
876     /**
877      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
878      */
879     public function testMappingInASequence()
880     {
881         Yaml::parse(<<<'EOF'
882 yaml:
883   - array stuff
884   hash: me
885 EOF
886         );
887     }
888
889     /**
890      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
891      * @expectedExceptionMessage missing colon
892      */
893     public function testScalarInSequence()
894     {
895         Yaml::parse(<<<'EOF'
896 foo:
897     - bar
898 "missing colon"
899     foo: bar
900 EOF
901         );
902     }
903
904     /**
905      * > It is an error for two equal keys to appear in the same mapping node.
906      * > In such a case the YAML processor may continue, ignoring the second
907      * > `key: value` pair and issuing an appropriate warning. This strategy
908      * > preserves a consistent information model for one-pass and random access
909      * > applications.
910      *
911      * @see http://yaml.org/spec/1.2/spec.html#id2759572
912      * @see http://yaml.org/spec/1.1/#id932806
913      * @group legacy
914      */
915     public function testMappingDuplicateKeyBlock()
916     {
917         $input = <<<'EOD'
918 parent:
919     child: first
920     child: duplicate
921 parent:
922     child: duplicate
923     child: duplicate
924 EOD;
925         $expected = array(
926             'parent' => array(
927                 'child' => 'first',
928             ),
929         );
930         $this->assertSame($expected, Yaml::parse($input));
931     }
932
933     /**
934      * @group legacy
935      */
936     public function testMappingDuplicateKeyFlow()
937     {
938         $input = <<<'EOD'
939 parent: { child: first, child: duplicate }
940 parent: { child: duplicate, child: duplicate }
941 EOD;
942         $expected = array(
943             'parent' => array(
944                 'child' => 'first',
945             ),
946         );
947         $this->assertSame($expected, Yaml::parse($input));
948     }
949
950     /**
951      * @group legacy
952      * @dataProvider getParseExceptionOnDuplicateData
953      * @expectedDeprecation Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated %s and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.
954      * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
955      */
956     public function testParseExceptionOnDuplicate($input, $duplicateKey, $lineNumber)
957     {
958         Yaml::parse($input);
959     }
960
961     public function getParseExceptionOnDuplicateData()
962     {
963         $tests = array();
964
965         $yaml = <<<EOD
966 parent: { child: first, child: duplicate }
967 EOD;
968         $tests[] = array($yaml, 'child', 1);
969
970         $yaml = <<<EOD
971 parent:
972   child: first,
973   child: duplicate
974 EOD;
975         $tests[] = array($yaml, 'child', 3);
976
977         $yaml = <<<EOD
978 parent: { child: foo }
979 parent: { child: bar }
980 EOD;
981         $tests[] = array($yaml, 'parent', 2);
982
983         $yaml = <<<EOD
984 parent: { child_mapping: { value: bar},  child_mapping: { value: bar} }
985 EOD;
986         $tests[] = array($yaml, 'child_mapping', 1);
987
988         $yaml = <<<EOD
989 parent:
990   child_mapping:
991     value: bar
992   child_mapping:
993     value: bar
994 EOD;
995         $tests[] = array($yaml, 'child_mapping', 4);
996
997         $yaml = <<<EOD
998 parent: { child_sequence: ['key1', 'key2', 'key3'],  child_sequence: ['key1', 'key2', 'key3'] }
999 EOD;
1000         $tests[] = array($yaml, 'child_sequence', 1);
1001
1002         $yaml = <<<EOD
1003 parent:
1004   child_sequence:
1005     - key1
1006     - key2
1007     - key3
1008   child_sequence:
1009     - key1
1010     - key2
1011     - key3
1012 EOD;
1013         $tests[] = array($yaml, 'child_sequence', 6);
1014
1015         return $tests;
1016     }
1017
1018     public function testEmptyValue()
1019     {
1020         $input = <<<'EOF'
1021 hash:
1022 EOF;
1023
1024         $this->assertEquals(array('hash' => null), Yaml::parse($input));
1025     }
1026
1027     public function testCommentAtTheRootIndent()
1028     {
1029         $this->assertEquals(array(
1030             'services' => array(
1031                 'app.foo_service' => array(
1032                     'class' => 'Foo',
1033                 ),
1034                 'app/bar_service' => array(
1035                     'class' => 'Bar',
1036                 ),
1037             ),
1038         ), Yaml::parse(<<<'EOF'
1039 # comment 1
1040 services:
1041 # comment 2
1042     # comment 3
1043     app.foo_service:
1044         class: Foo
1045 # comment 4
1046     # comment 5
1047     app/bar_service:
1048         class: Bar
1049 EOF
1050         ));
1051     }
1052
1053     public function testStringBlockWithComments()
1054     {
1055         $this->assertEquals(array('content' => <<<'EOT'
1056 # comment 1
1057 header
1058
1059     # comment 2
1060     <body>
1061         <h1>title</h1>
1062     </body>
1063
1064 footer # comment3
1065 EOT
1066         ), Yaml::parse(<<<'EOF'
1067 content: |
1068     # comment 1
1069     header
1070
1071         # comment 2
1072         <body>
1073             <h1>title</h1>
1074         </body>
1075
1076     footer # comment3
1077 EOF
1078         ));
1079     }
1080
1081     public function testFoldedStringBlockWithComments()
1082     {
1083         $this->assertEquals(array(array('content' => <<<'EOT'
1084 # comment 1
1085 header
1086
1087     # comment 2
1088     <body>
1089         <h1>title</h1>
1090     </body>
1091
1092 footer # comment3
1093 EOT
1094         )), Yaml::parse(<<<'EOF'
1095 -
1096     content: |
1097         # comment 1
1098         header
1099
1100             # comment 2
1101             <body>
1102                 <h1>title</h1>
1103             </body>
1104
1105         footer # comment3
1106 EOF
1107         ));
1108     }
1109
1110     public function testNestedFoldedStringBlockWithComments()
1111     {
1112         $this->assertEquals(array(array(
1113             'title' => 'some title',
1114             'content' => <<<'EOT'
1115 # comment 1
1116 header
1117
1118     # comment 2
1119     <body>
1120         <h1>title</h1>
1121     </body>
1122
1123 footer # comment3
1124 EOT
1125         )), Yaml::parse(<<<'EOF'
1126 -
1127     title: some title
1128     content: |
1129         # comment 1
1130         header
1131
1132             # comment 2
1133             <body>
1134                 <h1>title</h1>
1135             </body>
1136
1137         footer # comment3
1138 EOF
1139         ));
1140     }
1141
1142     public function testReferenceResolvingInInlineStrings()
1143     {
1144         $this->assertEquals(array(
1145             'var' => 'var-value',
1146             'scalar' => 'var-value',
1147             'list' => array('var-value'),
1148             'list_in_list' => array(array('var-value')),
1149             'map_in_list' => array(array('key' => 'var-value')),
1150             'embedded_mapping' => array(array('key' => 'var-value')),
1151             'map' => array('key' => 'var-value'),
1152             'list_in_map' => array('key' => array('var-value')),
1153             'map_in_map' => array('foo' => array('bar' => 'var-value')),
1154         ), Yaml::parse(<<<'EOF'
1155 var:  &var var-value
1156 scalar: *var
1157 list: [ *var ]
1158 list_in_list: [[ *var ]]
1159 map_in_list: [ { key: *var } ]
1160 embedded_mapping: [ key: *var ]
1161 map: { key: *var }
1162 list_in_map: { key: [*var] }
1163 map_in_map: { foo: { bar: *var } }
1164 EOF
1165         ));
1166     }
1167
1168     public function testYamlDirective()
1169     {
1170         $yaml = <<<'EOF'
1171 %YAML 1.2
1172 ---
1173 foo: 1
1174 bar: 2
1175 EOF;
1176         $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml));
1177     }
1178
1179     /**
1180      * @group legacy
1181      * @expectedDeprecation Implicit casting of numeric key to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 2.
1182      */
1183     public function testFloatKeys()
1184     {
1185         $yaml = <<<'EOF'
1186 foo:
1187     1.2: "bar"
1188     1.3: "baz"
1189 EOF;
1190
1191         $expected = array(
1192             'foo' => array(
1193                 '1.2' => 'bar',
1194                 '1.3' => 'baz',
1195             ),
1196         );
1197
1198         $this->assertEquals($expected, $this->parser->parse($yaml));
1199     }
1200
1201     /**
1202      * @group legacy
1203      * @expectedDeprecation Implicit casting of non-string key to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1.
1204      */
1205     public function testBooleanKeys()
1206     {
1207         $yaml = <<<'EOF'
1208 true: foo
1209 false: bar
1210 EOF;
1211
1212         $expected = array(
1213             1 => 'foo',
1214             0 => 'bar',
1215         );
1216
1217         $this->assertEquals($expected, $this->parser->parse($yaml));
1218     }
1219
1220     public function testExplicitStringCasting()
1221     {
1222         $yaml = <<<'EOF'
1223 '1.2': "bar"
1224 !!str 1.3: "baz"
1225
1226 'true': foo
1227 !!str false: bar
1228
1229 !!str null: 'null'
1230 '~': 'null'
1231 EOF;
1232
1233         $expected = array(
1234             '1.2' => 'bar',
1235             '1.3' => 'baz',
1236             'true' => 'foo',
1237             'false' => 'bar',
1238             'null' => 'null',
1239             '~' => 'null',
1240         );
1241
1242         $this->assertEquals($expected, $this->parser->parse($yaml));
1243     }
1244
1245     /**
1246      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1247      * @expectedExceptionMessage A colon cannot be used in an unquoted mapping value
1248      */
1249     public function testColonInMappingValueException()
1250     {
1251         $yaml = <<<'EOF'
1252 foo: bar: baz
1253 EOF;
1254
1255         $this->parser->parse($yaml);
1256     }
1257
1258     public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
1259     {
1260         $yaml = <<<'EOT'
1261 foo:
1262     bar: foobar # Note: a comment after a colon
1263 EOT;
1264
1265         $this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml));
1266     }
1267
1268     /**
1269      * @dataProvider getCommentLikeStringInScalarBlockData
1270      */
1271     public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
1272     {
1273         $this->assertSame($expectedParserResult, $this->parser->parse($yaml));
1274     }
1275
1276     public function getCommentLikeStringInScalarBlockData()
1277     {
1278         $tests = array();
1279
1280         $yaml = <<<'EOT'
1281 pages:
1282     -
1283         title: some title
1284         content: |
1285             # comment 1
1286             header
1287
1288                 # comment 2
1289                 <body>
1290                     <h1>title</h1>
1291                 </body>
1292
1293             footer # comment3
1294 EOT;
1295         $expected = array(
1296             'pages' => array(
1297                 array(
1298                     'title' => 'some title',
1299                     'content' => <<<'EOT'
1300 # comment 1
1301 header
1302
1303     # comment 2
1304     <body>
1305         <h1>title</h1>
1306     </body>
1307
1308 footer # comment3
1309 EOT
1310                     ,
1311                 ),
1312             ),
1313         );
1314         $tests[] = array($yaml, $expected);
1315
1316         $yaml = <<<'EOT'
1317 test: |
1318     foo
1319     # bar
1320     baz
1321 collection:
1322     - one: |
1323         foo
1324         # bar
1325         baz
1326     - two: |
1327         foo
1328         # bar
1329         baz
1330 EOT;
1331         $expected = array(
1332             'test' => <<<'EOT'
1333 foo
1334 # bar
1335 baz
1336
1337 EOT
1338             ,
1339             'collection' => array(
1340                 array(
1341                     'one' => <<<'EOT'
1342 foo
1343 # bar
1344 baz
1345
1346 EOT
1347                     ,
1348                 ),
1349                 array(
1350                     'two' => <<<'EOT'
1351 foo
1352 # bar
1353 baz
1354 EOT
1355                     ,
1356                 ),
1357             ),
1358         );
1359         $tests[] = array($yaml, $expected);
1360
1361         $yaml = <<<'EOT'
1362 foo:
1363   bar:
1364     scalar-block: >
1365       line1
1366       line2>
1367   baz:
1368 # comment
1369     foobar: ~
1370 EOT;
1371         $expected = array(
1372             'foo' => array(
1373                 'bar' => array(
1374                     'scalar-block' => "line1 line2>\n",
1375                 ),
1376                 'baz' => array(
1377                     'foobar' => null,
1378                 ),
1379             ),
1380         );
1381         $tests[] = array($yaml, $expected);
1382
1383         $yaml = <<<'EOT'
1384 a:
1385     b: hello
1386 #    c: |
1387 #        first row
1388 #        second row
1389     d: hello
1390 EOT;
1391         $expected = array(
1392             'a' => array(
1393                 'b' => 'hello',
1394                 'd' => 'hello',
1395             ),
1396         );
1397         $tests[] = array($yaml, $expected);
1398
1399         return $tests;
1400     }
1401
1402     public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
1403     {
1404         $yaml = <<<'EOT'
1405 test: >
1406     <h2>A heading</h2>
1407
1408     <ul>
1409     <li>a list</li>
1410     <li>may be a good example</li>
1411     </ul>
1412 EOT;
1413
1414         $this->assertSame(
1415             array(
1416                 'test' => <<<'EOT'
1417 <h2>A heading</h2>
1418 <ul> <li>a list</li> <li>may be a good example</li> </ul>
1419 EOT
1420                 ,
1421             ),
1422             $this->parser->parse($yaml)
1423         );
1424     }
1425
1426     public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
1427     {
1428         $yaml = <<<'EOT'
1429 test: >
1430     <h2>A heading</h2>
1431
1432     <ul>
1433       <li>a list</li>
1434       <li>may be a good example</li>
1435     </ul>
1436 EOT;
1437
1438         $this->assertSame(
1439             array(
1440                 'test' => <<<'EOT'
1441 <h2>A heading</h2>
1442 <ul>
1443   <li>a list</li>
1444   <li>may be a good example</li>
1445 </ul>
1446 EOT
1447                 ,
1448             ),
1449             $this->parser->parse($yaml)
1450         );
1451     }
1452
1453     /**
1454      * @dataProvider getBinaryData
1455      */
1456     public function testParseBinaryData($data)
1457     {
1458         $this->assertSame(array('data' => 'Hello world'), $this->parser->parse($data));
1459     }
1460
1461     public function getBinaryData()
1462     {
1463         return array(
1464             'enclosed with double quotes' => array('data: !!binary "SGVsbG8gd29ybGQ="'),
1465             'enclosed with single quotes' => array("data: !!binary 'SGVsbG8gd29ybGQ='"),
1466             'containing spaces' => array('data: !!binary  "SGVs bG8gd 29ybGQ="'),
1467             'in block scalar' => array(
1468                 <<<'EOT'
1469 data: !!binary |
1470     SGVsbG8gd29ybGQ=
1471 EOT
1472     ),
1473             'containing spaces in block scalar' => array(
1474                 <<<'EOT'
1475 data: !!binary |
1476     SGVs bG8gd 29ybGQ=
1477 EOT
1478     ),
1479         );
1480     }
1481
1482     /**
1483      * @dataProvider getInvalidBinaryData
1484      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1485      */
1486     public function testParseInvalidBinaryData($data, $expectedMessage)
1487     {
1488         if (method_exists($this, 'expectException')) {
1489             $this->expectExceptionMessageRegExp($expectedMessage);
1490         } else {
1491             $this->setExpectedExceptionRegExp(ParseException::class, $expectedMessage);
1492         }
1493
1494         $this->parser->parse($data);
1495     }
1496
1497     public function getInvalidBinaryData()
1498     {
1499         return array(
1500             'length not a multiple of four' => array('data: !!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'),
1501             'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
1502             'too many equals characters' => array('data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
1503             'misplaced equals character' => array('data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
1504             'length not a multiple of four in block scalar' => array(
1505                 <<<'EOT'
1506 data: !!binary |
1507     SGVsbG8d29ybGQ=
1508 EOT
1509                 ,
1510                 '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/',
1511             ),
1512             'invalid characters in block scalar' => array(
1513                 <<<'EOT'
1514 data: !!binary |
1515     SGVsbG8#d29ybGQ=
1516 EOT
1517                 ,
1518                 '/The base64 encoded data \(.*\) contains invalid characters/',
1519             ),
1520             'too many equals characters in block scalar' => array(
1521                 <<<'EOT'
1522 data: !!binary |
1523     SGVsbG8gd29yb===
1524 EOT
1525                 ,
1526                 '/The base64 encoded data \(.*\) contains invalid characters/',
1527             ),
1528             'misplaced equals character in block scalar' => array(
1529                 <<<'EOT'
1530 data: !!binary |
1531     SGVsbG8gd29ybG=Q
1532 EOT
1533                 ,
1534                 '/The base64 encoded data \(.*\) contains invalid characters/',
1535             ),
1536         );
1537     }
1538
1539     public function testParseDateAsMappingValue()
1540     {
1541         $yaml = <<<'EOT'
1542 date: 2002-12-14
1543 EOT;
1544         $expectedDate = new \DateTime();
1545         $expectedDate->setTimeZone(new \DateTimeZone('UTC'));
1546         $expectedDate->setDate(2002, 12, 14);
1547         $expectedDate->setTime(0, 0, 0);
1548
1549         $this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
1550     }
1551
1552     /**
1553      * @param $lineNumber
1554      * @param $yaml
1555      * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
1556      */
1557     public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
1558     {
1559         if (method_exists($this, 'expectException')) {
1560             $this->expectException('\Symfony\Component\Yaml\Exception\ParseException');
1561             $this->expectExceptionMessage(sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
1562         } else {
1563             $this->setExpectedException('\Symfony\Component\Yaml\Exception\ParseException', sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
1564         }
1565
1566         $this->parser->parse($yaml);
1567     }
1568
1569     public function parserThrowsExceptionWithCorrectLineNumberProvider()
1570     {
1571         return array(
1572             array(
1573                 4,
1574                 <<<'YAML'
1575 foo:
1576     -
1577         # bar
1578         bar: "123",
1579 YAML
1580             ),
1581             array(
1582                 5,
1583                 <<<'YAML'
1584 foo:
1585     -
1586         # bar
1587         # bar
1588         bar: "123",
1589 YAML
1590             ),
1591             array(
1592                 8,
1593                 <<<'YAML'
1594 foo:
1595     -
1596         # foobar
1597         baz: 123
1598 bar:
1599     -
1600         # bar
1601         bar: "123",
1602 YAML
1603             ),
1604             array(
1605                 10,
1606                 <<<'YAML'
1607 foo:
1608     -
1609         # foobar
1610         # foobar
1611         baz: 123
1612 bar:
1613     -
1614         # bar
1615         # bar
1616         bar: "123",
1617 YAML
1618             ),
1619         );
1620     }
1621
1622     public function testParseMultiLineQuotedString()
1623     {
1624         $yaml = <<<EOT
1625 foo: "bar
1626   baz
1627    foobar
1628 foo"
1629 bar: baz
1630 EOT;
1631
1632         $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
1633     }
1634
1635     public function testMultiLineQuotedStringWithTrailingBackslash()
1636     {
1637         $yaml = <<<YAML
1638 foobar:
1639     "foo\
1640     bar"
1641 YAML;
1642
1643         $this->assertSame(array('foobar' => 'foobar'), $this->parser->parse($yaml));
1644     }
1645
1646     public function testCommentCharactersInMultiLineQuotedStrings()
1647     {
1648         $yaml = <<<YAML
1649 foo:
1650     foobar: 'foo
1651       #bar'
1652     bar: baz
1653 YAML;
1654         $expected = array(
1655             'foo' => array(
1656                 'foobar' => 'foo #bar',
1657                 'bar' => 'baz',
1658             ),
1659         );
1660
1661         $this->assertSame($expected, $this->parser->parse($yaml));
1662     }
1663
1664     public function testBlankLinesInQuotedMultiLineString()
1665     {
1666         $yaml = <<<YAML
1667 foobar: 'foo
1668
1669     bar'
1670 YAML;
1671         $expected = array(
1672             'foobar' => "foo\nbar",
1673         );
1674
1675         $this->assertSame($expected, $this->parser->parse($yaml));
1676     }
1677
1678     public function testParseMultiLineUnquotedString()
1679     {
1680         $yaml = <<<EOT
1681 foo: bar
1682   baz
1683    foobar
1684   foo
1685 bar: baz
1686 EOT;
1687
1688         $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
1689     }
1690
1691     public function testParseMultiLineString()
1692     {
1693         $this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
1694     }
1695
1696     /**
1697      * @dataProvider multiLineDataProvider
1698      */
1699     public function testParseMultiLineMappingValue($yaml, $expected, $parseError)
1700     {
1701         $this->assertEquals($expected, $this->parser->parse($yaml));
1702     }
1703
1704     public function multiLineDataProvider()
1705     {
1706         $tests = array();
1707
1708         $yaml = <<<'EOF'
1709 foo:
1710 - bar:
1711     one
1712
1713     two
1714     three
1715 EOF;
1716         $expected = array(
1717             'foo' => array(
1718                 array(
1719                     'bar' => "one\ntwo three",
1720                 ),
1721             ),
1722         );
1723
1724         $tests[] = array($yaml, $expected, false);
1725
1726         $yaml = <<<'EOF'
1727 bar
1728 "foo"
1729 EOF;
1730         $expected = 'bar "foo"';
1731
1732         $tests[] = array($yaml, $expected, false);
1733
1734         $yaml = <<<'EOF'
1735 bar
1736 "foo
1737 EOF;
1738         $expected = 'bar "foo';
1739
1740         $tests[] = array($yaml, $expected, false);
1741
1742         $yaml = <<<'EOF'
1743 bar
1744
1745 'foo'
1746 EOF;
1747         $expected = "bar\n'foo'";
1748
1749         $tests[] = array($yaml, $expected, false);
1750
1751         $yaml = <<<'EOF'
1752 bar
1753
1754 foo'
1755 EOF;
1756         $expected = "bar\nfoo'";
1757
1758         $tests[] = array($yaml, $expected, false);
1759
1760         return $tests;
1761     }
1762
1763     public function testTaggedInlineMapping()
1764     {
1765         $this->assertEquals(new TaggedValue('foo', array('foo' => 'bar')), $this->parser->parse('!foo {foo: bar}', Yaml::PARSE_CUSTOM_TAGS));
1766     }
1767
1768     /**
1769      * @dataProvider taggedValuesProvider
1770      */
1771     public function testCustomTagSupport($expected, $yaml)
1772     {
1773         $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
1774     }
1775
1776     public function taggedValuesProvider()
1777     {
1778         return array(
1779             'sequences' => array(
1780                 array(new TaggedValue('foo', array('yaml')), new TaggedValue('quz', array('bar'))),
1781                 <<<YAML
1782 - !foo
1783     - yaml
1784 - !quz [bar]
1785 YAML
1786             ),
1787             'mappings' => array(
1788                 new TaggedValue('foo', array('foo' => new TaggedValue('quz', array('bar')), 'quz' => new TaggedValue('foo', array('quz' => 'bar')))),
1789                 <<<YAML
1790 !foo
1791 foo: !quz [bar]
1792 quz: !foo
1793    quz: bar
1794 YAML
1795             ),
1796             'inline' => array(
1797                 array(new TaggedValue('foo', array('foo', 'bar')), new TaggedValue('quz', array('foo' => 'bar', 'quz' => new TaggedValue('bar', array('one' => 'bar'))))),
1798                 <<<YAML
1799 - !foo [foo, bar]
1800 - !quz {foo: bar, quz: !bar {one: bar}}
1801 YAML
1802             ),
1803         );
1804     }
1805
1806     /**
1807      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1808      * @expectedExceptionMessage Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!iterator" at line 1 (near "!iterator [foo]").
1809      */
1810     public function testCustomTagsDisabled()
1811     {
1812         $this->parser->parse('!iterator [foo]');
1813     }
1814
1815     /**
1816      * @group legacy
1817      * @expectedDeprecation Using the unquoted scalar value "!iterator foo" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it on line 1.
1818      */
1819     public function testUnsupportedTagWithScalar()
1820     {
1821         $this->assertEquals('!iterator foo', $this->parser->parse('!iterator foo'));
1822     }
1823
1824     /**
1825      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1826      * @expectedExceptionMessage The built-in tag "!!foo" is not implemented at line 1 (near "!!foo").
1827      */
1828     public function testExceptionWhenUsingUnsuportedBuiltInTags()
1829     {
1830         $this->parser->parse('!!foo');
1831     }
1832
1833     /**
1834      * @group legacy
1835      * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1.
1836      */
1837     public function testComplexMappingThrowsParseException()
1838     {
1839         $yaml = <<<YAML
1840 ? "1"
1841 :
1842   name: végétalien
1843 YAML;
1844
1845         $this->parser->parse($yaml);
1846     }
1847
1848     /**
1849      * @group legacy
1850      * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 2.
1851      */
1852     public function testComplexMappingNestedInMappingThrowsParseException()
1853     {
1854         $yaml = <<<YAML
1855 diet:
1856   ? "1"
1857   :
1858     name: végétalien
1859 YAML;
1860
1861         $this->parser->parse($yaml);
1862     }
1863
1864     /**
1865      * @group legacy
1866      * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1.
1867      */
1868     public function testComplexMappingNestedInSequenceThrowsParseException()
1869     {
1870         $yaml = <<<YAML
1871 - ? "1"
1872   :
1873     name: végétalien
1874 YAML;
1875
1876         $this->parser->parse($yaml);
1877     }
1878
1879     /**
1880      * @expectedException        \Symfony\Component\Yaml\Exception\ParseException
1881      * @expectedExceptionMessage Unable to parse at line 1 (near "[parameters]").
1882      */
1883     public function testParsingIniThrowsException()
1884     {
1885         $ini = <<<INI
1886 [parameters]
1887   foo = bar
1888   bar = %foo%
1889 INI;
1890
1891         $this->parser->parse($ini);
1892     }
1893
1894     private function loadTestsFromFixtureFiles($testsFile)
1895     {
1896         $parser = new Parser();
1897
1898         $tests = array();
1899         $files = $parser->parseFile(__DIR__.'/Fixtures/'.$testsFile);
1900         foreach ($files as $file) {
1901             $yamls = file_get_contents(__DIR__.'/Fixtures/'.$file.'.yml');
1902
1903             // split YAMLs documents
1904             foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
1905                 if (!$yaml) {
1906                     continue;
1907                 }
1908
1909                 $test = $parser->parse($yaml);
1910                 if (isset($test['todo']) && $test['todo']) {
1911                     // TODO
1912                 } else {
1913                     eval('$expected = '.trim($test['php']).';');
1914
1915                     $tests[] = array(var_export($expected, true), $test['yaml'], $test['test'], isset($test['deprecated']) ? $test['deprecated'] : false);
1916                 }
1917             }
1918         }
1919
1920         return $tests;
1921     }
1922
1923     public function testCanParseVeryLongValue()
1924     {
1925         $longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000);
1926         $trickyVal = array('x' => $longStringWithSpaces);
1927
1928         $yamlString = Yaml::dump($trickyVal);
1929         $arrayFromYaml = $this->parser->parse($yamlString);
1930
1931         $this->assertEquals($trickyVal, $arrayFromYaml);
1932     }
1933
1934     /**
1935      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1936      * @expectedExceptionMessage Reference "foo" does not exist at line 2
1937      */
1938     public function testParserCleansUpReferencesBetweenRuns()
1939     {
1940         $yaml = <<<YAML
1941 foo: &foo
1942     baz: foobar
1943 bar:
1944     <<: *foo
1945 YAML;
1946         $this->parser->parse($yaml);
1947
1948         $yaml = <<<YAML
1949 bar:
1950     <<: *foo
1951 YAML;
1952         $this->parser->parse($yaml);
1953     }
1954
1955     public function testPhpConstantTagMappingKey()
1956     {
1957         $yaml = <<<YAML
1958 transitions:
1959     !php/const 'Symfony\Component\Yaml\Tests\B::FOO':
1960         from:
1961             - !php/const 'Symfony\Component\Yaml\Tests\B::BAR'
1962         to: !php/const 'Symfony\Component\Yaml\Tests\B::BAZ'
1963 YAML;
1964         $expected = array(
1965             'transitions' => array(
1966                 'foo' => array(
1967                     'from' => array(
1968                         'bar',
1969                     ),
1970                     'to' => 'baz',
1971                 ),
1972             ),
1973         );
1974
1975         $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
1976     }
1977
1978     /**
1979      * @group legacy
1980      * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 2.
1981      * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 4.
1982      * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 5.
1983      */
1984     public function testDeprecatedPhpConstantTagMappingKey()
1985     {
1986         $yaml = <<<YAML
1987 transitions:
1988     !php/const:Symfony\Component\Yaml\Tests\B::FOO:
1989         from:
1990             - !php/const:Symfony\Component\Yaml\Tests\B::BAR
1991         to: !php/const:Symfony\Component\Yaml\Tests\B::BAZ
1992 YAML;
1993         $expected = array(
1994             'transitions' => array(
1995                 'foo' => array(
1996                     'from' => array(
1997                         'bar',
1998                     ),
1999                     'to' => 'baz',
2000                 ),
2001             ),
2002         );
2003
2004         $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
2005     }
2006
2007     /**
2008      * @group legacy
2009      * @expectedDeprecation Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.
2010      */
2011     public function testPhpConstantTagMappingKeyWithKeysCastToStrings()
2012     {
2013         $yaml = <<<YAML
2014 transitions:
2015     !php/const 'Symfony\Component\Yaml\Tests\B::FOO':
2016         from:
2017             - !php/const 'Symfony\Component\Yaml\Tests\B::BAR'
2018         to: !php/const 'Symfony\Component\Yaml\Tests\B::BAZ'
2019 YAML;
2020         $expected = array(
2021             'transitions' => array(
2022                 'foo' => array(
2023                     'from' => array(
2024                         'bar',
2025                     ),
2026                     'to' => 'baz',
2027                 ),
2028             ),
2029         );
2030
2031         $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT | Yaml::PARSE_KEYS_AS_STRINGS));
2032     }
2033
2034     public function testMergeKeysWhenMappingsAreParsedAsObjects()
2035     {
2036         $yaml = <<<YAML
2037 foo: &FOO
2038     bar: 1
2039 bar: &BAR
2040     baz: 2
2041     <<: *FOO
2042 baz:
2043     baz_foo: 3
2044     <<:
2045         baz_bar: 4
2046 foobar:
2047     bar: ~
2048     <<: [*FOO, *BAR]
2049 YAML;
2050         $expected = (object) array(
2051             'foo' => (object) array(
2052                 'bar' => 1,
2053             ),
2054             'bar' => (object) array(
2055                 'baz' => 2,
2056                 'bar' => 1,
2057             ),
2058             'baz' => (object) array(
2059                 'baz_foo' => 3,
2060                 'baz_bar' => 4,
2061             ),
2062             'foobar' => (object) array(
2063                 'bar' => null,
2064                 'baz' => 2,
2065             ),
2066         );
2067
2068         $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
2069     }
2070
2071     public function testFilenamesAreParsedAsStringsWithoutFlag()
2072     {
2073         $file = __DIR__.'/Fixtures/index.yml';
2074
2075         $this->assertSame($file, $this->parser->parse($file));
2076     }
2077
2078     public function testParseFile()
2079     {
2080         $this->assertInternalType('array', $this->parser->parseFile(__DIR__.'/Fixtures/index.yml'));
2081     }
2082
2083     /**
2084      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
2085      * @expectedExceptionMessageRegExp #^File ".+/Fixtures/nonexistent.yml" does not exist\.$#
2086      */
2087     public function testParsingNonExistentFilesThrowsException()
2088     {
2089         $this->parser->parseFile(__DIR__.'/Fixtures/nonexistent.yml');
2090     }
2091
2092     /**
2093      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
2094      * @expectedExceptionMessageRegExp #^File ".+/Fixtures/not_readable.yml" cannot be read\.$#
2095      */
2096     public function testParsingNotReadableFilesThrowsException()
2097     {
2098         if ('\\' === DIRECTORY_SEPARATOR) {
2099             $this->markTestSkipped('chmod is not supported on Windows');
2100         }
2101
2102         $file = __DIR__.'/Fixtures/not_readable.yml';
2103         chmod($file, 0200);
2104
2105         $this->parser->parseFile($file);
2106     }
2107
2108     public function testParseReferencesOnMergeKeys()
2109     {
2110         $yaml = <<<YAML
2111 mergekeyrefdef:
2112     a: foo
2113     <<: &quux
2114         b: bar
2115         c: baz
2116 mergekeyderef:
2117     d: quux
2118     <<: *quux
2119 YAML;
2120         $expected = array(
2121             'mergekeyrefdef' => array(
2122                 'a' => 'foo',
2123                 'b' => 'bar',
2124                 'c' => 'baz',
2125             ),
2126             'mergekeyderef' => array(
2127                 'd' => 'quux',
2128                 'b' => 'bar',
2129                 'c' => 'baz',
2130             ),
2131         );
2132
2133         $this->assertSame($expected, $this->parser->parse($yaml));
2134     }
2135
2136     public function testParseReferencesOnMergeKeysWithMappingsParsedAsObjects()
2137     {
2138         $yaml = <<<YAML
2139 mergekeyrefdef:
2140     a: foo
2141     <<: &quux
2142         b: bar
2143         c: baz
2144 mergekeyderef:
2145     d: quux
2146     <<: *quux
2147 YAML;
2148         $expected = (object) array(
2149             'mergekeyrefdef' => (object) array(
2150                 'a' => 'foo',
2151                 'b' => 'bar',
2152                 'c' => 'baz',
2153             ),
2154             'mergekeyderef' => (object) array(
2155                 'd' => 'quux',
2156                 'b' => 'bar',
2157                 'c' => 'baz',
2158             ),
2159         );
2160
2161         $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
2162     }
2163
2164     /**
2165      * @expectedException \Symfony\Component\Yaml\Exception\ParseException
2166      * @expectedExceptionMessage Reference "foo" does not exist
2167      */
2168     public function testEvalRefException()
2169     {
2170         $yaml = <<<EOE
2171 foo: { &foo { a: Steve, <<: *foo} }
2172 EOE;
2173         $this->parser->parse($yaml);
2174     }
2175
2176     /**
2177      * @dataProvider indentedMappingData
2178      */
2179     public function testParseIndentedMappings($yaml, $expected)
2180     {
2181         $this->assertSame($expected, $this->parser->parse($yaml));
2182     }
2183
2184     public function indentedMappingData()
2185     {
2186         $tests = array();
2187
2188         $yaml = <<<YAML
2189 foo:
2190   - bar: "foobar"
2191     # A comment
2192     baz: "foobaz"
2193 YAML;
2194         $expected = array(
2195             'foo' => array(
2196                 array(
2197                     'bar' => 'foobar',
2198                     'baz' => 'foobaz',
2199                 ),
2200             ),
2201         );
2202         $tests['comment line is first line in indented block'] = array($yaml, $expected);
2203
2204         $yaml = <<<YAML
2205 foo:
2206     - bar:
2207         # comment
2208         baz: [1, 2, 3]
2209 YAML;
2210         $expected = array(
2211             'foo' => array(
2212                 array(
2213                     'bar' => array(
2214                         'baz' => array(1, 2, 3),
2215                     ),
2216                 ),
2217             ),
2218         );
2219         $tests['mapping value on new line starting with a comment line'] = array($yaml, $expected);
2220
2221         $yaml = <<<YAML
2222 foo:
2223   -
2224     bar: foobar
2225 YAML;
2226         $expected = array(
2227             'foo' => array(
2228                 array(
2229                     'bar' => 'foobar',
2230                 ),
2231             ),
2232         );
2233         $tests['mapping in sequence starting on a new line'] = array($yaml, $expected);
2234
2235         $yaml = <<<YAML
2236 foo:
2237
2238     bar: baz
2239 YAML;
2240         $expected = array(
2241             'foo' => array(
2242                 'bar' => 'baz',
2243             ),
2244         );
2245         $tests['blank line at the beginning of an indented mapping value'] = array($yaml, $expected);
2246
2247         return $tests;
2248     }
2249 }
2250
2251 class B
2252 {
2253     public $b = 'foo';
2254
2255     const FOO = 'foo';
2256     const BAR = 'bar';
2257     const BAZ = 'baz';
2258 }