菜谱项目

ParserTest.php 46KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982
  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\Exception\ParseException;
  13. use Symfony\Component\Yaml\Yaml;
  14. use Symfony\Component\Yaml\Parser;
  15. use Symfony\Component\Yaml\Tag\TaggedValue;
  16. class ParserTest extends TestCase
  17. {
  18. /** @var Parser */
  19. protected $parser;
  20. protected function setUp()
  21. {
  22. $this->parser = new Parser();
  23. }
  24. protected function tearDown()
  25. {
  26. $this->parser = null;
  27. }
  28. /**
  29. * @dataProvider getDataFormSpecifications
  30. */
  31. public function testSpecifications($expected, $yaml, $comment, $deprecated)
  32. {
  33. $deprecations = array();
  34. if ($deprecated) {
  35. set_error_handler(function ($type, $msg) use (&$deprecations) {
  36. if (E_USER_DEPRECATED !== $type) {
  37. restore_error_handler();
  38. if (class_exists('PHPUnit_Util_ErrorHandler')) {
  39. return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args());
  40. }
  41. return call_user_func_array('PHPUnit\Util\ErrorHandler::handleError', func_get_args());
  42. }
  43. $deprecations[] = $msg;
  44. });
  45. }
  46. $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
  47. if ($deprecated) {
  48. restore_error_handler();
  49. $this->assertCount(1, $deprecations);
  50. $this->assertContains('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0 on line 1.', $deprecations[0]);
  51. }
  52. }
  53. public function getDataFormSpecifications()
  54. {
  55. return $this->loadTestsFromFixtureFiles('index.yml');
  56. }
  57. /**
  58. * @dataProvider getNonStringMappingKeysData
  59. */
  60. public function testNonStringMappingKeys($expected, $yaml, $comment)
  61. {
  62. $this->assertSame($expected, var_export($this->parser->parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS), true), $comment);
  63. }
  64. public function getNonStringMappingKeysData()
  65. {
  66. return $this->loadTestsFromFixtureFiles('nonStringKeys.yml');
  67. }
  68. /**
  69. * @group legacy
  70. * @dataProvider getLegacyNonStringMappingKeysData
  71. */
  72. public function testLegacyNonStringMappingKeys($expected, $yaml, $comment)
  73. {
  74. $this->assertSame($expected, var_export($this->parser->parse($yaml), true), $comment);
  75. }
  76. public function getLegacyNonStringMappingKeysData()
  77. {
  78. return $this->loadTestsFromFixtureFiles('legacyNonStringKeys.yml');
  79. }
  80. public function testTabsInYaml()
  81. {
  82. // test tabs in YAML
  83. $yamls = array(
  84. "foo:\n bar",
  85. "foo:\n bar",
  86. "foo:\n bar",
  87. "foo:\n bar",
  88. );
  89. foreach ($yamls as $yaml) {
  90. try {
  91. $content = $this->parser->parse($yaml);
  92. $this->fail('YAML files must not contain tabs');
  93. } catch (\Exception $e) {
  94. $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs');
  95. $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');
  96. }
  97. }
  98. }
  99. public function testEndOfTheDocumentMarker()
  100. {
  101. $yaml = <<<'EOF'
  102. --- %YAML:1.0
  103. foo
  104. ...
  105. EOF;
  106. $this->assertEquals('foo', $this->parser->parse($yaml));
  107. }
  108. public function getBlockChompingTests()
  109. {
  110. $tests = array();
  111. $yaml = <<<'EOF'
  112. foo: |-
  113. one
  114. two
  115. bar: |-
  116. one
  117. two
  118. EOF;
  119. $expected = array(
  120. 'foo' => "one\ntwo",
  121. 'bar' => "one\ntwo",
  122. );
  123. $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml);
  124. $yaml = <<<'EOF'
  125. foo: |-
  126. one
  127. two
  128. bar: |-
  129. one
  130. two
  131. EOF;
  132. $expected = array(
  133. 'foo' => "one\ntwo",
  134. 'bar' => "one\ntwo",
  135. );
  136. $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  137. $yaml = <<<'EOF'
  138. {}
  139. EOF;
  140. $expected = array();
  141. $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = array($expected, $yaml);
  142. $yaml = <<<'EOF'
  143. foo: |-
  144. one
  145. two
  146. bar: |-
  147. one
  148. two
  149. EOF;
  150. $expected = array(
  151. 'foo' => "one\ntwo",
  152. 'bar' => "one\ntwo",
  153. );
  154. $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml);
  155. $yaml = <<<'EOF'
  156. foo: |
  157. one
  158. two
  159. bar: |
  160. one
  161. two
  162. EOF;
  163. $expected = array(
  164. 'foo' => "one\ntwo\n",
  165. 'bar' => "one\ntwo\n",
  166. );
  167. $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml);
  168. $yaml = <<<'EOF'
  169. foo: |
  170. one
  171. two
  172. bar: |
  173. one
  174. two
  175. EOF;
  176. $expected = array(
  177. 'foo' => "one\ntwo\n",
  178. 'bar' => "one\ntwo\n",
  179. );
  180. $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  181. $yaml = <<<'EOF'
  182. foo:
  183. - bar: |
  184. one
  185. two
  186. EOF;
  187. $expected = array(
  188. 'foo' => array(
  189. array(
  190. 'bar' => "one\n\ntwo",
  191. ),
  192. ),
  193. );
  194. $tests['Literal block chomping clip with embedded blank line inside unindented collection'] = array($expected, $yaml);
  195. $yaml = <<<'EOF'
  196. foo: |
  197. one
  198. two
  199. bar: |
  200. one
  201. two
  202. EOF;
  203. $expected = array(
  204. 'foo' => "one\ntwo\n",
  205. 'bar' => "one\ntwo",
  206. );
  207. $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml);
  208. $yaml = <<<'EOF'
  209. foo: |+
  210. one
  211. two
  212. bar: |+
  213. one
  214. two
  215. EOF;
  216. $expected = array(
  217. 'foo' => "one\ntwo\n",
  218. 'bar' => "one\ntwo\n",
  219. );
  220. $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml);
  221. $yaml = <<<'EOF'
  222. foo: |+
  223. one
  224. two
  225. bar: |+
  226. one
  227. two
  228. EOF;
  229. $expected = array(
  230. 'foo' => "one\ntwo\n\n",
  231. 'bar' => "one\ntwo\n\n",
  232. );
  233. $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  234. $yaml = <<<'EOF'
  235. foo: |+
  236. one
  237. two
  238. bar: |+
  239. one
  240. two
  241. EOF;
  242. $expected = array(
  243. 'foo' => "one\ntwo\n",
  244. 'bar' => "one\ntwo",
  245. );
  246. $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml);
  247. $yaml = <<<'EOF'
  248. foo: >-
  249. one
  250. two
  251. bar: >-
  252. one
  253. two
  254. EOF;
  255. $expected = array(
  256. 'foo' => 'one two',
  257. 'bar' => 'one two',
  258. );
  259. $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml);
  260. $yaml = <<<'EOF'
  261. foo: >-
  262. one
  263. two
  264. bar: >-
  265. one
  266. two
  267. EOF;
  268. $expected = array(
  269. 'foo' => 'one two',
  270. 'bar' => 'one two',
  271. );
  272. $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  273. $yaml = <<<'EOF'
  274. foo: >-
  275. one
  276. two
  277. bar: >-
  278. one
  279. two
  280. EOF;
  281. $expected = array(
  282. 'foo' => 'one two',
  283. 'bar' => 'one two',
  284. );
  285. $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml);
  286. $yaml = <<<'EOF'
  287. foo: >
  288. one
  289. two
  290. bar: >
  291. one
  292. two
  293. EOF;
  294. $expected = array(
  295. 'foo' => "one two\n",
  296. 'bar' => "one two\n",
  297. );
  298. $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml);
  299. $yaml = <<<'EOF'
  300. foo: >
  301. one
  302. two
  303. bar: >
  304. one
  305. two
  306. EOF;
  307. $expected = array(
  308. 'foo' => "one two\n",
  309. 'bar' => "one two\n",
  310. );
  311. $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  312. $yaml = <<<'EOF'
  313. foo: >
  314. one
  315. two
  316. bar: >
  317. one
  318. two
  319. EOF;
  320. $expected = array(
  321. 'foo' => "one two\n",
  322. 'bar' => 'one two',
  323. );
  324. $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml);
  325. $yaml = <<<'EOF'
  326. foo: >+
  327. one
  328. two
  329. bar: >+
  330. one
  331. two
  332. EOF;
  333. $expected = array(
  334. 'foo' => "one two\n",
  335. 'bar' => "one two\n",
  336. );
  337. $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml);
  338. $yaml = <<<'EOF'
  339. foo: >+
  340. one
  341. two
  342. bar: >+
  343. one
  344. two
  345. EOF;
  346. $expected = array(
  347. 'foo' => "one two\n\n",
  348. 'bar' => "one two\n\n",
  349. );
  350. $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  351. $yaml = <<<'EOF'
  352. foo: >+
  353. one
  354. two
  355. bar: >+
  356. one
  357. two
  358. EOF;
  359. $expected = array(
  360. 'foo' => "one two\n",
  361. 'bar' => 'one two',
  362. );
  363. $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);
  364. return $tests;
  365. }
  366. /**
  367. * @dataProvider getBlockChompingTests
  368. */
  369. public function testBlockChomping($expected, $yaml)
  370. {
  371. $this->assertSame($expected, $this->parser->parse($yaml));
  372. }
  373. /**
  374. * Regression test for issue #7989.
  375. *
  376. * @see https://github.com/symfony/symfony/issues/7989
  377. */
  378. public function testBlockLiteralWithLeadingNewlines()
  379. {
  380. $yaml = <<<'EOF'
  381. foo: |-
  382. bar
  383. EOF;
  384. $expected = array(
  385. 'foo' => "\n\nbar",
  386. );
  387. $this->assertSame($expected, $this->parser->parse($yaml));
  388. }
  389. public function testObjectSupportEnabled()
  390. {
  391. $input = <<<'EOF'
  392. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  393. bar: 1
  394. EOF;
  395. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  396. }
  397. /**
  398. * @group legacy
  399. */
  400. public function testObjectSupportEnabledPassingTrue()
  401. {
  402. $input = <<<'EOF'
  403. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  404. bar: 1
  405. EOF;
  406. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
  407. }
  408. /**
  409. * @group legacy
  410. */
  411. public function testObjectSupportEnabledWithDeprecatedTag()
  412. {
  413. $input = <<<'EOF'
  414. foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  415. bar: 1
  416. EOF;
  417. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  418. }
  419. /**
  420. * @dataProvider invalidDumpedObjectProvider
  421. */
  422. public function testObjectSupportDisabledButNoExceptions($input)
  423. {
  424. $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
  425. }
  426. /**
  427. * @dataProvider getObjectForMapTests
  428. */
  429. public function testObjectForMap($yaml, $expected, $explicitlyParseKeysAsStrings = false)
  430. {
  431. $flags = Yaml::PARSE_OBJECT_FOR_MAP;
  432. if ($explicitlyParseKeysAsStrings) {
  433. $flags |= Yaml::PARSE_KEYS_AS_STRINGS;
  434. }
  435. $this->assertEquals($expected, $this->parser->parse($yaml, $flags));
  436. }
  437. /**
  438. * @group legacy
  439. * @dataProvider getObjectForMapTests
  440. */
  441. public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected)
  442. {
  443. $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
  444. }
  445. public function getObjectForMapTests()
  446. {
  447. $tests = array();
  448. $yaml = <<<'EOF'
  449. foo:
  450. fiz: [cat]
  451. EOF;
  452. $expected = new \stdClass();
  453. $expected->foo = new \stdClass();
  454. $expected->foo->fiz = array('cat');
  455. $tests['mapping'] = array($yaml, $expected);
  456. $yaml = '{ "foo": "bar", "fiz": "cat" }';
  457. $expected = new \stdClass();
  458. $expected->foo = 'bar';
  459. $expected->fiz = 'cat';
  460. $tests['inline-mapping'] = array($yaml, $expected);
  461. $yaml = "foo: bar\nbaz: foobar";
  462. $expected = new \stdClass();
  463. $expected->foo = 'bar';
  464. $expected->baz = 'foobar';
  465. $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected);
  466. $yaml = <<<'EOT'
  467. array:
  468. - key: one
  469. - key: two
  470. EOT;
  471. $expected = new \stdClass();
  472. $expected->array = array();
  473. $expected->array[0] = new \stdClass();
  474. $expected->array[0]->key = 'one';
  475. $expected->array[1] = new \stdClass();
  476. $expected->array[1]->key = 'two';
  477. $tests['nest-map-and-sequence'] = array($yaml, $expected);
  478. $yaml = <<<'YAML'
  479. map:
  480. 1: one
  481. 2: two
  482. YAML;
  483. $expected = new \stdClass();
  484. $expected->map = new \stdClass();
  485. $expected->map->{1} = 'one';
  486. $expected->map->{2} = 'two';
  487. $tests['numeric-keys'] = array($yaml, $expected, true);
  488. $yaml = <<<'YAML'
  489. map:
  490. 0: one
  491. 1: two
  492. YAML;
  493. $expected = new \stdClass();
  494. $expected->map = new \stdClass();
  495. $expected->map->{0} = 'one';
  496. $expected->map->{1} = 'two';
  497. $tests['zero-indexed-numeric-keys'] = array($yaml, $expected, true);
  498. return $tests;
  499. }
  500. /**
  501. * @dataProvider invalidDumpedObjectProvider
  502. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  503. */
  504. public function testObjectsSupportDisabledWithExceptions($yaml)
  505. {
  506. $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
  507. }
  508. public function testCanParseContentWithTrailingSpaces()
  509. {
  510. $yaml = "items: \n foo: bar";
  511. $expected = array(
  512. 'items' => array('foo' => 'bar'),
  513. );
  514. $this->assertSame($expected, $this->parser->parse($yaml));
  515. }
  516. /**
  517. * @group legacy
  518. * @dataProvider invalidDumpedObjectProvider
  519. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  520. */
  521. public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml)
  522. {
  523. $this->parser->parse($yaml, true);
  524. }
  525. public function invalidDumpedObjectProvider()
  526. {
  527. $yamlTag = <<<'EOF'
  528. foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  529. bar: 1
  530. EOF;
  531. $localTag = <<<'EOF'
  532. foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  533. bar: 1
  534. EOF;
  535. return array(
  536. 'yaml-tag' => array($yamlTag),
  537. 'local-tag' => array($localTag),
  538. );
  539. }
  540. /**
  541. * @requires extension iconv
  542. */
  543. public function testNonUtf8Exception()
  544. {
  545. $yamls = array(
  546. iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"),
  547. iconv('UTF-8', 'ISO-8859-15', "euro: '€'"),
  548. iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"),
  549. );
  550. foreach ($yamls as $yaml) {
  551. try {
  552. $this->parser->parse($yaml);
  553. $this->fail('charsets other than UTF-8 are rejected.');
  554. } catch (\Exception $e) {
  555. $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
  556. }
  557. }
  558. }
  559. /**
  560. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  561. */
  562. public function testUnindentedCollectionException()
  563. {
  564. $yaml = <<<'EOF'
  565. collection:
  566. -item1
  567. -item2
  568. -item3
  569. EOF;
  570. $this->parser->parse($yaml);
  571. }
  572. /**
  573. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  574. */
  575. public function testShortcutKeyUnindentedCollectionException()
  576. {
  577. $yaml = <<<'EOF'
  578. collection:
  579. - key: foo
  580. foo: bar
  581. EOF;
  582. $this->parser->parse($yaml);
  583. }
  584. /**
  585. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  586. * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/
  587. */
  588. public function testMultipleDocumentsNotSupportedException()
  589. {
  590. Yaml::parse(<<<'EOL'
  591. # Ranking of 1998 home runs
  592. ---
  593. - Mark McGwire
  594. - Sammy Sosa
  595. - Ken Griffey
  596. # Team ranking
  597. ---
  598. - Chicago Cubs
  599. - St Louis Cardinals
  600. EOL
  601. );
  602. }
  603. /**
  604. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  605. */
  606. public function testSequenceInAMapping()
  607. {
  608. Yaml::parse(<<<'EOF'
  609. yaml:
  610. hash: me
  611. - array stuff
  612. EOF
  613. );
  614. }
  615. public function testSequenceInMappingStartedBySingleDashLine()
  616. {
  617. $yaml = <<<'EOT'
  618. a:
  619. -
  620. b:
  621. -
  622. bar: baz
  623. - foo
  624. d: e
  625. EOT;
  626. $expected = array(
  627. 'a' => array(
  628. array(
  629. 'b' => array(
  630. array(
  631. 'bar' => 'baz',
  632. ),
  633. ),
  634. ),
  635. 'foo',
  636. ),
  637. 'd' => 'e',
  638. );
  639. $this->assertSame($expected, $this->parser->parse($yaml));
  640. }
  641. public function testSequenceFollowedByCommentEmbeddedInMapping()
  642. {
  643. $yaml = <<<'EOT'
  644. a:
  645. b:
  646. - c
  647. # comment
  648. d: e
  649. EOT;
  650. $expected = array(
  651. 'a' => array(
  652. 'b' => array('c'),
  653. 'd' => 'e',
  654. ),
  655. );
  656. $this->assertSame($expected, $this->parser->parse($yaml));
  657. }
  658. /**
  659. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  660. */
  661. public function testMappingInASequence()
  662. {
  663. Yaml::parse(<<<'EOF'
  664. yaml:
  665. - array stuff
  666. hash: me
  667. EOF
  668. );
  669. }
  670. /**
  671. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  672. * @expectedExceptionMessage missing colon
  673. */
  674. public function testScalarInSequence()
  675. {
  676. Yaml::parse(<<<'EOF'
  677. foo:
  678. - bar
  679. "missing colon"
  680. foo: bar
  681. EOF
  682. );
  683. }
  684. /**
  685. * > It is an error for two equal keys to appear in the same mapping node.
  686. * > In such a case the YAML processor may continue, ignoring the second
  687. * > `key: value` pair and issuing an appropriate warning. This strategy
  688. * > preserves a consistent information model for one-pass and random access
  689. * > applications.
  690. *
  691. * @see http://yaml.org/spec/1.2/spec.html#id2759572
  692. * @see http://yaml.org/spec/1.1/#id932806
  693. * @group legacy
  694. */
  695. public function testMappingDuplicateKeyBlock()
  696. {
  697. $input = <<<'EOD'
  698. parent:
  699. child: first
  700. child: duplicate
  701. parent:
  702. child: duplicate
  703. child: duplicate
  704. EOD;
  705. $expected = array(
  706. 'parent' => array(
  707. 'child' => 'first',
  708. ),
  709. );
  710. $this->assertSame($expected, Yaml::parse($input));
  711. }
  712. /**
  713. * @group legacy
  714. */
  715. public function testMappingDuplicateKeyFlow()
  716. {
  717. $input = <<<'EOD'
  718. parent: { child: first, child: duplicate }
  719. parent: { child: duplicate, child: duplicate }
  720. EOD;
  721. $expected = array(
  722. 'parent' => array(
  723. 'child' => 'first',
  724. ),
  725. );
  726. $this->assertSame($expected, Yaml::parse($input));
  727. }
  728. /**
  729. * @group legacy
  730. * @dataProvider getParseExceptionOnDuplicateData
  731. * @expectedDeprecation Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated %s and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.
  732. * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
  733. */
  734. public function testParseExceptionOnDuplicate($input, $duplicateKey, $lineNumber)
  735. {
  736. Yaml::parse($input);
  737. }
  738. public function getParseExceptionOnDuplicateData()
  739. {
  740. $tests = array();
  741. $yaml = <<<EOD
  742. parent: { child: first, child: duplicate }
  743. EOD;
  744. $tests[] = array($yaml, 'child', 1);
  745. $yaml = <<<EOD
  746. parent:
  747. child: first,
  748. child: duplicate
  749. EOD;
  750. $tests[] = array($yaml, 'child', 3);
  751. $yaml = <<<EOD
  752. parent: { child: foo }
  753. parent: { child: bar }
  754. EOD;
  755. $tests[] = array($yaml, 'parent', 2);
  756. $yaml = <<<EOD
  757. parent: { child_mapping: { value: bar}, child_mapping: { value: bar} }
  758. EOD;
  759. $tests[] = array($yaml, 'child_mapping', 1);
  760. $yaml = <<<EOD
  761. parent:
  762. child_mapping:
  763. value: bar
  764. child_mapping:
  765. value: bar
  766. EOD;
  767. $tests[] = array($yaml, 'child_mapping', 4);
  768. $yaml = <<<EOD
  769. parent: { child_sequence: ['key1', 'key2', 'key3'], child_sequence: ['key1', 'key2', 'key3'] }
  770. EOD;
  771. $tests[] = array($yaml, 'child_sequence', 1);
  772. $yaml = <<<EOD
  773. parent:
  774. child_sequence:
  775. - key1
  776. - key2
  777. - key3
  778. child_sequence:
  779. - key1
  780. - key2
  781. - key3
  782. EOD;
  783. $tests[] = array($yaml, 'child_sequence', 6);
  784. return $tests;
  785. }
  786. public function testEmptyValue()
  787. {
  788. $input = <<<'EOF'
  789. hash:
  790. EOF;
  791. $this->assertEquals(array('hash' => null), Yaml::parse($input));
  792. }
  793. public function testCommentAtTheRootIndent()
  794. {
  795. $this->assertEquals(array(
  796. 'services' => array(
  797. 'app.foo_service' => array(
  798. 'class' => 'Foo',
  799. ),
  800. 'app/bar_service' => array(
  801. 'class' => 'Bar',
  802. ),
  803. ),
  804. ), Yaml::parse(<<<'EOF'
  805. # comment 1
  806. services:
  807. # comment 2
  808. # comment 3
  809. app.foo_service:
  810. class: Foo
  811. # comment 4
  812. # comment 5
  813. app/bar_service:
  814. class: Bar
  815. EOF
  816. ));
  817. }
  818. public function testStringBlockWithComments()
  819. {
  820. $this->assertEquals(array('content' => <<<'EOT'
  821. # comment 1
  822. header
  823. # comment 2
  824. <body>
  825. <h1>title</h1>
  826. </body>
  827. footer # comment3
  828. EOT
  829. ), Yaml::parse(<<<'EOF'
  830. content: |
  831. # comment 1
  832. header
  833. # comment 2
  834. <body>
  835. <h1>title</h1>
  836. </body>
  837. footer # comment3
  838. EOF
  839. ));
  840. }
  841. public function testFoldedStringBlockWithComments()
  842. {
  843. $this->assertEquals(array(array('content' => <<<'EOT'
  844. # comment 1
  845. header
  846. # comment 2
  847. <body>
  848. <h1>title</h1>
  849. </body>
  850. footer # comment3
  851. EOT
  852. )), Yaml::parse(<<<'EOF'
  853. -
  854. content: |
  855. # comment 1
  856. header
  857. # comment 2
  858. <body>
  859. <h1>title</h1>
  860. </body>
  861. footer # comment3
  862. EOF
  863. ));
  864. }
  865. public function testNestedFoldedStringBlockWithComments()
  866. {
  867. $this->assertEquals(array(array(
  868. 'title' => 'some title',
  869. 'content' => <<<'EOT'
  870. # comment 1
  871. header
  872. # comment 2
  873. <body>
  874. <h1>title</h1>
  875. </body>
  876. footer # comment3
  877. EOT
  878. )), Yaml::parse(<<<'EOF'
  879. -
  880. title: some title
  881. content: |
  882. # comment 1
  883. header
  884. # comment 2
  885. <body>
  886. <h1>title</h1>
  887. </body>
  888. footer # comment3
  889. EOF
  890. ));
  891. }
  892. public function testReferenceResolvingInInlineStrings()
  893. {
  894. $this->assertEquals(array(
  895. 'var' => 'var-value',
  896. 'scalar' => 'var-value',
  897. 'list' => array('var-value'),
  898. 'list_in_list' => array(array('var-value')),
  899. 'map_in_list' => array(array('key' => 'var-value')),
  900. 'embedded_mapping' => array(array('key' => 'var-value')),
  901. 'map' => array('key' => 'var-value'),
  902. 'list_in_map' => array('key' => array('var-value')),
  903. 'map_in_map' => array('foo' => array('bar' => 'var-value')),
  904. ), Yaml::parse(<<<'EOF'
  905. var: &var var-value
  906. scalar: *var
  907. list: [ *var ]
  908. list_in_list: [[ *var ]]
  909. map_in_list: [ { key: *var } ]
  910. embedded_mapping: [ key: *var ]
  911. map: { key: *var }
  912. list_in_map: { key: [*var] }
  913. map_in_map: { foo: { bar: *var } }
  914. EOF
  915. ));
  916. }
  917. public function testYamlDirective()
  918. {
  919. $yaml = <<<'EOF'
  920. %YAML 1.2
  921. ---
  922. foo: 1
  923. bar: 2
  924. EOF;
  925. $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml));
  926. }
  927. /**
  928. * @group legacy
  929. * @expectedDeprecation Implicit casting of numeric key to string is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 2.
  930. */
  931. public function testFloatKeys()
  932. {
  933. $yaml = <<<'EOF'
  934. foo:
  935. 1.2: "bar"
  936. 1.3: "baz"
  937. EOF;
  938. $expected = array(
  939. 'foo' => array(
  940. '1.2' => 'bar',
  941. '1.3' => 'baz',
  942. ),
  943. );
  944. $this->assertEquals($expected, $this->parser->parse($yaml));
  945. }
  946. /**
  947. * @group legacy
  948. * @expectedDeprecation Implicit casting of non-string key to string is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1.
  949. */
  950. public function testBooleanKeys()
  951. {
  952. $yaml = <<<'EOF'
  953. true: foo
  954. false: bar
  955. EOF;
  956. $expected = array(
  957. 1 => 'foo',
  958. 0 => 'bar',
  959. );
  960. $this->assertEquals($expected, $this->parser->parse($yaml));
  961. }
  962. public function testExplicitStringCastingOfFloatKeys()
  963. {
  964. $yaml = <<<'EOF'
  965. foo:
  966. 1.2: "bar"
  967. 1.3: "baz"
  968. EOF;
  969. $expected = array(
  970. 'foo' => array(
  971. '1.2' => 'bar',
  972. '1.3' => 'baz',
  973. ),
  974. );
  975. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS));
  976. }
  977. public function testExplicitStringCastingOfBooleanKeys()
  978. {
  979. $yaml = <<<'EOF'
  980. true: foo
  981. false: bar
  982. EOF;
  983. $expected = array(
  984. 'true' => 'foo',
  985. 'false' => 'bar',
  986. );
  987. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS));
  988. }
  989. /**
  990. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  991. * @expectedExceptionMessage A colon cannot be used in an unquoted mapping value
  992. */
  993. public function testColonInMappingValueException()
  994. {
  995. $yaml = <<<'EOF'
  996. foo: bar: baz
  997. EOF;
  998. $this->parser->parse($yaml);
  999. }
  1000. public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
  1001. {
  1002. $yaml = <<<'EOT'
  1003. foo:
  1004. bar: foobar # Note: a comment after a colon
  1005. EOT;
  1006. $this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml));
  1007. }
  1008. /**
  1009. * @dataProvider getCommentLikeStringInScalarBlockData
  1010. */
  1011. public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
  1012. {
  1013. $this->assertSame($expectedParserResult, $this->parser->parse($yaml));
  1014. }
  1015. public function getCommentLikeStringInScalarBlockData()
  1016. {
  1017. $tests = array();
  1018. $yaml = <<<'EOT'
  1019. pages:
  1020. -
  1021. title: some title
  1022. content: |
  1023. # comment 1
  1024. header
  1025. # comment 2
  1026. <body>
  1027. <h1>title</h1>
  1028. </body>
  1029. footer # comment3
  1030. EOT;
  1031. $expected = array(
  1032. 'pages' => array(
  1033. array(
  1034. 'title' => 'some title',
  1035. 'content' => <<<'EOT'
  1036. # comment 1
  1037. header
  1038. # comment 2
  1039. <body>
  1040. <h1>title</h1>
  1041. </body>
  1042. footer # comment3
  1043. EOT
  1044. ,
  1045. ),
  1046. ),
  1047. );
  1048. $tests[] = array($yaml, $expected);
  1049. $yaml = <<<'EOT'
  1050. test: |
  1051. foo
  1052. # bar
  1053. baz
  1054. collection:
  1055. - one: |
  1056. foo
  1057. # bar
  1058. baz
  1059. - two: |
  1060. foo
  1061. # bar
  1062. baz
  1063. EOT;
  1064. $expected = array(
  1065. 'test' => <<<'EOT'
  1066. foo
  1067. # bar
  1068. baz
  1069. EOT
  1070. ,
  1071. 'collection' => array(
  1072. array(
  1073. 'one' => <<<'EOT'
  1074. foo
  1075. # bar
  1076. baz
  1077. EOT
  1078. ,
  1079. ),
  1080. array(
  1081. 'two' => <<<'EOT'
  1082. foo
  1083. # bar
  1084. baz
  1085. EOT
  1086. ,
  1087. ),
  1088. ),
  1089. );
  1090. $tests[] = array($yaml, $expected);
  1091. $yaml = <<<'EOT'
  1092. foo:
  1093. bar:
  1094. scalar-block: >
  1095. line1
  1096. line2>
  1097. baz:
  1098. # comment
  1099. foobar: ~
  1100. EOT;
  1101. $expected = array(
  1102. 'foo' => array(
  1103. 'bar' => array(
  1104. 'scalar-block' => "line1 line2>\n",
  1105. ),
  1106. 'baz' => array(
  1107. 'foobar' => null,
  1108. ),
  1109. ),
  1110. );
  1111. $tests[] = array($yaml, $expected);
  1112. $yaml = <<<'EOT'
  1113. a:
  1114. b: hello
  1115. # c: |
  1116. # first row
  1117. # second row
  1118. d: hello
  1119. EOT;
  1120. $expected = array(
  1121. 'a' => array(
  1122. 'b' => 'hello',
  1123. 'd' => 'hello',
  1124. ),
  1125. );
  1126. $tests[] = array($yaml, $expected);
  1127. return $tests;
  1128. }
  1129. public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
  1130. {
  1131. $yaml = <<<'EOT'
  1132. test: >
  1133. <h2>A heading</h2>
  1134. <ul>
  1135. <li>a list</li>
  1136. <li>may be a good example</li>
  1137. </ul>
  1138. EOT;
  1139. $this->assertSame(
  1140. array(
  1141. 'test' => <<<'EOT'
  1142. <h2>A heading</h2>
  1143. <ul> <li>a list</li> <li>may be a good example</li> </ul>
  1144. EOT
  1145. ,
  1146. ),
  1147. $this->parser->parse($yaml)
  1148. );
  1149. }
  1150. public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
  1151. {
  1152. $yaml = <<<'EOT'
  1153. test: >
  1154. <h2>A heading</h2>
  1155. <ul>
  1156. <li>a list</li>
  1157. <li>may be a good example</li>
  1158. </ul>
  1159. EOT;
  1160. $this->assertSame(
  1161. array(
  1162. 'test' => <<<'EOT'
  1163. <h2>A heading</h2>
  1164. <ul>
  1165. <li>a list</li>
  1166. <li>may be a good example</li>
  1167. </ul>
  1168. EOT
  1169. ,
  1170. ),
  1171. $this->parser->parse($yaml)
  1172. );
  1173. }
  1174. /**
  1175. * @dataProvider getBinaryData
  1176. */
  1177. public function testParseBinaryData($data)
  1178. {
  1179. $this->assertSame(array('data' => 'Hello world'), $this->parser->parse($data));
  1180. }
  1181. public function getBinaryData()
  1182. {
  1183. return array(
  1184. 'enclosed with double quotes' => array('data: !!binary "SGVsbG8gd29ybGQ="'),
  1185. 'enclosed with single quotes' => array("data: !!binary 'SGVsbG8gd29ybGQ='"),
  1186. 'containing spaces' => array('data: !!binary "SGVs bG8gd 29ybGQ="'),
  1187. 'in block scalar' => array(
  1188. <<<'EOT'
  1189. data: !!binary |
  1190. SGVsbG8gd29ybGQ=
  1191. EOT
  1192. ),
  1193. 'containing spaces in block scalar' => array(
  1194. <<<'EOT'
  1195. data: !!binary |
  1196. SGVs bG8gd 29ybGQ=
  1197. EOT
  1198. ),
  1199. );
  1200. }
  1201. /**
  1202. * @dataProvider getInvalidBinaryData
  1203. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  1204. */
  1205. public function testParseInvalidBinaryData($data, $expectedMessage)
  1206. {
  1207. if (method_exists($this, 'expectException')) {
  1208. $this->expectExceptionMessageRegExp($expectedMessage);
  1209. } else {
  1210. $this->setExpectedExceptionRegExp(ParseException::class, $expectedMessage);
  1211. }
  1212. $this->parser->parse($data);
  1213. }
  1214. public function getInvalidBinaryData()
  1215. {
  1216. return array(
  1217. '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\)/'),
  1218. 'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1219. 'too many equals characters' => array('data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1220. 'misplaced equals character' => array('data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
  1221. 'length not a multiple of four in block scalar' => array(
  1222. <<<'EOT'
  1223. data: !!binary |
  1224. SGVsbG8d29ybGQ=
  1225. EOT
  1226. ,
  1227. '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/',
  1228. ),
  1229. 'invalid characters in block scalar' => array(
  1230. <<<'EOT'
  1231. data: !!binary |
  1232. SGVsbG8#d29ybGQ=
  1233. EOT
  1234. ,
  1235. '/The base64 encoded data \(.*\) contains invalid characters/',
  1236. ),
  1237. 'too many equals characters in block scalar' => array(
  1238. <<<'EOT'
  1239. data: !!binary |
  1240. SGVsbG8gd29yb===
  1241. EOT
  1242. ,
  1243. '/The base64 encoded data \(.*\) contains invalid characters/',
  1244. ),
  1245. 'misplaced equals character in block scalar' => array(
  1246. <<<'EOT'
  1247. data: !!binary |
  1248. SGVsbG8gd29ybG=Q
  1249. EOT
  1250. ,
  1251. '/The base64 encoded data \(.*\) contains invalid characters/',
  1252. ),
  1253. );
  1254. }
  1255. public function testParseDateAsMappingValue()
  1256. {
  1257. $yaml = <<<'EOT'
  1258. date: 2002-12-14
  1259. EOT;
  1260. $expectedDate = new \DateTime();
  1261. $expectedDate->setTimeZone(new \DateTimeZone('UTC'));
  1262. $expectedDate->setDate(2002, 12, 14);
  1263. $expectedDate->setTime(0, 0, 0);
  1264. $this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
  1265. }
  1266. /**
  1267. * @param $lineNumber
  1268. * @param $yaml
  1269. * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
  1270. */
  1271. public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
  1272. {
  1273. if (method_exists($this, 'expectException')) {
  1274. $this->expectException('\Symfony\Component\Yaml\Exception\ParseException');
  1275. $this->expectExceptionMessage(sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
  1276. } else {
  1277. $this->setExpectedException('\Symfony\Component\Yaml\Exception\ParseException', sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
  1278. }
  1279. $this->parser->parse($yaml);
  1280. }
  1281. public function parserThrowsExceptionWithCorrectLineNumberProvider()
  1282. {
  1283. return array(
  1284. array(
  1285. 4,
  1286. <<<'YAML'
  1287. foo:
  1288. -
  1289. # bar
  1290. bar: "123",
  1291. YAML
  1292. ),
  1293. array(
  1294. 5,
  1295. <<<'YAML'
  1296. foo:
  1297. -
  1298. # bar
  1299. # bar
  1300. bar: "123",
  1301. YAML
  1302. ),
  1303. array(
  1304. 8,
  1305. <<<'YAML'
  1306. foo:
  1307. -
  1308. # foobar
  1309. baz: 123
  1310. bar:
  1311. -
  1312. # bar
  1313. bar: "123",
  1314. YAML
  1315. ),
  1316. array(
  1317. 10,
  1318. <<<'YAML'
  1319. foo:
  1320. -
  1321. # foobar
  1322. # foobar
  1323. baz: 123
  1324. bar:
  1325. -
  1326. # bar
  1327. # bar
  1328. bar: "123",
  1329. YAML
  1330. ),
  1331. );
  1332. }
  1333. public function testParseMultiLineQuotedString()
  1334. {
  1335. $yaml = <<<EOT
  1336. foo: "bar
  1337. baz
  1338. foobar
  1339. foo"
  1340. bar: baz
  1341. EOT;
  1342. $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
  1343. }
  1344. public function testMultiLineQuotedStringWithTrailingBackslash()
  1345. {
  1346. $yaml = <<<YAML
  1347. foobar:
  1348. "foo\
  1349. bar"
  1350. YAML;
  1351. $this->assertSame(array('foobar' => 'foobar'), $this->parser->parse($yaml));
  1352. }
  1353. public function testParseMultiLineUnquotedString()
  1354. {
  1355. $yaml = <<<EOT
  1356. foo: bar
  1357. baz
  1358. foobar
  1359. foo
  1360. bar: baz
  1361. EOT;
  1362. $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
  1363. }
  1364. public function testParseMultiLineString()
  1365. {
  1366. $this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
  1367. }
  1368. /**
  1369. * @dataProvider multiLineDataProvider
  1370. */
  1371. public function testParseMultiLineMappingValue($yaml, $expected, $parseError)
  1372. {
  1373. $this->assertEquals($expected, $this->parser->parse($yaml));
  1374. }
  1375. public function multiLineDataProvider()
  1376. {
  1377. $tests = array();
  1378. $yaml = <<<'EOF'
  1379. foo:
  1380. - bar:
  1381. one
  1382. two
  1383. three
  1384. EOF;
  1385. $expected = array(
  1386. 'foo' => array(
  1387. array(
  1388. 'bar' => "one\ntwo three",
  1389. ),
  1390. ),
  1391. );
  1392. $tests[] = array($yaml, $expected, false);
  1393. $yaml = <<<'EOF'
  1394. bar
  1395. "foo"
  1396. EOF;
  1397. $expected = 'bar "foo"';
  1398. $tests[] = array($yaml, $expected, false);
  1399. $yaml = <<<'EOF'
  1400. bar
  1401. "foo
  1402. EOF;
  1403. $expected = 'bar "foo';
  1404. $tests[] = array($yaml, $expected, false);
  1405. $yaml = <<<'EOF'
  1406. bar
  1407. 'foo'
  1408. EOF;
  1409. $expected = "bar\n'foo'";
  1410. $tests[] = array($yaml, $expected, false);
  1411. $yaml = <<<'EOF'
  1412. bar
  1413. foo'
  1414. EOF;
  1415. $expected = "bar\nfoo'";
  1416. $tests[] = array($yaml, $expected, false);
  1417. return $tests;
  1418. }
  1419. public function testTaggedInlineMapping()
  1420. {
  1421. $this->assertEquals(new TaggedValue('foo', array('foo' => 'bar')), $this->parser->parse('!foo {foo: bar}', Yaml::PARSE_CUSTOM_TAGS));
  1422. }
  1423. /**
  1424. * @dataProvider taggedValuesProvider
  1425. */
  1426. public function testCustomTagSupport($expected, $yaml)
  1427. {
  1428. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
  1429. }
  1430. public function taggedValuesProvider()
  1431. {
  1432. return array(
  1433. 'sequences' => array(
  1434. array(new TaggedValue('foo', array('yaml')), new TaggedValue('quz', array('bar'))),
  1435. <<<YAML
  1436. - !foo
  1437. - yaml
  1438. - !quz [bar]
  1439. YAML
  1440. ),
  1441. 'mappings' => array(
  1442. new TaggedValue('foo', array('foo' => new TaggedValue('quz', array('bar')), 'quz' => new TaggedValue('foo', array('quz' => 'bar')))),
  1443. <<<YAML
  1444. !foo
  1445. foo: !quz [bar]
  1446. quz: !foo
  1447. quz: bar
  1448. YAML
  1449. ),
  1450. 'inline' => array(
  1451. array(new TaggedValue('foo', array('foo', 'bar')), new TaggedValue('quz', array('foo' => 'bar', 'quz' => new TaggedValue('bar', array('one' => 'bar'))))),
  1452. <<<YAML
  1453. - !foo [foo, bar]
  1454. - !quz {foo: bar, quz: !bar {one: bar}}
  1455. YAML
  1456. ),
  1457. );
  1458. }
  1459. /**
  1460. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  1461. * @expectedExceptionMessage Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!iterator" at line 1 (near "!iterator [foo]").
  1462. */
  1463. public function testCustomTagsDisabled()
  1464. {
  1465. $this->parser->parse('!iterator [foo]');
  1466. }
  1467. /**
  1468. * @group legacy
  1469. * @expectedDeprecation Using the unquoted scalar value "!iterator foo" is deprecated since version 3.3 and will be considered as a tagged value in 4.0. You must quote it on line 1.
  1470. */
  1471. public function testUnsupportedTagWithScalar()
  1472. {
  1473. $this->assertEquals('!iterator foo', $this->parser->parse('!iterator foo'));
  1474. }
  1475. /**
  1476. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  1477. * @expectedExceptionMessage The built-in tag "!!foo" is not implemented.
  1478. */
  1479. public function testExceptionWhenUsingUnsuportedBuiltInTags()
  1480. {
  1481. $this->parser->parse('!!foo');
  1482. }
  1483. /**
  1484. * @group legacy
  1485. * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1.
  1486. */
  1487. public function testComplexMappingThrowsParseException()
  1488. {
  1489. $yaml = <<<YAML
  1490. ? "1"
  1491. :
  1492. name: végétalien
  1493. YAML;
  1494. $this->parser->parse($yaml);
  1495. }
  1496. /**
  1497. * @group legacy
  1498. * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 2.
  1499. */
  1500. public function testComplexMappingNestedInMappingThrowsParseException()
  1501. {
  1502. $yaml = <<<YAML
  1503. diet:
  1504. ? "1"
  1505. :
  1506. name: végétalien
  1507. YAML;
  1508. $this->parser->parse($yaml);
  1509. }
  1510. /**
  1511. * @group legacy
  1512. * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1.
  1513. */
  1514. public function testComplexMappingNestedInSequenceThrowsParseException()
  1515. {
  1516. $yaml = <<<YAML
  1517. - ? "1"
  1518. :
  1519. name: végétalien
  1520. YAML;
  1521. $this->parser->parse($yaml);
  1522. }
  1523. /**
  1524. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  1525. * @expectedExceptionMessage Unable to parse at line 1 (near "[parameters]").
  1526. */
  1527. public function testParsingIniThrowsException()
  1528. {
  1529. $ini = <<<INI
  1530. [parameters]
  1531. foo = bar
  1532. bar = %foo%
  1533. INI;
  1534. $this->parser->parse($ini);
  1535. }
  1536. private function loadTestsFromFixtureFiles($testsFile)
  1537. {
  1538. $parser = new Parser();
  1539. $tests = array();
  1540. $files = $parser->parse(file_get_contents(__DIR__.'/Fixtures/'.$testsFile));
  1541. foreach ($files as $file) {
  1542. $yamls = file_get_contents(__DIR__.'/Fixtures/'.$file.'.yml');
  1543. // split YAMLs documents
  1544. foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
  1545. if (!$yaml) {
  1546. continue;
  1547. }
  1548. $test = $parser->parse($yaml);
  1549. if (isset($test['todo']) && $test['todo']) {
  1550. // TODO
  1551. } else {
  1552. eval('$expected = '.trim($test['php']).';');
  1553. $tests[] = array(var_export($expected, true), $test['yaml'], $test['test'], isset($test['deprecated']) ? $test['deprecated'] : false);
  1554. }
  1555. }
  1556. }
  1557. return $tests;
  1558. }
  1559. public function testCanParseVeryLongValue()
  1560. {
  1561. $longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000);
  1562. $trickyVal = array('x' => $longStringWithSpaces);
  1563. $yamlString = Yaml::dump($trickyVal);
  1564. $arrayFromYaml = $this->parser->parse($yamlString);
  1565. $this->assertEquals($trickyVal, $arrayFromYaml);
  1566. }
  1567. /**
  1568. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  1569. * @expectedExceptionMessage Reference "foo" does not exist at line 2
  1570. */
  1571. public function testParserCleansUpReferencesBetweenRuns()
  1572. {
  1573. $yaml = <<<YAML
  1574. foo: &foo
  1575. baz: foobar
  1576. bar:
  1577. <<: *foo
  1578. YAML;
  1579. $this->parser->parse($yaml);
  1580. $yaml = <<<YAML
  1581. bar:
  1582. <<: *foo
  1583. YAML;
  1584. $this->parser->parse($yaml);
  1585. }
  1586. public function testPhpConstantTagMappingKey()
  1587. {
  1588. $yaml = <<<YAML
  1589. transitions:
  1590. !php/const:Symfony\Component\Yaml\Tests\B::FOO:
  1591. from:
  1592. - !php/const:Symfony\Component\Yaml\Tests\B::BAR
  1593. to: !php/const:Symfony\Component\Yaml\Tests\B::BAZ
  1594. YAML;
  1595. $expected = array(
  1596. 'transitions' => array(
  1597. 'foo' => array(
  1598. 'from' => array(
  1599. 'bar',
  1600. ),
  1601. 'to' => 'baz',
  1602. ),
  1603. ),
  1604. );
  1605. $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
  1606. }
  1607. public function testPhpConstantTagMappingKeyWithKeysCastToStrings()
  1608. {
  1609. $yaml = <<<YAML
  1610. transitions:
  1611. !php/const:Symfony\Component\Yaml\Tests\B::FOO:
  1612. from:
  1613. - !php/const:Symfony\Component\Yaml\Tests\B::BAR
  1614. to: !php/const:Symfony\Component\Yaml\Tests\B::BAZ
  1615. YAML;
  1616. $expected = array(
  1617. 'transitions' => array(
  1618. 'foo' => array(
  1619. 'from' => array(
  1620. 'bar',
  1621. ),
  1622. 'to' => 'baz',
  1623. ),
  1624. ),
  1625. );
  1626. $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT | Yaml::PARSE_KEYS_AS_STRINGS));
  1627. }
  1628. public function testMergeKeysWhenMappingsAreParsedAsObjects()
  1629. {
  1630. $yaml = <<<YAML
  1631. foo: &FOO
  1632. bar: 1
  1633. bar: &BAR
  1634. baz: 2
  1635. <<: *FOO
  1636. baz:
  1637. baz_foo: 3
  1638. <<:
  1639. baz_bar: 4
  1640. foobar:
  1641. bar: ~
  1642. <<: [*FOO, *BAR]
  1643. YAML;
  1644. $expected = (object) array(
  1645. 'foo' => (object) array(
  1646. 'bar' => 1,
  1647. ),
  1648. 'bar' => (object) array(
  1649. 'baz' => 2,
  1650. 'bar' => 1,
  1651. ),
  1652. 'baz' => (object) array(
  1653. 'baz_foo' => 3,
  1654. 'baz_bar' => 4,
  1655. ),
  1656. 'foobar' => (object) array(
  1657. 'bar' => null,
  1658. 'baz' => 2,
  1659. ),
  1660. );
  1661. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  1662. }
  1663. public function testParseReferencesOnMergeKeys()
  1664. {
  1665. $yaml = <<<YAML
  1666. mergekeyrefdef:
  1667. a: foo
  1668. <<: &quux
  1669. b: bar
  1670. c: baz
  1671. mergekeyderef:
  1672. d: quux
  1673. <<: *quux
  1674. YAML;
  1675. $expected = array(
  1676. 'mergekeyrefdef' => array(
  1677. 'a' => 'foo',
  1678. 'b' => 'bar',
  1679. 'c' => 'baz',
  1680. ),
  1681. 'mergekeyderef' => array(
  1682. 'd' => 'quux',
  1683. 'b' => 'bar',
  1684. 'c' => 'baz',
  1685. ),
  1686. );
  1687. $this->assertSame($expected, $this->parser->parse($yaml));
  1688. }
  1689. public function testParseReferencesOnMergeKeysWithMappingsParsedAsObjects()
  1690. {
  1691. $yaml = <<<YAML
  1692. mergekeyrefdef:
  1693. a: foo
  1694. <<: &quux
  1695. b: bar
  1696. c: baz
  1697. mergekeyderef:
  1698. d: quux
  1699. <<: *quux
  1700. YAML;
  1701. $expected = (object) array(
  1702. 'mergekeyrefdef' => (object) array(
  1703. 'a' => 'foo',
  1704. 'b' => 'bar',
  1705. 'c' => 'baz',
  1706. ),
  1707. 'mergekeyderef' => (object) array(
  1708. 'd' => 'quux',
  1709. 'b' => 'bar',
  1710. 'c' => 'baz',
  1711. ),
  1712. );
  1713. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  1714. }
  1715. }
  1716. class B
  1717. {
  1718. public $b = 'foo';
  1719. const FOO = 'foo';
  1720. const BAR = 'bar';
  1721. const BAZ = 'baz';
  1722. }