ParserTest.php (16451B)
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 Symfony\Component\Yaml\Yaml; 15 use Symfony\Component\Yaml\Parser; 16 17 class ParserTest extends \PHPUnit_Framework_TestCase 18 { 19 protected $parser; 20 21 protected function setUp() 22 { 23 $this->parser = new Parser(); 24 } 25 26 protected function tearDown() 27 { 28 $this->parser = null; 29 } 30 31 /** 32 * @dataProvider getDataFormSpecifications 33 */ 34 public function testSpecifications($file, $expected, $yaml, $comment) 35 { 36 $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment); 37 } 38 39 public function getDataFormSpecifications() 40 { 41 $parser = new Parser(); 42 $path = __DIR__.'/Fixtures'; 43 44 $tests = array(); 45 $files = $parser->parse(file_get_contents($path.'/index.yml')); 46 foreach ($files as $file) { 47 $yamls = file_get_contents($path.'/'.$file.'.yml'); 48 49 // split YAMLs documents 50 foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { 51 if (!$yaml) { 52 continue; 53 } 54 55 $test = $parser->parse($yaml); 56 if (isset($test['todo']) && $test['todo']) { 57 // TODO 58 } else { 59 eval('$expected = '.trim($test['php']).';'); 60 61 $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']); 62 } 63 } 64 } 65 66 return $tests; 67 } 68 69 public function testTabsInYaml() 70 { 71 // test tabs in YAML 72 $yamls = array( 73 "foo:\n bar", 74 "foo:\n bar", 75 "foo:\n bar", 76 "foo:\n bar", 77 ); 78 79 foreach ($yamls as $yaml) { 80 try { 81 $content = $this->parser->parse($yaml); 82 83 $this->fail('YAML files must not contain tabs'); 84 } catch (\Exception $e) { 85 $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs'); 86 $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'); 87 } 88 } 89 } 90 91 public function testEndOfTheDocumentMarker() 92 { 93 $yaml = <<<EOF 94 --- %YAML:1.0 95 foo 96 ... 97 EOF; 98 99 $this->assertEquals('foo', $this->parser->parse($yaml)); 100 } 101 102 public function getBlockChompingTests() 103 { 104 $tests = array(); 105 106 $yaml = <<<'EOF' 107 foo: |- 108 one 109 two 110 bar: |- 111 one 112 two 113 114 EOF; 115 $expected = array( 116 'foo' => "one\ntwo", 117 'bar' => "one\ntwo", 118 ); 119 $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml); 120 121 $yaml = <<<'EOF' 122 foo: |- 123 one 124 two 125 126 bar: |- 127 one 128 two 129 130 131 EOF; 132 $expected = array( 133 'foo' => "one\ntwo", 134 'bar' => "one\ntwo", 135 ); 136 $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml); 137 138 $yaml = <<<'EOF' 139 {} 140 141 142 EOF; 143 $expected = array(); 144 $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = array($expected, $yaml); 145 146 $yaml = <<<'EOF' 147 foo: |- 148 one 149 two 150 bar: |- 151 one 152 two 153 EOF; 154 $expected = array( 155 'foo' => "one\ntwo", 156 'bar' => "one\ntwo", 157 ); 158 $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml); 159 160 $yaml = <<<'EOF' 161 foo: | 162 one 163 two 164 bar: | 165 one 166 two 167 168 EOF; 169 $expected = array( 170 'foo' => "one\ntwo\n", 171 'bar' => "one\ntwo\n", 172 ); 173 $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml); 174 175 $yaml = <<<'EOF' 176 foo: | 177 one 178 two 179 180 bar: | 181 one 182 two 183 184 185 EOF; 186 $expected = array( 187 'foo' => "one\ntwo\n", 188 'bar' => "one\ntwo\n", 189 ); 190 $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml); 191 192 $yaml = <<<'EOF' 193 foo: | 194 one 195 two 196 bar: | 197 one 198 two 199 EOF; 200 $expected = array( 201 'foo' => "one\ntwo\n", 202 'bar' => "one\ntwo", 203 ); 204 $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml); 205 206 $yaml = <<<'EOF' 207 foo: |+ 208 one 209 two 210 bar: |+ 211 one 212 two 213 214 EOF; 215 $expected = array( 216 'foo' => "one\ntwo\n", 217 'bar' => "one\ntwo\n", 218 ); 219 $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml); 220 221 $yaml = <<<'EOF' 222 foo: |+ 223 one 224 two 225 226 bar: |+ 227 one 228 two 229 230 231 EOF; 232 $expected = array( 233 'foo' => "one\ntwo\n\n", 234 'bar' => "one\ntwo\n\n", 235 ); 236 $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml); 237 238 $yaml = <<<'EOF' 239 foo: |+ 240 one 241 two 242 bar: |+ 243 one 244 two 245 EOF; 246 $expected = array( 247 'foo' => "one\ntwo\n", 248 'bar' => "one\ntwo", 249 ); 250 $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml); 251 252 $yaml = <<<'EOF' 253 foo: >- 254 one 255 two 256 bar: >- 257 one 258 two 259 260 EOF; 261 $expected = array( 262 'foo' => 'one two', 263 'bar' => 'one two', 264 ); 265 $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml); 266 267 $yaml = <<<'EOF' 268 foo: >- 269 one 270 two 271 272 bar: >- 273 one 274 two 275 276 277 EOF; 278 $expected = array( 279 'foo' => 'one two', 280 'bar' => 'one two', 281 ); 282 $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml); 283 284 $yaml = <<<'EOF' 285 foo: >- 286 one 287 two 288 bar: >- 289 one 290 two 291 EOF; 292 $expected = array( 293 'foo' => 'one two', 294 'bar' => 'one two', 295 ); 296 $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml); 297 298 $yaml = <<<'EOF' 299 foo: > 300 one 301 two 302 bar: > 303 one 304 two 305 306 EOF; 307 $expected = array( 308 'foo' => "one two\n", 309 'bar' => "one two\n", 310 ); 311 $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml); 312 313 $yaml = <<<'EOF' 314 foo: > 315 one 316 two 317 318 bar: > 319 one 320 two 321 322 323 EOF; 324 $expected = array( 325 'foo' => "one two\n", 326 'bar' => "one two\n", 327 ); 328 $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml); 329 330 $yaml = <<<'EOF' 331 foo: > 332 one 333 two 334 bar: > 335 one 336 two 337 EOF; 338 $expected = array( 339 'foo' => "one two\n", 340 'bar' => 'one two', 341 ); 342 $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml); 343 344 $yaml = <<<'EOF' 345 foo: >+ 346 one 347 two 348 bar: >+ 349 one 350 two 351 352 EOF; 353 $expected = array( 354 'foo' => "one two\n", 355 'bar' => "one two\n", 356 ); 357 $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml); 358 359 $yaml = <<<'EOF' 360 foo: >+ 361 one 362 two 363 364 bar: >+ 365 one 366 two 367 368 369 EOF; 370 $expected = array( 371 'foo' => "one two\n\n", 372 'bar' => "one two\n\n", 373 ); 374 $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml); 375 376 $yaml = <<<'EOF' 377 foo: >+ 378 one 379 two 380 bar: >+ 381 one 382 two 383 EOF; 384 $expected = array( 385 'foo' => "one two\n", 386 'bar' => 'one two', 387 ); 388 $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml); 389 390 return $tests; 391 } 392 393 /** 394 * @dataProvider getBlockChompingTests 395 */ 396 public function testBlockChomping($expected, $yaml) 397 { 398 $this->assertSame($expected, $this->parser->parse($yaml)); 399 } 400 401 /** 402 * Regression test for issue #7989. 403 * 404 * @see https://github.com/symfony/symfony/issues/7989 405 */ 406 public function testBlockLiteralWithLeadingNewlines() 407 { 408 $yaml = <<<'EOF' 409 foo: |- 410 411 412 bar 413 414 EOF; 415 $expected = array( 416 'foo' => "\n\nbar", 417 ); 418 419 $this->assertSame($expected, $this->parser->parse($yaml)); 420 } 421 422 public function testObjectSupportEnabled() 423 { 424 $input = <<<EOF 425 foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";} 426 bar: 1 427 EOF; 428 $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); 429 } 430 431 public function testObjectSupportDisabledButNoExceptions() 432 { 433 $input = <<<EOF 434 foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} 435 bar: 1 436 EOF; 437 438 $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects'); 439 } 440 441 /** 442 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 443 */ 444 public function testObjectsSupportDisabledWithExceptions() 445 { 446 $this->parser->parse('foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}', true, false); 447 } 448 449 public function testNonUtf8Exception() 450 { 451 if (!function_exists('iconv')) { 452 $this->markTestSkipped('Exceptions for non-utf8 charsets require the iconv() function.'); 453 454 return; 455 } 456 457 $yamls = array( 458 iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"), 459 iconv('UTF-8', 'ISO-8859-15', "euro: '€'"), 460 iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"), 461 ); 462 463 foreach ($yamls as $yaml) { 464 try { 465 $this->parser->parse($yaml); 466 467 $this->fail('charsets other than UTF-8 are rejected.'); 468 } catch (\Exception $e) { 469 $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.'); 470 } 471 } 472 } 473 474 /** 475 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 476 */ 477 public function testUnindentedCollectionException() 478 { 479 $yaml = <<<EOF 480 481 collection: 482 -item1 483 -item2 484 -item3 485 486 EOF; 487 488 $this->parser->parse($yaml); 489 } 490 491 /** 492 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 493 */ 494 public function testShortcutKeyUnindentedCollectionException() 495 { 496 $yaml = <<<EOF 497 498 collection: 499 - key: foo 500 foo: bar 501 502 EOF; 503 504 $this->parser->parse($yaml); 505 } 506 507 /** 508 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 509 * @expectedExceptionMessage Multiple documents are not supported. 510 */ 511 public function testMultipleDocumentsNotSupportedException() 512 { 513 Yaml::parse(<<<EOL 514 # Ranking of 1998 home runs 515 --- 516 - Mark McGwire 517 - Sammy Sosa 518 - Ken Griffey 519 520 # Team ranking 521 --- 522 - Chicago Cubs 523 - St Louis Cardinals 524 EOL 525 ); 526 } 527 528 /** 529 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 530 */ 531 public function testSequenceInAMapping() 532 { 533 Yaml::parse(<<<EOF 534 yaml: 535 hash: me 536 - array stuff 537 EOF 538 ); 539 } 540 541 /** 542 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 543 */ 544 public function testMappingInASequence() 545 { 546 Yaml::parse(<<<EOF 547 yaml: 548 - array stuff 549 hash: me 550 EOF 551 ); 552 } 553 554 /** 555 * @expectedException \Symfony\Component\Yaml\Exception\ParseException 556 * @expectedExceptionMessage missing colon 557 */ 558 public function testScalarInSequence() 559 { 560 Yaml::parse(<<<EOF 561 foo: 562 - bar 563 "missing colon" 564 foo: bar 565 EOF 566 ); 567 } 568 569 /** 570 * > It is an error for two equal keys to appear in the same mapping node. 571 * > In such a case the YAML processor may continue, ignoring the second 572 * > `key: value` pair and issuing an appropriate warning. This strategy 573 * > preserves a consistent information model for one-pass and random access 574 * > applications. 575 * 576 * @see http://yaml.org/spec/1.2/spec.html#id2759572 577 * @see http://yaml.org/spec/1.1/#id932806 578 * 579 * @covers \Symfony\Component\Yaml\Parser::parse 580 */ 581 public function testMappingDuplicateKeyBlock() 582 { 583 $input = <<<EOD 584 parent: 585 child: first 586 child: duplicate 587 parent: 588 child: duplicate 589 child: duplicate 590 EOD; 591 $expected = array( 592 'parent' => array( 593 'child' => 'first', 594 ), 595 ); 596 $this->assertSame($expected, Yaml::parse($input)); 597 } 598 599 /** 600 * @covers \Symfony\Component\Yaml\Inline::parseMapping 601 */ 602 public function testMappingDuplicateKeyFlow() 603 { 604 $input = <<<EOD 605 parent: { child: first, child: duplicate } 606 parent: { child: duplicate, child: duplicate } 607 EOD; 608 $expected = array( 609 'parent' => array( 610 'child' => 'first', 611 ), 612 ); 613 $this->assertSame($expected, Yaml::parse($input)); 614 } 615 616 public function testEmptyValue() 617 { 618 $input = <<<EOF 619 hash: 620 EOF; 621 622 $this->assertEquals(array('hash' => null), Yaml::parse($input)); 623 } 624 625 public function testStringBlockWithComments() 626 { 627 $this->assertEquals(array('content' => <<<EOT 628 # comment 1 629 header 630 631 # comment 2 632 <body> 633 <h1>title</h1> 634 </body> 635 636 footer # comment3 637 EOT 638 ), Yaml::parse(<<<EOF 639 content: | 640 # comment 1 641 header 642 643 # comment 2 644 <body> 645 <h1>title</h1> 646 </body> 647 648 footer # comment3 649 EOF 650 )); 651 } 652 653 public function testFoldedStringBlockWithComments() 654 { 655 $this->assertEquals(array(array('content' => <<<EOT 656 # comment 1 657 header 658 659 # comment 2 660 <body> 661 <h1>title</h1> 662 </body> 663 664 footer # comment3 665 EOT 666 )), Yaml::parse(<<<EOF 667 - 668 content: | 669 # comment 1 670 header 671 672 # comment 2 673 <body> 674 <h1>title</h1> 675 </body> 676 677 footer # comment3 678 EOF 679 )); 680 } 681 682 public function testNestedFoldedStringBlockWithComments() 683 { 684 $this->assertEquals(array(array( 685 'title' => 'some title', 686 'content' => <<<EOT 687 # comment 1 688 header 689 690 # comment 2 691 <body> 692 <h1>title</h1> 693 </body> 694 695 footer # comment3 696 EOT 697 )), Yaml::parse(<<<EOF 698 - 699 title: some title 700 content: | 701 # comment 1 702 header 703 704 # comment 2 705 <body> 706 <h1>title</h1> 707 </body> 708 709 footer # comment3 710 EOF 711 )); 712 } 713 714 public function testReferenceResolvingInInlineStrings() 715 { 716 $this->assertEquals(array( 717 'var' => 'var-value', 718 'scalar' => 'var-value', 719 'list' => array('var-value'), 720 'list_in_list' => array(array('var-value')), 721 'map_in_list' => array(array('key' => 'var-value')), 722 'embedded_mapping' => array(array('key' => 'var-value')), 723 'map' => array('key' => 'var-value'), 724 'list_in_map' => array('key' => array('var-value')), 725 'map_in_map' => array('foo' => array('bar' => 'var-value')), 726 ), Yaml::parse(<<<EOF 727 var: &var var-value 728 scalar: *var 729 list: [ *var ] 730 list_in_list: [[ *var ]] 731 map_in_list: [ { key: *var } ] 732 embedded_mapping: [ key: *var ] 733 map: { key: *var } 734 list_in_map: { key: [*var] } 735 map_in_map: { foo: { bar: *var } } 736 EOF 737 )); 738 } 739 740 public function testYamlDirective() 741 { 742 $yaml = <<<EOF 743 %YAML 1.2 744 --- 745 foo: 1 746 bar: 2 747 EOF; 748 $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml)); 749 } 750 751 public function testFloatKeys() 752 { 753 $yaml = <<<EOF 754 foo: 755 1.2: "bar" 756 1.3: "baz" 757 EOF; 758 759 $expected = array( 760 'foo' => array( 761 '1.2' => 'bar', 762 '1.3' => 'baz', 763 ), 764 ); 765 766 $this->assertEquals($expected, $this->parser->parse($yaml)); 767 } 768 } 769 770 class B 771 { 772 public $b = 'foo'; 773 }