ParserTest.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Yaml\Tests;
  11. use Symfony\Component\Yaml\Yaml;
  12. use Symfony\Component\Yaml\Parser;
  13. class ParserTest extends \PHPUnit_Framework_TestCase
  14. {
  15. protected $parser;
  16. protected function setUp()
  17. {
  18. $this->parser = new Parser();
  19. }
  20. protected function tearDown()
  21. {
  22. $this->parser = null;
  23. }
  24. /**
  25. * @dataProvider getDataFormSpecifications
  26. */
  27. public function testSpecifications($file, $expected, $yaml, $comment)
  28. {
  29. $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
  30. }
  31. public function getDataFormSpecifications()
  32. {
  33. $parser = new Parser();
  34. $path = __DIR__.'/Fixtures';
  35. $tests = array();
  36. $files = $parser->parse(file_get_contents($path.'/index.yml'));
  37. foreach ($files as $file) {
  38. $yamls = file_get_contents($path.'/'.$file.'.yml');
  39. // split YAMLs documents
  40. foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
  41. if (!$yaml) {
  42. continue;
  43. }
  44. $test = $parser->parse($yaml);
  45. if (isset($test['todo']) && $test['todo']) {
  46. // TODO
  47. } else {
  48. eval('$expected = '.trim($test['php']).';');
  49. $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']);
  50. }
  51. }
  52. }
  53. return $tests;
  54. }
  55. public function testTabsInYaml()
  56. {
  57. // test tabs in YAML
  58. $yamls = array(
  59. "foo:\n bar",
  60. "foo:\n bar",
  61. "foo:\n bar",
  62. "foo:\n bar",
  63. );
  64. foreach ($yamls as $yaml) {
  65. try {
  66. $content = $this->parser->parse($yaml);
  67. $this->fail('YAML files must not contain tabs');
  68. } catch (\Exception $e) {
  69. $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs');
  70. $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');
  71. }
  72. }
  73. }
  74. public function testEndOfTheDocumentMarker()
  75. {
  76. $yaml = <<<'EOF'
  77. --- %YAML:1.0
  78. foo
  79. ...
  80. EOF;
  81. $this->assertEquals('foo', $this->parser->parse($yaml));
  82. }
  83. public function getBlockChompingTests()
  84. {
  85. $tests = array();
  86. $yaml = <<<'EOF'
  87. foo: |-
  88. one
  89. two
  90. bar: |-
  91. one
  92. two
  93. EOF;
  94. $expected = array(
  95. 'foo' => "one\ntwo",
  96. 'bar' => "one\ntwo",
  97. );
  98. $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml);
  99. $yaml = <<<'EOF'
  100. foo: |-
  101. one
  102. two
  103. bar: |-
  104. one
  105. two
  106. EOF;
  107. $expected = array(
  108. 'foo' => "one\ntwo",
  109. 'bar' => "one\ntwo",
  110. );
  111. $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  112. $yaml = <<<'EOF'
  113. {}
  114. EOF;
  115. $expected = array();
  116. $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = array($expected, $yaml);
  117. $yaml = <<<'EOF'
  118. foo: |-
  119. one
  120. two
  121. bar: |-
  122. one
  123. two
  124. EOF;
  125. $expected = array(
  126. 'foo' => "one\ntwo",
  127. 'bar' => "one\ntwo",
  128. );
  129. $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml);
  130. $yaml = <<<'EOF'
  131. foo: |
  132. one
  133. two
  134. bar: |
  135. one
  136. two
  137. EOF;
  138. $expected = array(
  139. 'foo' => "one\ntwo\n",
  140. 'bar' => "one\ntwo\n",
  141. );
  142. $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml);
  143. $yaml = <<<'EOF'
  144. foo: |
  145. one
  146. two
  147. bar: |
  148. one
  149. two
  150. EOF;
  151. $expected = array(
  152. 'foo' => "one\ntwo\n",
  153. 'bar' => "one\ntwo\n",
  154. );
  155. $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  156. $yaml = <<<'EOF'
  157. foo: |
  158. one
  159. two
  160. bar: |
  161. one
  162. two
  163. EOF;
  164. $expected = array(
  165. 'foo' => "one\ntwo\n",
  166. 'bar' => "one\ntwo",
  167. );
  168. $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml);
  169. $yaml = <<<'EOF'
  170. foo: |+
  171. one
  172. two
  173. bar: |+
  174. one
  175. two
  176. EOF;
  177. $expected = array(
  178. 'foo' => "one\ntwo\n",
  179. 'bar' => "one\ntwo\n",
  180. );
  181. $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml);
  182. $yaml = <<<'EOF'
  183. foo: |+
  184. one
  185. two
  186. bar: |+
  187. one
  188. two
  189. EOF;
  190. $expected = array(
  191. 'foo' => "one\ntwo\n\n",
  192. 'bar' => "one\ntwo\n\n",
  193. );
  194. $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  195. $yaml = <<<'EOF'
  196. foo: |+
  197. one
  198. two
  199. bar: |+
  200. one
  201. two
  202. EOF;
  203. $expected = array(
  204. 'foo' => "one\ntwo\n",
  205. 'bar' => "one\ntwo",
  206. );
  207. $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml);
  208. $yaml = <<<'EOF'
  209. foo: >-
  210. one
  211. two
  212. bar: >-
  213. one
  214. two
  215. EOF;
  216. $expected = array(
  217. 'foo' => 'one two',
  218. 'bar' => 'one two',
  219. );
  220. $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml);
  221. $yaml = <<<'EOF'
  222. foo: >-
  223. one
  224. two
  225. bar: >-
  226. one
  227. two
  228. EOF;
  229. $expected = array(
  230. 'foo' => 'one two',
  231. 'bar' => 'one two',
  232. );
  233. $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  234. $yaml = <<<'EOF'
  235. foo: >-
  236. one
  237. two
  238. bar: >-
  239. one
  240. two
  241. EOF;
  242. $expected = array(
  243. 'foo' => 'one two',
  244. 'bar' => 'one two',
  245. );
  246. $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml);
  247. $yaml = <<<'EOF'
  248. foo: >
  249. one
  250. two
  251. bar: >
  252. one
  253. two
  254. EOF;
  255. $expected = array(
  256. 'foo' => "one two\n",
  257. 'bar' => "one two\n",
  258. );
  259. $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml);
  260. $yaml = <<<'EOF'
  261. foo: >
  262. one
  263. two
  264. bar: >
  265. one
  266. two
  267. EOF;
  268. $expected = array(
  269. 'foo' => "one two\n",
  270. 'bar' => "one two\n",
  271. );
  272. $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  273. $yaml = <<<'EOF'
  274. foo: >
  275. one
  276. two
  277. bar: >
  278. one
  279. two
  280. EOF;
  281. $expected = array(
  282. 'foo' => "one two\n",
  283. 'bar' => 'one two',
  284. );
  285. $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml);
  286. $yaml = <<<'EOF'
  287. foo: >+
  288. one
  289. two
  290. bar: >+
  291. one
  292. two
  293. EOF;
  294. $expected = array(
  295. 'foo' => "one two\n",
  296. 'bar' => "one two\n",
  297. );
  298. $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml);
  299. $yaml = <<<'EOF'
  300. foo: >+
  301. one
  302. two
  303. bar: >+
  304. one
  305. two
  306. EOF;
  307. $expected = array(
  308. 'foo' => "one two\n\n",
  309. 'bar' => "one two\n\n",
  310. );
  311. $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  312. $yaml = <<<'EOF'
  313. foo: >+
  314. one
  315. two
  316. bar: >+
  317. one
  318. two
  319. EOF;
  320. $expected = array(
  321. 'foo' => "one two\n",
  322. 'bar' => 'one two',
  323. );
  324. $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);
  325. return $tests;
  326. }
  327. /**
  328. * @dataProvider getBlockChompingTests
  329. */
  330. public function testBlockChomping($expected, $yaml)
  331. {
  332. $this->assertSame($expected, $this->parser->parse($yaml));
  333. }
  334. /**
  335. * Regression test for issue #7989.
  336. *
  337. * @see https://github.com/symfony/symfony/issues/7989
  338. */
  339. public function testBlockLiteralWithLeadingNewlines()
  340. {
  341. $yaml = <<<'EOF'
  342. foo: |-
  343. bar
  344. EOF;
  345. $expected = array(
  346. 'foo' => "\n\nbar",
  347. );
  348. $this->assertSame($expected, $this->parser->parse($yaml));
  349. }
  350. public function testObjectSupportEnabled()
  351. {
  352. $input = <<<EOF
  353. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  354. bar: 1
  355. EOF;
  356. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  357. }
  358. /**
  359. * @group legacy
  360. */
  361. public function testObjectSupportEnabledPassingTrue()
  362. {
  363. $input = <<<EOF
  364. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  365. bar: 1
  366. EOF;
  367. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
  368. }
  369. /**
  370. * @group legacy
  371. */
  372. public function testObjectSupportEnabledWithDeprecatedTag()
  373. {
  374. $input = <<<EOF
  375. foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  376. bar: 1
  377. EOF;
  378. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  379. }
  380. /**
  381. * @dataProvider invalidDumpedObjectProvider
  382. */
  383. public function testObjectSupportDisabledButNoExceptions($input)
  384. {
  385. $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
  386. }
  387. /**
  388. * @dataProvider getObjectForMapTests
  389. */
  390. public function testObjectForMap($yaml, $expected)
  391. {
  392. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  393. }
  394. /**
  395. * @group legacy
  396. * @dataProvider getObjectForMapTests
  397. */
  398. public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected)
  399. {
  400. $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
  401. }
  402. public function getObjectForMapTests()
  403. {
  404. $tests = array();
  405. $yaml = <<<EOF
  406. foo:
  407. fiz: [cat]
  408. EOF;
  409. $expected = new \stdClass();
  410. $expected->foo = new \stdClass();
  411. $expected->foo->fiz = array('cat');
  412. $tests['mapping'] = array($yaml, $expected);
  413. $yaml = '{ "foo": "bar", "fiz": "cat" }';
  414. $expected = new \stdClass();
  415. $expected->foo = 'bar';
  416. $expected->fiz = 'cat';
  417. $tests['inline-mapping'] = array($yaml, $expected);
  418. $yaml = "foo: bar\nbaz: foobar";
  419. $expected = new \stdClass();
  420. $expected->foo = 'bar';
  421. $expected->baz = 'foobar';
  422. $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected);
  423. $yaml = <<<EOT
  424. array:
  425. - key: one
  426. - key: two
  427. EOT;
  428. $expected = new \stdClass();
  429. $expected->array = array();
  430. $expected->array[0] = new \stdClass();
  431. $expected->array[0]->key = 'one';
  432. $expected->array[1] = new \stdClass();
  433. $expected->array[1]->key = 'two';
  434. $tests['nest-map-and-sequence'] = array($yaml, $expected);
  435. $yaml = <<<YAML
  436. map:
  437. 1: one
  438. 2: two
  439. YAML;
  440. $expected = new \stdClass();
  441. $expected->map = new \stdClass();
  442. $expected->map->{1} = 'one';
  443. $expected->map->{2} = 'two';
  444. $tests['numeric-keys'] = array($yaml, $expected);
  445. $yaml = <<<YAML
  446. map:
  447. 0: one
  448. 1: two
  449. YAML;
  450. $expected = new \stdClass();
  451. $expected->map = new \stdClass();
  452. $expected->map->{0} = 'one';
  453. $expected->map->{1} = 'two';
  454. $tests['zero-indexed-numeric-keys'] = array($yaml, $expected);
  455. return $tests;
  456. }
  457. /**
  458. * @dataProvider invalidDumpedObjectProvider
  459. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  460. */
  461. public function testObjectsSupportDisabledWithExceptions($yaml)
  462. {
  463. $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
  464. }
  465. /**
  466. * @group legacy
  467. * @dataProvider invalidDumpedObjectProvider
  468. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  469. */
  470. public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml)
  471. {
  472. $this->parser->parse($yaml, true);
  473. }
  474. public function invalidDumpedObjectProvider()
  475. {
  476. $yamlTag = <<<EOF
  477. foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  478. bar: 1
  479. EOF;
  480. $localTag = <<<EOF
  481. foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  482. bar: 1
  483. EOF;
  484. return array(
  485. 'yaml-tag' => array($yamlTag),
  486. 'local-tag' => array($localTag),
  487. );
  488. }
  489. /**
  490. * @requires extension iconv
  491. */
  492. public function testNonUtf8Exception()
  493. {
  494. $yamls = array(
  495. iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"),
  496. iconv('UTF-8', 'ISO-8859-15', "euro: '€'"),
  497. iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"),
  498. );
  499. foreach ($yamls as $yaml) {
  500. try {
  501. $this->parser->parse($yaml);
  502. $this->fail('charsets other than UTF-8 are rejected.');
  503. } catch (\Exception $e) {
  504. $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
  505. }
  506. }
  507. }
  508. /**
  509. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  510. */
  511. public function testUnindentedCollectionException()
  512. {
  513. $yaml = <<<'EOF'
  514. collection:
  515. -item1
  516. -item2
  517. -item3
  518. EOF;
  519. $this->parser->parse($yaml);
  520. }
  521. /**
  522. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  523. */
  524. public function testShortcutKeyUnindentedCollectionException()
  525. {
  526. $yaml = <<<'EOF'
  527. collection:
  528. - key: foo
  529. foo: bar
  530. EOF;
  531. $this->parser->parse($yaml);
  532. }
  533. /**
  534. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  535. * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/
  536. */
  537. public function testMultipleDocumentsNotSupportedException()
  538. {
  539. Yaml::parse(<<<'EOL'
  540. # Ranking of 1998 home runs
  541. ---
  542. - Mark McGwire
  543. - Sammy Sosa
  544. - Ken Griffey
  545. # Team ranking
  546. ---
  547. - Chicago Cubs
  548. - St Louis Cardinals
  549. EOL
  550. );
  551. }
  552. /**
  553. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  554. */
  555. public function testSequenceInAMapping()
  556. {
  557. Yaml::parse(<<<'EOF'
  558. yaml:
  559. hash: me
  560. - array stuff
  561. EOF
  562. );
  563. }
  564. public function testSequenceInMappingStartedBySingleDashLine()
  565. {
  566. $yaml = <<<EOT
  567. a:
  568. -
  569. b:
  570. -
  571. bar: baz
  572. - foo
  573. d: e
  574. EOT;
  575. $expected = array(
  576. 'a' => array(
  577. array(
  578. 'b' => array(
  579. array(
  580. 'bar' => 'baz',
  581. ),
  582. ),
  583. ),
  584. 'foo',
  585. ),
  586. 'd' => 'e',
  587. );
  588. $this->assertSame($expected, $this->parser->parse($yaml));
  589. }
  590. public function testSequenceFollowedByCommentEmbeddedInMapping()
  591. {
  592. $yaml = <<<EOT
  593. a:
  594. b:
  595. - c
  596. # comment
  597. d: e
  598. EOT;
  599. $expected = array(
  600. 'a' => array(
  601. 'b' => array('c'),
  602. 'd' => 'e',
  603. ),
  604. );
  605. $this->assertSame($expected, $this->parser->parse($yaml));
  606. }
  607. /**
  608. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  609. */
  610. public function testMappingInASequence()
  611. {
  612. Yaml::parse(<<<'EOF'
  613. yaml:
  614. - array stuff
  615. hash: me
  616. EOF
  617. );
  618. }
  619. /**
  620. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  621. * @expectedExceptionMessage missing colon
  622. */
  623. public function testScalarInSequence()
  624. {
  625. Yaml::parse(<<<EOF
  626. foo:
  627. - bar
  628. "missing colon"
  629. foo: bar
  630. EOF
  631. );
  632. }
  633. /**
  634. * > It is an error for two equal keys to appear in the same mapping node.
  635. * > In such a case the YAML processor may continue, ignoring the second
  636. * > `key: value` pair and issuing an appropriate warning. This strategy
  637. * > preserves a consistent information model for one-pass and random access
  638. * > applications.
  639. *
  640. * @see http://yaml.org/spec/1.2/spec.html#id2759572
  641. * @see http://yaml.org/spec/1.1/#id932806
  642. */
  643. public function testMappingDuplicateKeyBlock()
  644. {
  645. $input = <<<EOD
  646. parent:
  647. child: first
  648. child: duplicate
  649. parent:
  650. child: duplicate
  651. child: duplicate
  652. EOD;
  653. $expected = array(
  654. 'parent' => array(
  655. 'child' => 'first',
  656. ),
  657. );
  658. $this->assertSame($expected, Yaml::parse($input));
  659. }
  660. public function testMappingDuplicateKeyFlow()
  661. {
  662. $input = <<<EOD
  663. parent: { child: first, child: duplicate }
  664. parent: { child: duplicate, child: duplicate }
  665. EOD;
  666. $expected = array(
  667. 'parent' => array(
  668. 'child' => 'first',
  669. ),
  670. );
  671. $this->assertSame($expected, Yaml::parse($input));
  672. }
  673. public function testEmptyValue()
  674. {
  675. $input = <<<'EOF'
  676. hash:
  677. EOF;
  678. $this->assertEquals(array('hash' => null), Yaml::parse($input));
  679. }
  680. public function testCommentAtTheRootIndent()
  681. {
  682. $this->assertEquals(array(
  683. 'services' => array(
  684. 'app.foo_service' => array(
  685. 'class' => 'Foo',
  686. ),
  687. 'app/bar_service' => array(
  688. 'class' => 'Bar',
  689. ),
  690. ),
  691. ), Yaml::parse(<<<'EOF'
  692. # comment 1
  693. services:
  694. # comment 2
  695. # comment 3
  696. app.foo_service:
  697. class: Foo
  698. # comment 4
  699. # comment 5
  700. app/bar_service:
  701. class: Bar
  702. EOF
  703. ));
  704. }
  705. public function testStringBlockWithComments()
  706. {
  707. $this->assertEquals(array('content' => <<<'EOT'
  708. # comment 1
  709. header
  710. # comment 2
  711. <body>
  712. <h1>title</h1>
  713. </body>
  714. footer # comment3
  715. EOT
  716. ), Yaml::parse(<<<'EOF'
  717. content: |
  718. # comment 1
  719. header
  720. # comment 2
  721. <body>
  722. <h1>title</h1>
  723. </body>
  724. footer # comment3
  725. EOF
  726. ));
  727. }
  728. public function testFoldedStringBlockWithComments()
  729. {
  730. $this->assertEquals(array(array('content' => <<<'EOT'
  731. # comment 1
  732. header
  733. # comment 2
  734. <body>
  735. <h1>title</h1>
  736. </body>
  737. footer # comment3
  738. EOT
  739. )), Yaml::parse(<<<'EOF'
  740. -
  741. content: |
  742. # comment 1
  743. header
  744. # comment 2
  745. <body>
  746. <h1>title</h1>
  747. </body>
  748. footer # comment3
  749. EOF
  750. ));
  751. }
  752. public function testNestedFoldedStringBlockWithComments()
  753. {
  754. $this->assertEquals(array(array(
  755. 'title' => 'some title',
  756. 'content' => <<<'EOT'
  757. # comment 1
  758. header
  759. # comment 2
  760. <body>
  761. <h1>title</h1>
  762. </body>
  763. footer # comment3
  764. EOT
  765. )), Yaml::parse(<<<'EOF'
  766. -
  767. title: some title
  768. content: |
  769. # comment 1
  770. header
  771. # comment 2
  772. <body>
  773. <h1>title</h1>
  774. </body>
  775. footer # comment3
  776. EOF
  777. ));
  778. }
  779. public function testReferenceResolvingInInlineStrings()
  780. {
  781. $this->assertEquals(array(
  782. 'var' => 'var-value',
  783. 'scalar' => 'var-value',
  784. 'list' => array('var-value'),
  785. 'list_in_list' => array(array('var-value')),
  786. 'map_in_list' => array(array('key' => 'var-value')),
  787. 'embedded_mapping' => array(array('key' => 'var-value')),
  788. 'map' => array('key' => 'var-value'),
  789. 'list_in_map' => array('key' => array('var-value')),
  790. 'map_in_map' => array('foo' => array('bar' => 'var-value')),
  791. ), Yaml::parse(<<<'EOF'
  792. var: &var var-value
  793. scalar: *var
  794. list: [ *var ]
  795. list_in_list: [[ *var ]]
  796. map_in_list: [ { key: *var } ]
  797. embedded_mapping: [ key: *var ]
  798. map: { key: *var }
  799. list_in_map: { key: [*var] }
  800. map_in_map: { foo: { bar: *var } }
  801. EOF
  802. ));
  803. }
  804. public function testYamlDirective()
  805. {
  806. $yaml = <<<'EOF'
  807. %YAML 1.2
  808. ---
  809. foo: 1
  810. bar: 2
  811. EOF;
  812. $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml));
  813. }
  814. public function testFloatKeys()
  815. {
  816. $yaml = <<<'EOF'
  817. foo:
  818. 1.2: "bar"
  819. 1.3: "baz"
  820. EOF;
  821. $expected = array(
  822. 'foo' => array(
  823. '1.2' => 'bar',
  824. '1.3' => 'baz',
  825. ),
  826. );
  827. $this->assertEquals($expected, $this->parser->parse($yaml));
  828. }
  829. /**
  830. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  831. * @expectedExceptionMessage A colon cannot be used in an unquoted mapping value
  832. */
  833. public function testColonInMappingValueException()
  834. {
  835. $yaml = <<<EOF
  836. foo: bar: baz
  837. EOF;
  838. $this->parser->parse($yaml);
  839. }
  840. public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
  841. {
  842. $yaml = <<<EOT
  843. foo:
  844. bar: foobar # Note: a comment after a colon
  845. EOT;
  846. $this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml));
  847. }
  848. /**
  849. * @dataProvider getCommentLikeStringInScalarBlockData
  850. */
  851. public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
  852. {
  853. $this->assertSame($expectedParserResult, $this->parser->parse($yaml));
  854. }
  855. public function getCommentLikeStringInScalarBlockData()
  856. {
  857. $tests = array();
  858. $yaml = <<<'EOT'
  859. pages:
  860. -
  861. title: some title
  862. content: |
  863. # comment 1
  864. header
  865. # comment 2
  866. <body>
  867. <h1>title</h1>
  868. </body>
  869. footer # comment3
  870. EOT;
  871. $expected = array(
  872. 'pages' => array(
  873. array(
  874. 'title' => 'some title',
  875. 'content' => <<<'EOT'
  876. # comment 1
  877. header
  878. # comment 2
  879. <body>
  880. <h1>title</h1>
  881. </body>
  882. footer # comment3
  883. EOT
  884. ,
  885. ),
  886. ),
  887. );
  888. $tests[] = array($yaml, $expected);
  889. $yaml = <<<'EOT'
  890. test: |
  891. foo
  892. # bar
  893. baz
  894. collection:
  895. - one: |
  896. foo
  897. # bar
  898. baz
  899. - two: |
  900. foo
  901. # bar
  902. baz
  903. EOT;
  904. $expected = array(
  905. 'test' => <<<'EOT'
  906. foo
  907. # bar
  908. baz
  909. EOT
  910. ,
  911. 'collection' => array(
  912. array(
  913. 'one' => <<<'EOT'
  914. foo
  915. # bar
  916. baz
  917. EOT
  918. ,
  919. ),
  920. array(
  921. 'two' => <<<'EOT'
  922. foo
  923. # bar
  924. baz
  925. EOT
  926. ,
  927. ),
  928. ),
  929. );
  930. $tests[] = array($yaml, $expected);
  931. $yaml = <<<EOT
  932. foo:
  933. bar:
  934. scalar-block: >
  935. line1
  936. line2>
  937. baz:
  938. # comment
  939. foobar: ~
  940. EOT;
  941. $expected = array(
  942. 'foo' => array(
  943. 'bar' => array(
  944. 'scalar-block' => "line1 line2>\n",
  945. ),
  946. 'baz' => array(
  947. 'foobar' => null,
  948. ),
  949. ),
  950. );
  951. $tests[] = array($yaml, $expected);
  952. $yaml = <<<'EOT'
  953. a:
  954. b: hello
  955. # c: |
  956. # first row
  957. # second row
  958. d: hello
  959. EOT;
  960. $expected = array(
  961. 'a' => array(
  962. 'b' => 'hello',
  963. 'd' => 'hello',
  964. ),
  965. );
  966. $tests[] = array($yaml, $expected);
  967. return $tests;
  968. }
  969. public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
  970. {
  971. $yaml = <<<EOT
  972. test: >
  973. <h2>A heading</h2>
  974. <ul>
  975. <li>a list</li>
  976. <li>may be a good example</li>
  977. </ul>
  978. EOT;
  979. $this->assertSame(
  980. array(
  981. 'test' => <<<EOT
  982. <h2>A heading</h2>
  983. <ul> <li>a list</li> <li>may be a good example</li> </ul>
  984. EOT
  985. ,
  986. ),
  987. $this->parser->parse($yaml)
  988. );
  989. }
  990. public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
  991. {
  992. $yaml = <<<EOT
  993. test: >
  994. <h2>A heading</h2>
  995. <ul>
  996. <li>a list</li>
  997. <li>may be a good example</li>
  998. </ul>
  999. EOT;
  1000. $this->assertSame(
  1001. array(
  1002. 'test' => <<<EOT
  1003. <h2>A heading</h2>
  1004. <ul>
  1005. <li>a list</li>
  1006. <li>may be a good example</li>
  1007. </ul>
  1008. EOT
  1009. ,
  1010. ),
  1011. $this->parser->parse($yaml)
  1012. );
  1013. }
  1014. /**
  1015. * @dataProvider getBinaryData
  1016. */
  1017. public function testParseBinaryData($data)
  1018. {
  1019. $this->assertSame(array('data' => 'Hello world'), $this->parser->parse($data));
  1020. }
  1021. public function getBinaryData()
  1022. {
  1023. return array(
  1024. 'enclosed with double quotes' => array('data: !!binary "SGVsbG8gd29ybGQ="'),
  1025. 'enclosed with single quotes' => array("data: !!binary 'SGVsbG8gd29ybGQ='"),
  1026. 'containing spaces' => array('data: !!binary "SGVs bG8gd 29ybGQ="'),
  1027. 'in block scalar' => array(
  1028. <<<EOT
  1029. data: !!binary |
  1030. SGVsbG8gd29ybGQ=
  1031. EOT
  1032. ),
  1033. 'containing spaces in block scalar' => array(
  1034. <<<EOT
  1035. data: !!binary |
  1036. SGVs bG8gd 29ybGQ=
  1037. EOT
  1038. ),
  1039. );
  1040. }
  1041. /**
  1042. * @dataProvider getInvalidBinaryData
  1043. */
  1044. public function testParseInvalidBinaryData($data, $expectedMessage)
  1045. {
  1046. $this->setExpectedExceptionRegExp('\Symfony\Component\Yaml\Exception\ParseException', $expectedMessage);
  1047. $this->parser->parse($data);
  1048. }
  1049. public function getInvalidBinaryData()
  1050. {
  1051. return array(
  1052. '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\)/'),
  1053. 'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1054. 'too many equals characters' => array('data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1055. 'misplaced equals character' => array('data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1056. 'length not a multiple of four in block scalar' => array(
  1057. <<<EOT
  1058. data: !!binary |
  1059. SGVsbG8d29ybGQ=
  1060. EOT
  1061. ,
  1062. '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/',
  1063. ),
  1064. 'invalid characters in block scalar' => array(
  1065. <<<EOT
  1066. data: !!binary |
  1067. SGVsbG8#d29ybGQ=
  1068. EOT
  1069. ,
  1070. '/The base64 encoded data \(.*\) contains invalid characters/',
  1071. ),
  1072. 'too many equals characters in block scalar' => array(
  1073. <<<EOT
  1074. data: !!binary |
  1075. SGVsbG8gd29yb===
  1076. EOT
  1077. ,
  1078. '/The base64 encoded data \(.*\) contains invalid characters/',
  1079. ),
  1080. 'misplaced equals character in block scalar' => array(
  1081. <<<EOT
  1082. data: !!binary |
  1083. SGVsbG8gd29ybG=Q
  1084. EOT
  1085. ,
  1086. '/The base64 encoded data \(.*\) contains invalid characters/',
  1087. ),
  1088. );
  1089. }
  1090. public function testParseDateAsMappingValue()
  1091. {
  1092. $yaml = <<<EOT
  1093. date: 2002-12-14
  1094. EOT;
  1095. $expectedDate = new \DateTime();
  1096. $expectedDate->setTimeZone(new \DateTimeZone('UTC'));
  1097. $expectedDate->setDate(2002, 12, 14);
  1098. $expectedDate->setTime(0, 0, 0);
  1099. $this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
  1100. }
  1101. /**
  1102. * @param $lineNumber
  1103. * @param $yaml
  1104. * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
  1105. */
  1106. public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
  1107. {
  1108. $this->setExpectedException(
  1109. '\Symfony\Component\Yaml\Exception\ParseException',
  1110. sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber)
  1111. );
  1112. $this->parser->parse($yaml);
  1113. }
  1114. public function parserThrowsExceptionWithCorrectLineNumberProvider()
  1115. {
  1116. return array(
  1117. array(
  1118. 4,
  1119. <<<YAML
  1120. foo:
  1121. -
  1122. # bar
  1123. bar: "123",
  1124. YAML
  1125. ),
  1126. array(
  1127. 5,
  1128. <<<YAML
  1129. foo:
  1130. -
  1131. # bar
  1132. # bar
  1133. bar: "123",
  1134. YAML
  1135. ),
  1136. array(
  1137. 8,
  1138. <<<YAML
  1139. foo:
  1140. -
  1141. # foobar
  1142. baz: 123
  1143. bar:
  1144. -
  1145. # bar
  1146. bar: "123",
  1147. YAML
  1148. ),
  1149. array(
  1150. 10,
  1151. <<<YAML
  1152. foo:
  1153. -
  1154. # foobar
  1155. # foobar
  1156. baz: 123
  1157. bar:
  1158. -
  1159. # bar
  1160. # bar
  1161. bar: "123",
  1162. YAML
  1163. ),
  1164. );
  1165. }
  1166. }
  1167. class B
  1168. {
  1169. public $b = 'foo';
  1170. }