ParserTest.php 25 KB

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