Ei kuvausta

ParserTest.php 32KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474
  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, $deprecated)
  28. {
  29. $deprecations = array();
  30. if ($deprecated) {
  31. set_error_handler(function ($type, $msg) use (&$deprecations) {
  32. if (E_USER_DEPRECATED !== $type) {
  33. restore_error_handler();
  34. return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args());
  35. }
  36. $deprecations[] = $msg;
  37. });
  38. }
  39. $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
  40. if ($deprecated) {
  41. restore_error_handler();
  42. $this->assertCount(1, $deprecations);
  43. $this->assertContains('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0.', $deprecations[0]);
  44. }
  45. }
  46. public function getDataFormSpecifications()
  47. {
  48. $parser = new Parser();
  49. $path = __DIR__.'/Fixtures';
  50. $tests = array();
  51. $files = $parser->parse(file_get_contents($path.'/index.yml'));
  52. foreach ($files as $file) {
  53. $yamls = file_get_contents($path.'/'.$file.'.yml');
  54. // split YAMLs documents
  55. foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
  56. if (!$yaml) {
  57. continue;
  58. }
  59. $test = $parser->parse($yaml);
  60. if (isset($test['todo']) && $test['todo']) {
  61. // TODO
  62. } else {
  63. eval('$expected = '.trim($test['php']).';');
  64. $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test'], isset($test['deprecated']) ? $test['deprecated'] : false);
  65. }
  66. }
  67. }
  68. return $tests;
  69. }
  70. public function testTabsInYaml()
  71. {
  72. // test tabs in YAML
  73. $yamls = array(
  74. "foo:\n bar",
  75. "foo:\n bar",
  76. "foo:\n bar",
  77. "foo:\n bar",
  78. );
  79. foreach ($yamls as $yaml) {
  80. try {
  81. $content = $this->parser->parse($yaml);
  82. $this->fail('YAML files must not contain tabs');
  83. } catch (\Exception $e) {
  84. $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs');
  85. $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');
  86. }
  87. }
  88. }
  89. public function testEndOfTheDocumentMarker()
  90. {
  91. $yaml = <<<'EOF'
  92. --- %YAML:1.0
  93. foo
  94. ...
  95. EOF;
  96. $this->assertEquals('foo', $this->parser->parse($yaml));
  97. }
  98. public function getBlockChompingTests()
  99. {
  100. $tests = array();
  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 single trailing newline'] = array($expected, $yaml);
  114. $yaml = <<<'EOF'
  115. foo: |-
  116. one
  117. two
  118. bar: |-
  119. one
  120. two
  121. EOF;
  122. $expected = array(
  123. 'foo' => "one\ntwo",
  124. 'bar' => "one\ntwo",
  125. );
  126. $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  127. $yaml = <<<'EOF'
  128. {}
  129. EOF;
  130. $expected = array();
  131. $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = 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",
  142. 'bar' => "one\ntwo",
  143. );
  144. $tests['Literal block chomping strip without 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 single trailing newline'] = array($expected, $yaml);
  158. $yaml = <<<'EOF'
  159. foo: |
  160. one
  161. two
  162. bar: |
  163. one
  164. two
  165. EOF;
  166. $expected = array(
  167. 'foo' => "one\ntwo\n",
  168. 'bar' => "one\ntwo\n",
  169. );
  170. $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  171. $yaml = <<<'EOF'
  172. foo:
  173. - bar: |
  174. one
  175. two
  176. EOF;
  177. $expected = array(
  178. 'foo' => array(
  179. array(
  180. 'bar' => "one\n\ntwo",
  181. ),
  182. ),
  183. );
  184. $tests['Literal block chomping clip with embedded blank line inside unindented collection'] = 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",
  196. );
  197. $tests['Literal block chomping clip without 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",
  208. 'bar' => "one\ntwo\n",
  209. );
  210. $tests['Literal block chomping keep with single trailing newline'] = 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\n",
  221. 'bar' => "one\ntwo\n\n",
  222. );
  223. $tests['Literal block chomping keep with multiple trailing newlines'] = 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\ntwo\n",
  234. 'bar' => "one\ntwo",
  235. );
  236. $tests['Literal block chomping keep without 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 single trailing newline'] = 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 with multiple trailing newlines'] = 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',
  273. 'bar' => 'one two',
  274. );
  275. $tests['Folded block chomping strip without 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 single trailing newline'] = 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\n",
  300. );
  301. $tests['Folded block chomping clip with multiple trailing newlines'] = 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',
  313. );
  314. $tests['Folded block chomping clip without 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",
  325. 'bar' => "one two\n",
  326. );
  327. $tests['Folded block chomping keep with single trailing newline'] = 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\n",
  338. 'bar' => "one two\n\n",
  339. );
  340. $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  341. $yaml = <<<'EOF'
  342. foo: >+
  343. one
  344. two
  345. bar: >+
  346. one
  347. two
  348. EOF;
  349. $expected = array(
  350. 'foo' => "one two\n",
  351. 'bar' => 'one two',
  352. );
  353. $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);
  354. return $tests;
  355. }
  356. /**
  357. * @dataProvider getBlockChompingTests
  358. */
  359. public function testBlockChomping($expected, $yaml)
  360. {
  361. $this->assertSame($expected, $this->parser->parse($yaml));
  362. }
  363. /**
  364. * Regression test for issue #7989.
  365. *
  366. * @see https://github.com/symfony/symfony/issues/7989
  367. */
  368. public function testBlockLiteralWithLeadingNewlines()
  369. {
  370. $yaml = <<<'EOF'
  371. foo: |-
  372. bar
  373. EOF;
  374. $expected = array(
  375. 'foo' => "\n\nbar",
  376. );
  377. $this->assertSame($expected, $this->parser->parse($yaml));
  378. }
  379. public function testObjectSupportEnabled()
  380. {
  381. $input = <<<'EOF'
  382. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  383. bar: 1
  384. EOF;
  385. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  386. }
  387. /**
  388. * @group legacy
  389. */
  390. public function testObjectSupportEnabledPassingTrue()
  391. {
  392. $input = <<<'EOF'
  393. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  394. bar: 1
  395. EOF;
  396. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
  397. }
  398. /**
  399. * @group legacy
  400. */
  401. public function testObjectSupportEnabledWithDeprecatedTag()
  402. {
  403. $input = <<<'EOF'
  404. foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  405. bar: 1
  406. EOF;
  407. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  408. }
  409. /**
  410. * @dataProvider invalidDumpedObjectProvider
  411. */
  412. public function testObjectSupportDisabledButNoExceptions($input)
  413. {
  414. $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
  415. }
  416. /**
  417. * @dataProvider getObjectForMapTests
  418. */
  419. public function testObjectForMap($yaml, $expected)
  420. {
  421. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  422. }
  423. /**
  424. * @group legacy
  425. * @dataProvider getObjectForMapTests
  426. */
  427. public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected)
  428. {
  429. $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
  430. }
  431. public function getObjectForMapTests()
  432. {
  433. $tests = array();
  434. $yaml = <<<'EOF'
  435. foo:
  436. fiz: [cat]
  437. EOF;
  438. $expected = new \stdClass();
  439. $expected->foo = new \stdClass();
  440. $expected->foo->fiz = array('cat');
  441. $tests['mapping'] = array($yaml, $expected);
  442. $yaml = '{ "foo": "bar", "fiz": "cat" }';
  443. $expected = new \stdClass();
  444. $expected->foo = 'bar';
  445. $expected->fiz = 'cat';
  446. $tests['inline-mapping'] = array($yaml, $expected);
  447. $yaml = "foo: bar\nbaz: foobar";
  448. $expected = new \stdClass();
  449. $expected->foo = 'bar';
  450. $expected->baz = 'foobar';
  451. $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected);
  452. $yaml = <<<'EOT'
  453. array:
  454. - key: one
  455. - key: two
  456. EOT;
  457. $expected = new \stdClass();
  458. $expected->array = array();
  459. $expected->array[0] = new \stdClass();
  460. $expected->array[0]->key = 'one';
  461. $expected->array[1] = new \stdClass();
  462. $expected->array[1]->key = 'two';
  463. $tests['nest-map-and-sequence'] = array($yaml, $expected);
  464. $yaml = <<<'YAML'
  465. map:
  466. 1: one
  467. 2: two
  468. YAML;
  469. $expected = new \stdClass();
  470. $expected->map = new \stdClass();
  471. $expected->map->{1} = 'one';
  472. $expected->map->{2} = 'two';
  473. $tests['numeric-keys'] = array($yaml, $expected);
  474. $yaml = <<<'YAML'
  475. map:
  476. 0: one
  477. 1: two
  478. YAML;
  479. $expected = new \stdClass();
  480. $expected->map = new \stdClass();
  481. $expected->map->{0} = 'one';
  482. $expected->map->{1} = 'two';
  483. $tests['zero-indexed-numeric-keys'] = array($yaml, $expected);
  484. return $tests;
  485. }
  486. /**
  487. * @dataProvider invalidDumpedObjectProvider
  488. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  489. */
  490. public function testObjectsSupportDisabledWithExceptions($yaml)
  491. {
  492. $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
  493. }
  494. /**
  495. * @group legacy
  496. * @dataProvider invalidDumpedObjectProvider
  497. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  498. */
  499. public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml)
  500. {
  501. $this->parser->parse($yaml, true);
  502. }
  503. public function invalidDumpedObjectProvider()
  504. {
  505. $yamlTag = <<<'EOF'
  506. foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  507. bar: 1
  508. EOF;
  509. $localTag = <<<'EOF'
  510. foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  511. bar: 1
  512. EOF;
  513. return array(
  514. 'yaml-tag' => array($yamlTag),
  515. 'local-tag' => array($localTag),
  516. );
  517. }
  518. /**
  519. * @requires extension iconv
  520. */
  521. public function testNonUtf8Exception()
  522. {
  523. $yamls = array(
  524. iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"),
  525. iconv('UTF-8', 'ISO-8859-15', "euro: '€'"),
  526. iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"),
  527. );
  528. foreach ($yamls as $yaml) {
  529. try {
  530. $this->parser->parse($yaml);
  531. $this->fail('charsets other than UTF-8 are rejected.');
  532. } catch (\Exception $e) {
  533. $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
  534. }
  535. }
  536. }
  537. /**
  538. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  539. */
  540. public function testUnindentedCollectionException()
  541. {
  542. $yaml = <<<'EOF'
  543. collection:
  544. -item1
  545. -item2
  546. -item3
  547. EOF;
  548. $this->parser->parse($yaml);
  549. }
  550. /**
  551. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  552. */
  553. public function testShortcutKeyUnindentedCollectionException()
  554. {
  555. $yaml = <<<'EOF'
  556. collection:
  557. - key: foo
  558. foo: bar
  559. EOF;
  560. $this->parser->parse($yaml);
  561. }
  562. /**
  563. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  564. * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/
  565. */
  566. public function testMultipleDocumentsNotSupportedException()
  567. {
  568. Yaml::parse(<<<'EOL'
  569. # Ranking of 1998 home runs
  570. ---
  571. - Mark McGwire
  572. - Sammy Sosa
  573. - Ken Griffey
  574. # Team ranking
  575. ---
  576. - Chicago Cubs
  577. - St Louis Cardinals
  578. EOL
  579. );
  580. }
  581. /**
  582. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  583. */
  584. public function testSequenceInAMapping()
  585. {
  586. Yaml::parse(<<<'EOF'
  587. yaml:
  588. hash: me
  589. - array stuff
  590. EOF
  591. );
  592. }
  593. public function testSequenceInMappingStartedBySingleDashLine()
  594. {
  595. $yaml = <<<'EOT'
  596. a:
  597. -
  598. b:
  599. -
  600. bar: baz
  601. - foo
  602. d: e
  603. EOT;
  604. $expected = array(
  605. 'a' => array(
  606. array(
  607. 'b' => array(
  608. array(
  609. 'bar' => 'baz',
  610. ),
  611. ),
  612. ),
  613. 'foo',
  614. ),
  615. 'd' => 'e',
  616. );
  617. $this->assertSame($expected, $this->parser->parse($yaml));
  618. }
  619. public function testSequenceFollowedByCommentEmbeddedInMapping()
  620. {
  621. $yaml = <<<'EOT'
  622. a:
  623. b:
  624. - c
  625. # comment
  626. d: e
  627. EOT;
  628. $expected = array(
  629. 'a' => array(
  630. 'b' => array('c'),
  631. 'd' => 'e',
  632. ),
  633. );
  634. $this->assertSame($expected, $this->parser->parse($yaml));
  635. }
  636. /**
  637. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  638. */
  639. public function testMappingInASequence()
  640. {
  641. Yaml::parse(<<<'EOF'
  642. yaml:
  643. - array stuff
  644. hash: me
  645. EOF
  646. );
  647. }
  648. /**
  649. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  650. * @expectedExceptionMessage missing colon
  651. */
  652. public function testScalarInSequence()
  653. {
  654. Yaml::parse(<<<'EOF'
  655. foo:
  656. - bar
  657. "missing colon"
  658. foo: bar
  659. EOF
  660. );
  661. }
  662. /**
  663. * > It is an error for two equal keys to appear in the same mapping node.
  664. * > In such a case the YAML processor may continue, ignoring the second
  665. * > `key: value` pair and issuing an appropriate warning. This strategy
  666. * > preserves a consistent information model for one-pass and random access
  667. * > applications.
  668. *
  669. * @see http://yaml.org/spec/1.2/spec.html#id2759572
  670. * @see http://yaml.org/spec/1.1/#id932806
  671. * @group legacy
  672. */
  673. public function testMappingDuplicateKeyBlock()
  674. {
  675. $input = <<<'EOD'
  676. parent:
  677. child: first
  678. child: duplicate
  679. parent:
  680. child: duplicate
  681. child: duplicate
  682. EOD;
  683. $expected = array(
  684. 'parent' => array(
  685. 'child' => 'first',
  686. ),
  687. );
  688. $this->assertSame($expected, Yaml::parse($input));
  689. }
  690. /**
  691. * @group legacy
  692. */
  693. public function testMappingDuplicateKeyFlow()
  694. {
  695. $input = <<<'EOD'
  696. parent: { child: first, child: duplicate }
  697. parent: { child: duplicate, child: duplicate }
  698. EOD;
  699. $expected = array(
  700. 'parent' => array(
  701. 'child' => 'first',
  702. ),
  703. );
  704. $this->assertSame($expected, Yaml::parse($input));
  705. }
  706. /**
  707. * @group legacy
  708. * @dataProvider getParseExceptionOnDuplicateData
  709. * @expectedDeprecation Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated %s.
  710. * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
  711. */
  712. public function testParseExceptionOnDuplicate($input, $duplicateKey, $lineNumber)
  713. {
  714. Yaml::parse($input);
  715. }
  716. public function getParseExceptionOnDuplicateData()
  717. {
  718. $tests = array();
  719. $yaml = <<<EOD
  720. parent: { child: first, child: duplicate }
  721. EOD;
  722. $tests[] = array($yaml, 'child', 1);
  723. $yaml = <<<EOD
  724. parent:
  725. child: first,
  726. child: duplicate
  727. EOD;
  728. $tests[] = array($yaml, 'child', 3);
  729. $yaml = <<<EOD
  730. parent: { child: foo }
  731. parent: { child: bar }
  732. EOD;
  733. $tests[] = array($yaml, 'parent', 2);
  734. $yaml = <<<EOD
  735. parent: { child_mapping: { value: bar}, child_mapping: { value: bar} }
  736. EOD;
  737. $tests[] = array($yaml, 'child_mapping', 1);
  738. $yaml = <<<EOD
  739. parent:
  740. child_mapping:
  741. value: bar
  742. child_mapping:
  743. value: bar
  744. EOD;
  745. $tests[] = array($yaml, 'child_mapping', 4);
  746. $yaml = <<<EOD
  747. parent: { child_sequence: ['key1', 'key2', 'key3'], child_sequence: ['key1', 'key2', 'key3'] }
  748. EOD;
  749. $tests[] = array($yaml, 'child_sequence', 1);
  750. $yaml = <<<EOD
  751. parent:
  752. child_sequence:
  753. - key1
  754. - key2
  755. - key3
  756. child_sequence:
  757. - key1
  758. - key2
  759. - key3
  760. EOD;
  761. $tests[] = array($yaml, 'child_sequence', 6);
  762. return $tests;
  763. }
  764. public function testEmptyValue()
  765. {
  766. $input = <<<'EOF'
  767. hash:
  768. EOF;
  769. $this->assertEquals(array('hash' => null), Yaml::parse($input));
  770. }
  771. public function testCommentAtTheRootIndent()
  772. {
  773. $this->assertEquals(array(
  774. 'services' => array(
  775. 'app.foo_service' => array(
  776. 'class' => 'Foo',
  777. ),
  778. 'app/bar_service' => array(
  779. 'class' => 'Bar',
  780. ),
  781. ),
  782. ), Yaml::parse(<<<'EOF'
  783. # comment 1
  784. services:
  785. # comment 2
  786. # comment 3
  787. app.foo_service:
  788. class: Foo
  789. # comment 4
  790. # comment 5
  791. app/bar_service:
  792. class: Bar
  793. EOF
  794. ));
  795. }
  796. public function testStringBlockWithComments()
  797. {
  798. $this->assertEquals(array('content' => <<<'EOT'
  799. # comment 1
  800. header
  801. # comment 2
  802. <body>
  803. <h1>title</h1>
  804. </body>
  805. footer # comment3
  806. EOT
  807. ), Yaml::parse(<<<'EOF'
  808. content: |
  809. # comment 1
  810. header
  811. # comment 2
  812. <body>
  813. <h1>title</h1>
  814. </body>
  815. footer # comment3
  816. EOF
  817. ));
  818. }
  819. public function testFoldedStringBlockWithComments()
  820. {
  821. $this->assertEquals(array(array('content' => <<<'EOT'
  822. # comment 1
  823. header
  824. # comment 2
  825. <body>
  826. <h1>title</h1>
  827. </body>
  828. footer # comment3
  829. EOT
  830. )), Yaml::parse(<<<'EOF'
  831. -
  832. content: |
  833. # comment 1
  834. header
  835. # comment 2
  836. <body>
  837. <h1>title</h1>
  838. </body>
  839. footer # comment3
  840. EOF
  841. ));
  842. }
  843. public function testNestedFoldedStringBlockWithComments()
  844. {
  845. $this->assertEquals(array(array(
  846. 'title' => 'some title',
  847. 'content' => <<<'EOT'
  848. # comment 1
  849. header
  850. # comment 2
  851. <body>
  852. <h1>title</h1>
  853. </body>
  854. footer # comment3
  855. EOT
  856. )), Yaml::parse(<<<'EOF'
  857. -
  858. title: some title
  859. content: |
  860. # comment 1
  861. header
  862. # comment 2
  863. <body>
  864. <h1>title</h1>
  865. </body>
  866. footer # comment3
  867. EOF
  868. ));
  869. }
  870. public function testReferenceResolvingInInlineStrings()
  871. {
  872. $this->assertEquals(array(
  873. 'var' => 'var-value',
  874. 'scalar' => 'var-value',
  875. 'list' => array('var-value'),
  876. 'list_in_list' => array(array('var-value')),
  877. 'map_in_list' => array(array('key' => 'var-value')),
  878. 'embedded_mapping' => array(array('key' => 'var-value')),
  879. 'map' => array('key' => 'var-value'),
  880. 'list_in_map' => array('key' => array('var-value')),
  881. 'map_in_map' => array('foo' => array('bar' => 'var-value')),
  882. ), Yaml::parse(<<<'EOF'
  883. var: &var var-value
  884. scalar: *var
  885. list: [ *var ]
  886. list_in_list: [[ *var ]]
  887. map_in_list: [ { key: *var } ]
  888. embedded_mapping: [ key: *var ]
  889. map: { key: *var }
  890. list_in_map: { key: [*var] }
  891. map_in_map: { foo: { bar: *var } }
  892. EOF
  893. ));
  894. }
  895. public function testYamlDirective()
  896. {
  897. $yaml = <<<'EOF'
  898. %YAML 1.2
  899. ---
  900. foo: 1
  901. bar: 2
  902. EOF;
  903. $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml));
  904. }
  905. public function testFloatKeys()
  906. {
  907. $yaml = <<<'EOF'
  908. foo:
  909. 1.2: "bar"
  910. 1.3: "baz"
  911. EOF;
  912. $expected = array(
  913. 'foo' => array(
  914. '1.2' => 'bar',
  915. '1.3' => 'baz',
  916. ),
  917. );
  918. $this->assertEquals($expected, $this->parser->parse($yaml));
  919. }
  920. /**
  921. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  922. * @expectedExceptionMessage A colon cannot be used in an unquoted mapping value
  923. */
  924. public function testColonInMappingValueException()
  925. {
  926. $yaml = <<<'EOF'
  927. foo: bar: baz
  928. EOF;
  929. $this->parser->parse($yaml);
  930. }
  931. public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
  932. {
  933. $yaml = <<<'EOT'
  934. foo:
  935. bar: foobar # Note: a comment after a colon
  936. EOT;
  937. $this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml));
  938. }
  939. /**
  940. * @dataProvider getCommentLikeStringInScalarBlockData
  941. */
  942. public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
  943. {
  944. $this->assertSame($expectedParserResult, $this->parser->parse($yaml));
  945. }
  946. public function getCommentLikeStringInScalarBlockData()
  947. {
  948. $tests = array();
  949. $yaml = <<<'EOT'
  950. pages:
  951. -
  952. title: some title
  953. content: |
  954. # comment 1
  955. header
  956. # comment 2
  957. <body>
  958. <h1>title</h1>
  959. </body>
  960. footer # comment3
  961. EOT;
  962. $expected = array(
  963. 'pages' => array(
  964. array(
  965. 'title' => 'some title',
  966. 'content' => <<<'EOT'
  967. # comment 1
  968. header
  969. # comment 2
  970. <body>
  971. <h1>title</h1>
  972. </body>
  973. footer # comment3
  974. EOT
  975. ,
  976. ),
  977. ),
  978. );
  979. $tests[] = array($yaml, $expected);
  980. $yaml = <<<'EOT'
  981. test: |
  982. foo
  983. # bar
  984. baz
  985. collection:
  986. - one: |
  987. foo
  988. # bar
  989. baz
  990. - two: |
  991. foo
  992. # bar
  993. baz
  994. EOT;
  995. $expected = array(
  996. 'test' => <<<'EOT'
  997. foo
  998. # bar
  999. baz
  1000. EOT
  1001. ,
  1002. 'collection' => array(
  1003. array(
  1004. 'one' => <<<'EOT'
  1005. foo
  1006. # bar
  1007. baz
  1008. EOT
  1009. ,
  1010. ),
  1011. array(
  1012. 'two' => <<<'EOT'
  1013. foo
  1014. # bar
  1015. baz
  1016. EOT
  1017. ,
  1018. ),
  1019. ),
  1020. );
  1021. $tests[] = array($yaml, $expected);
  1022. $yaml = <<<'EOT'
  1023. foo:
  1024. bar:
  1025. scalar-block: >
  1026. line1
  1027. line2>
  1028. baz:
  1029. # comment
  1030. foobar: ~
  1031. EOT;
  1032. $expected = array(
  1033. 'foo' => array(
  1034. 'bar' => array(
  1035. 'scalar-block' => "line1 line2>\n",
  1036. ),
  1037. 'baz' => array(
  1038. 'foobar' => null,
  1039. ),
  1040. ),
  1041. );
  1042. $tests[] = array($yaml, $expected);
  1043. $yaml = <<<'EOT'
  1044. a:
  1045. b: hello
  1046. # c: |
  1047. # first row
  1048. # second row
  1049. d: hello
  1050. EOT;
  1051. $expected = array(
  1052. 'a' => array(
  1053. 'b' => 'hello',
  1054. 'd' => 'hello',
  1055. ),
  1056. );
  1057. $tests[] = array($yaml, $expected);
  1058. return $tests;
  1059. }
  1060. public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
  1061. {
  1062. $yaml = <<<'EOT'
  1063. test: >
  1064. <h2>A heading</h2>
  1065. <ul>
  1066. <li>a list</li>
  1067. <li>may be a good example</li>
  1068. </ul>
  1069. EOT;
  1070. $this->assertSame(
  1071. array(
  1072. 'test' => <<<'EOT'
  1073. <h2>A heading</h2>
  1074. <ul> <li>a list</li> <li>may be a good example</li> </ul>
  1075. EOT
  1076. ,
  1077. ),
  1078. $this->parser->parse($yaml)
  1079. );
  1080. }
  1081. public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
  1082. {
  1083. $yaml = <<<'EOT'
  1084. test: >
  1085. <h2>A heading</h2>
  1086. <ul>
  1087. <li>a list</li>
  1088. <li>may be a good example</li>
  1089. </ul>
  1090. EOT;
  1091. $this->assertSame(
  1092. array(
  1093. 'test' => <<<'EOT'
  1094. <h2>A heading</h2>
  1095. <ul>
  1096. <li>a list</li>
  1097. <li>may be a good example</li>
  1098. </ul>
  1099. EOT
  1100. ,
  1101. ),
  1102. $this->parser->parse($yaml)
  1103. );
  1104. }
  1105. /**
  1106. * @dataProvider getBinaryData
  1107. */
  1108. public function testParseBinaryData($data)
  1109. {
  1110. $this->assertSame(array('data' => 'Hello world'), $this->parser->parse($data));
  1111. }
  1112. public function getBinaryData()
  1113. {
  1114. return array(
  1115. 'enclosed with double quotes' => array('data: !!binary "SGVsbG8gd29ybGQ="'),
  1116. 'enclosed with single quotes' => array("data: !!binary 'SGVsbG8gd29ybGQ='"),
  1117. 'containing spaces' => array('data: !!binary "SGVs bG8gd 29ybGQ="'),
  1118. 'in block scalar' => array(
  1119. <<<'EOT'
  1120. data: !!binary |
  1121. SGVsbG8gd29ybGQ=
  1122. EOT
  1123. ),
  1124. 'containing spaces in block scalar' => array(
  1125. <<<'EOT'
  1126. data: !!binary |
  1127. SGVs bG8gd 29ybGQ=
  1128. EOT
  1129. ),
  1130. );
  1131. }
  1132. /**
  1133. * @dataProvider getInvalidBinaryData
  1134. */
  1135. public function testParseInvalidBinaryData($data, $expectedMessage)
  1136. {
  1137. $this->setExpectedExceptionRegExp('\Symfony\Component\Yaml\Exception\ParseException', $expectedMessage);
  1138. $this->parser->parse($data);
  1139. }
  1140. public function getInvalidBinaryData()
  1141. {
  1142. return array(
  1143. '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\)/'),
  1144. 'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1145. 'too many equals characters' => array('data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1146. 'misplaced equals character' => array('data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1147. 'length not a multiple of four in block scalar' => array(
  1148. <<<'EOT'
  1149. data: !!binary |
  1150. SGVsbG8d29ybGQ=
  1151. EOT
  1152. ,
  1153. '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/',
  1154. ),
  1155. 'invalid characters in block scalar' => array(
  1156. <<<'EOT'
  1157. data: !!binary |
  1158. SGVsbG8#d29ybGQ=
  1159. EOT
  1160. ,
  1161. '/The base64 encoded data \(.*\) contains invalid characters/',
  1162. ),
  1163. 'too many equals characters in block scalar' => array(
  1164. <<<'EOT'
  1165. data: !!binary |
  1166. SGVsbG8gd29yb===
  1167. EOT
  1168. ,
  1169. '/The base64 encoded data \(.*\) contains invalid characters/',
  1170. ),
  1171. 'misplaced equals character in block scalar' => array(
  1172. <<<'EOT'
  1173. data: !!binary |
  1174. SGVsbG8gd29ybG=Q
  1175. EOT
  1176. ,
  1177. '/The base64 encoded data \(.*\) contains invalid characters/',
  1178. ),
  1179. );
  1180. }
  1181. public function testParseDateAsMappingValue()
  1182. {
  1183. $yaml = <<<'EOT'
  1184. date: 2002-12-14
  1185. EOT;
  1186. $expectedDate = new \DateTime();
  1187. $expectedDate->setTimeZone(new \DateTimeZone('UTC'));
  1188. $expectedDate->setDate(2002, 12, 14);
  1189. $expectedDate->setTime(0, 0, 0);
  1190. $this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
  1191. }
  1192. /**
  1193. * @param $lineNumber
  1194. * @param $yaml
  1195. * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
  1196. */
  1197. public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
  1198. {
  1199. $this->setExpectedException(
  1200. '\Symfony\Component\Yaml\Exception\ParseException',
  1201. sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber)
  1202. );
  1203. $this->parser->parse($yaml);
  1204. }
  1205. public function parserThrowsExceptionWithCorrectLineNumberProvider()
  1206. {
  1207. return array(
  1208. array(
  1209. 4,
  1210. <<<'YAML'
  1211. foo:
  1212. -
  1213. # bar
  1214. bar: "123",
  1215. YAML
  1216. ),
  1217. array(
  1218. 5,
  1219. <<<'YAML'
  1220. foo:
  1221. -
  1222. # bar
  1223. # bar
  1224. bar: "123",
  1225. YAML
  1226. ),
  1227. array(
  1228. 8,
  1229. <<<'YAML'
  1230. foo:
  1231. -
  1232. # foobar
  1233. baz: 123
  1234. bar:
  1235. -
  1236. # bar
  1237. bar: "123",
  1238. YAML
  1239. ),
  1240. array(
  1241. 10,
  1242. <<<'YAML'
  1243. foo:
  1244. -
  1245. # foobar
  1246. # foobar
  1247. baz: 123
  1248. bar:
  1249. -
  1250. # bar
  1251. # bar
  1252. bar: "123",
  1253. YAML
  1254. ),
  1255. );
  1256. }
  1257. public function testParseMultiLineQuotedString()
  1258. {
  1259. $yaml = <<<EOT
  1260. foo: "bar
  1261. baz
  1262. foobar
  1263. foo"
  1264. bar: baz
  1265. EOT;
  1266. $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
  1267. }
  1268. public function testParseMultiLineUnquotedString()
  1269. {
  1270. $yaml = <<<EOT
  1271. foo: bar
  1272. baz
  1273. foobar
  1274. foo
  1275. bar: baz
  1276. EOT;
  1277. $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
  1278. }
  1279. }
  1280. class B
  1281. {
  1282. public $b = 'foo';
  1283. }