ParserTest.php 27 KB

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