ParserTest.php 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Yaml\Tests;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Yaml\Parser;
  13. use Symfony\Component\Yaml\Tag\TaggedValue;
  14. use Symfony\Component\Yaml\Yaml;
  15. class ParserTest extends TestCase
  16. {
  17. /** @var Parser */
  18. protected $parser;
  19. protected function setUp()
  20. {
  21. $this->parser = new Parser();
  22. }
  23. protected function tearDown()
  24. {
  25. $this->parser = null;
  26. chmod(__DIR__.'/Fixtures/not_readable.yml', 0644);
  27. }
  28. /**
  29. * @dataProvider getDataFormSpecifications
  30. */
  31. public function testSpecifications($expected, $yaml, $comment, $deprecated)
  32. {
  33. $deprecations = [];
  34. if ($deprecated) {
  35. set_error_handler(function ($type, $msg) use (&$deprecations) {
  36. if (\E_USER_DEPRECATED !== $type) {
  37. restore_error_handler();
  38. return \call_user_func_array('PHPUnit\Util\ErrorHandler::handleError', \func_get_args());
  39. }
  40. $deprecations[] = $msg;
  41. return null;
  42. });
  43. }
  44. $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
  45. if ($deprecated) {
  46. restore_error_handler();
  47. $this->assertCount(1, $deprecations);
  48. $this->assertStringContainsString(true !== $deprecated ? $deprecated : 'Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0 on line 1.', $deprecations[0]);
  49. }
  50. }
  51. public function getDataFormSpecifications()
  52. {
  53. return $this->loadTestsFromFixtureFiles('index.yml');
  54. }
  55. /**
  56. * @group legacy
  57. * @expectedDeprecationMessage Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable
  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 = [
  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. $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 = [];
  111. $yaml = <<<'EOF'
  112. foo: |-
  113. one
  114. two
  115. bar: |-
  116. one
  117. two
  118. EOF;
  119. $expected = [
  120. 'foo' => "one\ntwo",
  121. 'bar' => "one\ntwo",
  122. ];
  123. $tests['Literal block chomping strip with single trailing newline'] = [$expected, $yaml];
  124. $yaml = <<<'EOF'
  125. foo: |-
  126. one
  127. two
  128. bar: |-
  129. one
  130. two
  131. EOF;
  132. $expected = [
  133. 'foo' => "one\ntwo",
  134. 'bar' => "one\ntwo",
  135. ];
  136. $tests['Literal block chomping strip with multiple trailing newlines'] = [$expected, $yaml];
  137. $yaml = <<<'EOF'
  138. {}
  139. EOF;
  140. $expected = [];
  141. $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = [$expected, $yaml];
  142. $yaml = <<<'EOF'
  143. foo: |-
  144. one
  145. two
  146. bar: |-
  147. one
  148. two
  149. EOF;
  150. $expected = [
  151. 'foo' => "one\ntwo",
  152. 'bar' => "one\ntwo",
  153. ];
  154. $tests['Literal block chomping strip without trailing newline'] = [$expected, $yaml];
  155. $yaml = <<<'EOF'
  156. foo: |
  157. one
  158. two
  159. bar: |
  160. one
  161. two
  162. EOF;
  163. $expected = [
  164. 'foo' => "one\ntwo\n",
  165. 'bar' => "one\ntwo\n",
  166. ];
  167. $tests['Literal block chomping clip with single trailing newline'] = [$expected, $yaml];
  168. $yaml = <<<'EOF'
  169. foo: |
  170. one
  171. two
  172. bar: |
  173. one
  174. two
  175. EOF;
  176. $expected = [
  177. 'foo' => "one\ntwo\n",
  178. 'bar' => "one\ntwo\n",
  179. ];
  180. $tests['Literal block chomping clip with multiple trailing newlines'] = [$expected, $yaml];
  181. $yaml = <<<'EOF'
  182. foo:
  183. - bar: |
  184. one
  185. two
  186. EOF;
  187. $expected = [
  188. 'foo' => [
  189. [
  190. 'bar' => "one\n\ntwo",
  191. ],
  192. ],
  193. ];
  194. $tests['Literal block chomping clip with embedded blank line inside unindented collection'] = [$expected, $yaml];
  195. $yaml = <<<'EOF'
  196. foo: |
  197. one
  198. two
  199. bar: |
  200. one
  201. two
  202. EOF;
  203. $expected = [
  204. 'foo' => "one\ntwo\n",
  205. 'bar' => "one\ntwo",
  206. ];
  207. $tests['Literal block chomping clip without trailing newline'] = [$expected, $yaml];
  208. $yaml = <<<'EOF'
  209. foo: |+
  210. one
  211. two
  212. bar: |+
  213. one
  214. two
  215. EOF;
  216. $expected = [
  217. 'foo' => "one\ntwo\n",
  218. 'bar' => "one\ntwo\n",
  219. ];
  220. $tests['Literal block chomping keep with single trailing newline'] = [$expected, $yaml];
  221. $yaml = <<<'EOF'
  222. foo: |+
  223. one
  224. two
  225. bar: |+
  226. one
  227. two
  228. EOF;
  229. $expected = [
  230. 'foo' => "one\ntwo\n\n",
  231. 'bar' => "one\ntwo\n\n",
  232. ];
  233. $tests['Literal block chomping keep with multiple trailing newlines'] = [$expected, $yaml];
  234. $yaml = <<<'EOF'
  235. foo: |+
  236. one
  237. two
  238. bar: |+
  239. one
  240. two
  241. EOF;
  242. $expected = [
  243. 'foo' => "one\ntwo\n",
  244. 'bar' => "one\ntwo",
  245. ];
  246. $tests['Literal block chomping keep without trailing newline'] = [$expected, $yaml];
  247. $yaml = <<<'EOF'
  248. foo: >-
  249. one
  250. two
  251. bar: >-
  252. one
  253. two
  254. EOF;
  255. $expected = [
  256. 'foo' => 'one two',
  257. 'bar' => 'one two',
  258. ];
  259. $tests['Folded block chomping strip with single trailing newline'] = [$expected, $yaml];
  260. $yaml = <<<'EOF'
  261. foo: >-
  262. one
  263. two
  264. bar: >-
  265. one
  266. two
  267. EOF;
  268. $expected = [
  269. 'foo' => 'one two',
  270. 'bar' => 'one two',
  271. ];
  272. $tests['Folded block chomping strip with multiple trailing newlines'] = [$expected, $yaml];
  273. $yaml = <<<'EOF'
  274. foo: >-
  275. one
  276. two
  277. bar: >-
  278. one
  279. two
  280. EOF;
  281. $expected = [
  282. 'foo' => 'one two',
  283. 'bar' => 'one two',
  284. ];
  285. $tests['Folded block chomping strip without trailing newline'] = [$expected, $yaml];
  286. $yaml = <<<'EOF'
  287. foo: >
  288. one
  289. two
  290. bar: >
  291. one
  292. two
  293. EOF;
  294. $expected = [
  295. 'foo' => "one two\n",
  296. 'bar' => "one two\n",
  297. ];
  298. $tests['Folded block chomping clip with single trailing newline'] = [$expected, $yaml];
  299. $yaml = <<<'EOF'
  300. foo: >
  301. one
  302. two
  303. bar: >
  304. one
  305. two
  306. EOF;
  307. $expected = [
  308. 'foo' => "one two\n",
  309. 'bar' => "one two\n",
  310. ];
  311. $tests['Folded block chomping clip with multiple trailing newlines'] = [$expected, $yaml];
  312. $yaml = <<<'EOF'
  313. foo: >
  314. one
  315. two
  316. bar: >
  317. one
  318. two
  319. EOF;
  320. $expected = [
  321. 'foo' => "one two\n",
  322. 'bar' => 'one two',
  323. ];
  324. $tests['Folded block chomping clip without trailing newline'] = [$expected, $yaml];
  325. $yaml = <<<'EOF'
  326. foo: >+
  327. one
  328. two
  329. bar: >+
  330. one
  331. two
  332. EOF;
  333. $expected = [
  334. 'foo' => "one two\n",
  335. 'bar' => "one two\n",
  336. ];
  337. $tests['Folded block chomping keep with single trailing newline'] = [$expected, $yaml];
  338. $yaml = <<<'EOF'
  339. foo: >+
  340. one
  341. two
  342. bar: >+
  343. one
  344. two
  345. EOF;
  346. $expected = [
  347. 'foo' => "one two\n\n",
  348. 'bar' => "one two\n\n",
  349. ];
  350. $tests['Folded block chomping keep with multiple trailing newlines'] = [$expected, $yaml];
  351. $yaml = <<<'EOF'
  352. foo: >+
  353. one
  354. two
  355. bar: >+
  356. one
  357. two
  358. EOF;
  359. $expected = [
  360. 'foo' => "one two\n",
  361. 'bar' => 'one two',
  362. ];
  363. $tests['Folded block chomping keep without trailing newline'] = [$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 = [
  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(['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(['foo' => new B(), 'bar' => 1], $this->parser->parse($input, false, true), '->parse() is able to parse objects');
  407. }
  408. /**
  409. * @group legacy
  410. * @dataProvider deprecatedObjectValueProvider
  411. */
  412. public function testObjectSupportEnabledWithDeprecatedTag($yaml)
  413. {
  414. $this->assertEquals(['foo' => new B(), 'bar' => 1], $this->parser->parse($yaml, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
  415. }
  416. public function deprecatedObjectValueProvider()
  417. {
  418. return [
  419. [
  420. <<<YAML
  421. foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  422. bar: 1
  423. YAML
  424. ],
  425. [
  426. <<<YAML
  427. foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  428. bar: 1
  429. YAML
  430. ],
  431. ];
  432. }
  433. /**
  434. * @dataProvider invalidDumpedObjectProvider
  435. */
  436. public function testObjectSupportDisabledButNoExceptions($input)
  437. {
  438. $this->assertEquals(['foo' => null, 'bar' => 1], $this->parser->parse($input), '->parse() does not parse objects');
  439. }
  440. /**
  441. * @dataProvider getObjectForMapTests
  442. */
  443. public function testObjectForMap($yaml, $expected)
  444. {
  445. $flags = Yaml::PARSE_OBJECT_FOR_MAP;
  446. $this->assertEquals($expected, $this->parser->parse($yaml, $flags));
  447. }
  448. /**
  449. * @group legacy
  450. * @dataProvider getObjectForMapTests
  451. */
  452. public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected)
  453. {
  454. $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
  455. }
  456. public function getObjectForMapTests()
  457. {
  458. $tests = [];
  459. $yaml = <<<'EOF'
  460. foo:
  461. fiz: [cat]
  462. EOF;
  463. $expected = new \stdClass();
  464. $expected->foo = new \stdClass();
  465. $expected->foo->fiz = ['cat'];
  466. $tests['mapping'] = [$yaml, $expected];
  467. $yaml = '{ "foo": "bar", "fiz": "cat" }';
  468. $expected = new \stdClass();
  469. $expected->foo = 'bar';
  470. $expected->fiz = 'cat';
  471. $tests['inline-mapping'] = [$yaml, $expected];
  472. $yaml = "foo: bar\nbaz: foobar";
  473. $expected = new \stdClass();
  474. $expected->foo = 'bar';
  475. $expected->baz = 'foobar';
  476. $tests['object-for-map-is-applied-after-parsing'] = [$yaml, $expected];
  477. $yaml = <<<'EOT'
  478. array:
  479. - key: one
  480. - key: two
  481. EOT;
  482. $expected = new \stdClass();
  483. $expected->array = [];
  484. $expected->array[0] = new \stdClass();
  485. $expected->array[0]->key = 'one';
  486. $expected->array[1] = new \stdClass();
  487. $expected->array[1]->key = 'two';
  488. $tests['nest-map-and-sequence'] = [$yaml, $expected];
  489. $yaml = <<<'YAML'
  490. map:
  491. 1: one
  492. 2: two
  493. YAML;
  494. $expected = new \stdClass();
  495. $expected->map = new \stdClass();
  496. $expected->map->{1} = 'one';
  497. $expected->map->{2} = 'two';
  498. $tests['numeric-keys'] = [$yaml, $expected];
  499. $yaml = <<<'YAML'
  500. map:
  501. '0': one
  502. '1': two
  503. YAML;
  504. $expected = new \stdClass();
  505. $expected->map = new \stdClass();
  506. $expected->map->{0} = 'one';
  507. $expected->map->{1} = 'two';
  508. $tests['zero-indexed-numeric-keys'] = [$yaml, $expected];
  509. return $tests;
  510. }
  511. /**
  512. * @dataProvider invalidDumpedObjectProvider
  513. */
  514. public function testObjectsSupportDisabledWithExceptions($yaml)
  515. {
  516. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  517. $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
  518. }
  519. public function testCanParseContentWithTrailingSpaces()
  520. {
  521. $yaml = "items: \n foo: bar";
  522. $expected = [
  523. 'items' => ['foo' => 'bar'],
  524. ];
  525. $this->assertSame($expected, $this->parser->parse($yaml));
  526. }
  527. /**
  528. * @group legacy
  529. * @dataProvider invalidDumpedObjectProvider
  530. */
  531. public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml)
  532. {
  533. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  534. $this->parser->parse($yaml, true);
  535. }
  536. public function invalidDumpedObjectProvider()
  537. {
  538. $yamlTag = <<<'EOF'
  539. foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  540. bar: 1
  541. EOF;
  542. $localTag = <<<'EOF'
  543. foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  544. bar: 1
  545. EOF;
  546. return [
  547. 'yaml-tag' => [$yamlTag],
  548. 'local-tag' => [$localTag],
  549. ];
  550. }
  551. /**
  552. * @requires extension iconv
  553. */
  554. public function testNonUtf8Exception()
  555. {
  556. $yamls = [
  557. iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"),
  558. iconv('UTF-8', 'ISO-8859-15', "euro: '€'"),
  559. iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"),
  560. ];
  561. foreach ($yamls as $yaml) {
  562. try {
  563. $this->parser->parse($yaml);
  564. $this->fail('charsets other than UTF-8 are rejected.');
  565. } catch (\Exception $e) {
  566. $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
  567. }
  568. }
  569. }
  570. public function testUnindentedCollectionException()
  571. {
  572. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  573. $yaml = <<<'EOF'
  574. collection:
  575. -item1
  576. -item2
  577. -item3
  578. EOF;
  579. $this->parser->parse($yaml);
  580. }
  581. public function testShortcutKeyUnindentedCollectionException()
  582. {
  583. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  584. $yaml = <<<'EOF'
  585. collection:
  586. - key: foo
  587. foo: bar
  588. EOF;
  589. $this->parser->parse($yaml);
  590. }
  591. public function testMultipleDocumentsNotSupportedException()
  592. {
  593. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  594. $this->expectExceptionMessageMatches('/^Multiple documents are not supported.+/');
  595. Yaml::parse(<<<'EOL'
  596. # Ranking of 1998 home runs
  597. ---
  598. - Mark McGwire
  599. - Sammy Sosa
  600. - Ken Griffey
  601. # Team ranking
  602. ---
  603. - Chicago Cubs
  604. - St Louis Cardinals
  605. EOL
  606. );
  607. }
  608. public function testSequenceInAMapping()
  609. {
  610. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  611. Yaml::parse(<<<'EOF'
  612. yaml:
  613. hash: me
  614. - array stuff
  615. EOF
  616. );
  617. }
  618. public function testSequenceInMappingStartedBySingleDashLine()
  619. {
  620. $yaml = <<<'EOT'
  621. a:
  622. -
  623. b:
  624. -
  625. bar: baz
  626. - foo
  627. d: e
  628. EOT;
  629. $expected = [
  630. 'a' => [
  631. [
  632. 'b' => [
  633. [
  634. 'bar' => 'baz',
  635. ],
  636. ],
  637. ],
  638. 'foo',
  639. ],
  640. 'd' => 'e',
  641. ];
  642. $this->assertSame($expected, $this->parser->parse($yaml));
  643. }
  644. public function testSequenceFollowedByCommentEmbeddedInMapping()
  645. {
  646. $yaml = <<<'EOT'
  647. a:
  648. b:
  649. - c
  650. # comment
  651. d: e
  652. EOT;
  653. $expected = [
  654. 'a' => [
  655. 'b' => ['c'],
  656. 'd' => 'e',
  657. ],
  658. ];
  659. $this->assertSame($expected, $this->parser->parse($yaml));
  660. }
  661. public function testNonStringFollowedByCommentEmbeddedInMapping()
  662. {
  663. $yaml = <<<'EOT'
  664. a:
  665. b:
  666. {}
  667. # comment
  668. d:
  669. 1.1
  670. # another comment
  671. EOT;
  672. $expected = [
  673. 'a' => [
  674. 'b' => [],
  675. 'd' => 1.1,
  676. ],
  677. ];
  678. $this->assertSame($expected, $this->parser->parse($yaml));
  679. }
  680. public function getParseExceptionNotAffectedMultiLineStringLastResortParsing()
  681. {
  682. $tests = [];
  683. $yaml = <<<'EOT'
  684. a
  685. b:
  686. EOT;
  687. $tests['parse error on first line'] = [$yaml];
  688. $yaml = <<<'EOT'
  689. a
  690. b
  691. c:
  692. EOT;
  693. $tests['parse error due to inconsistent indentation'] = [$yaml];
  694. $yaml = <<<'EOT'
  695. & * ! | > ' " % @ ` #, { asd a;sdasd }-@^qw3
  696. EOT;
  697. $tests['symfony/symfony/issues/22967#issuecomment-322067742'] = [$yaml];
  698. return $tests;
  699. }
  700. /**
  701. * @dataProvider getParseExceptionNotAffectedMultiLineStringLastResortParsing
  702. */
  703. public function testParseExceptionNotAffectedByMultiLineStringLastResortParsing($yaml)
  704. {
  705. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  706. $this->parser->parse($yaml);
  707. }
  708. public function testMultiLineStringLastResortParsing()
  709. {
  710. $yaml = <<<'EOT'
  711. test:
  712. You can have things that don't look like strings here
  713. true
  714. yes you can
  715. EOT;
  716. $expected = [
  717. 'test' => 'You can have things that don\'t look like strings here true yes you can',
  718. ];
  719. $this->assertSame($expected, $this->parser->parse($yaml));
  720. $yaml = <<<'EOT'
  721. a:
  722. b
  723. c
  724. EOT;
  725. $expected = [
  726. 'a' => 'b c',
  727. ];
  728. $this->assertSame($expected, $this->parser->parse($yaml));
  729. }
  730. public function testMappingInASequence()
  731. {
  732. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  733. Yaml::parse(<<<'EOF'
  734. yaml:
  735. - array stuff
  736. hash: me
  737. EOF
  738. );
  739. }
  740. public function testScalarInSequence()
  741. {
  742. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  743. $this->expectExceptionMessage('missing colon');
  744. Yaml::parse(<<<'EOF'
  745. foo:
  746. - bar
  747. "missing colon"
  748. foo: bar
  749. EOF
  750. );
  751. }
  752. /**
  753. * > It is an error for two equal keys to appear in the same mapping node.
  754. * > In such a case the YAML processor may continue, ignoring the second
  755. * > `key: value` pair and issuing an appropriate warning. This strategy
  756. * > preserves a consistent information model for one-pass and random access
  757. * > applications.
  758. *
  759. * @see http://yaml.org/spec/1.2/spec.html#id2759572
  760. * @see http://yaml.org/spec/1.1/#id932806
  761. * @group legacy
  762. */
  763. public function testMappingDuplicateKeyBlock()
  764. {
  765. $input = <<<'EOD'
  766. parent:
  767. child: first
  768. child: duplicate
  769. parent:
  770. child: duplicate
  771. child: duplicate
  772. EOD;
  773. $expected = [
  774. 'parent' => [
  775. 'child' => 'first',
  776. ],
  777. ];
  778. $this->assertSame($expected, Yaml::parse($input));
  779. }
  780. /**
  781. * @group legacy
  782. */
  783. public function testMappingDuplicateKeyFlow()
  784. {
  785. $input = <<<'EOD'
  786. parent: { child: first, child: duplicate }
  787. parent: { child: duplicate, child: duplicate }
  788. EOD;
  789. $expected = [
  790. 'parent' => [
  791. 'child' => 'first',
  792. ],
  793. ];
  794. $this->assertSame($expected, Yaml::parse($input));
  795. }
  796. /**
  797. * @group legacy
  798. * @dataProvider getParseExceptionOnDuplicateData
  799. * @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.
  800. * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
  801. */
  802. public function testParseExceptionOnDuplicate($input, $duplicateKey, $lineNumber)
  803. {
  804. Yaml::parse($input);
  805. }
  806. public function getParseExceptionOnDuplicateData()
  807. {
  808. $tests = [];
  809. $yaml = <<<EOD
  810. parent: { child: first, child: duplicate }
  811. EOD;
  812. $tests[] = [$yaml, 'child', 1];
  813. $yaml = <<<EOD
  814. parent:
  815. child: first,
  816. child: duplicate
  817. EOD;
  818. $tests[] = [$yaml, 'child', 3];
  819. $yaml = <<<EOD
  820. parent: { child: foo }
  821. parent: { child: bar }
  822. EOD;
  823. $tests[] = [$yaml, 'parent', 2];
  824. $yaml = <<<EOD
  825. parent: { child_mapping: { value: bar}, child_mapping: { value: bar} }
  826. EOD;
  827. $tests[] = [$yaml, 'child_mapping', 1];
  828. $yaml = <<<EOD
  829. parent:
  830. child_mapping:
  831. value: bar
  832. child_mapping:
  833. value: bar
  834. EOD;
  835. $tests[] = [$yaml, 'child_mapping', 4];
  836. $yaml = <<<EOD
  837. parent: { child_sequence: ['key1', 'key2', 'key3'], child_sequence: ['key1', 'key2', 'key3'] }
  838. EOD;
  839. $tests[] = [$yaml, 'child_sequence', 1];
  840. $yaml = <<<EOD
  841. parent:
  842. child_sequence:
  843. - key1
  844. - key2
  845. - key3
  846. child_sequence:
  847. - key1
  848. - key2
  849. - key3
  850. EOD;
  851. $tests[] = [$yaml, 'child_sequence', 6];
  852. return $tests;
  853. }
  854. public function testEmptyValue()
  855. {
  856. $input = <<<'EOF'
  857. hash:
  858. EOF;
  859. $this->assertEquals(['hash' => null], Yaml::parse($input));
  860. }
  861. public function testCommentAtTheRootIndent()
  862. {
  863. $this->assertEquals([
  864. 'services' => [
  865. 'app.foo_service' => [
  866. 'class' => 'Foo',
  867. ],
  868. 'app/bar_service' => [
  869. 'class' => 'Bar',
  870. ],
  871. ],
  872. ], Yaml::parse(<<<'EOF'
  873. # comment 1
  874. services:
  875. # comment 2
  876. # comment 3
  877. app.foo_service:
  878. class: Foo
  879. # comment 4
  880. # comment 5
  881. app/bar_service:
  882. class: Bar
  883. EOF
  884. ));
  885. }
  886. public function testStringBlockWithComments()
  887. {
  888. $this->assertEquals(['content' => <<<'EOT'
  889. # comment 1
  890. header
  891. # comment 2
  892. <body>
  893. <h1>title</h1>
  894. </body>
  895. footer # comment3
  896. EOT
  897. ], Yaml::parse(<<<'EOF'
  898. content: |
  899. # comment 1
  900. header
  901. # comment 2
  902. <body>
  903. <h1>title</h1>
  904. </body>
  905. footer # comment3
  906. EOF
  907. ));
  908. }
  909. public function testFoldedStringBlockWithComments()
  910. {
  911. $this->assertEquals([['content' => <<<'EOT'
  912. # comment 1
  913. header
  914. # comment 2
  915. <body>
  916. <h1>title</h1>
  917. </body>
  918. footer # comment3
  919. EOT
  920. ]], Yaml::parse(<<<'EOF'
  921. -
  922. content: |
  923. # comment 1
  924. header
  925. # comment 2
  926. <body>
  927. <h1>title</h1>
  928. </body>
  929. footer # comment3
  930. EOF
  931. ));
  932. }
  933. public function testNestedFoldedStringBlockWithComments()
  934. {
  935. $this->assertEquals([[
  936. 'title' => 'some title',
  937. 'content' => <<<'EOT'
  938. # comment 1
  939. header
  940. # comment 2
  941. <body>
  942. <h1>title</h1>
  943. </body>
  944. footer # comment3
  945. EOT
  946. ]], Yaml::parse(<<<'EOF'
  947. -
  948. title: some title
  949. content: |
  950. # comment 1
  951. header
  952. # comment 2
  953. <body>
  954. <h1>title</h1>
  955. </body>
  956. footer # comment3
  957. EOF
  958. ));
  959. }
  960. public function testReferenceResolvingInInlineStrings()
  961. {
  962. $this->assertEquals([
  963. 'var' => 'var-value',
  964. 'scalar' => 'var-value',
  965. 'list' => ['var-value'],
  966. 'list_in_list' => [['var-value']],
  967. 'map_in_list' => [['key' => 'var-value']],
  968. 'embedded_mapping' => [['key' => 'var-value']],
  969. 'map' => ['key' => 'var-value'],
  970. 'list_in_map' => ['key' => ['var-value']],
  971. 'map_in_map' => ['foo' => ['bar' => 'var-value']],
  972. ], Yaml::parse(<<<'EOF'
  973. var: &var var-value
  974. scalar: *var
  975. list: [ *var ]
  976. list_in_list: [[ *var ]]
  977. map_in_list: [ { key: *var } ]
  978. embedded_mapping: [ key: *var ]
  979. map: { key: *var }
  980. list_in_map: { key: [*var] }
  981. map_in_map: { foo: { bar: *var } }
  982. EOF
  983. ));
  984. }
  985. public function testYamlDirective()
  986. {
  987. $yaml = <<<'EOF'
  988. %YAML 1.2
  989. ---
  990. foo: 1
  991. bar: 2
  992. EOF;
  993. $this->assertEquals(['foo' => 1, 'bar' => 2], $this->parser->parse($yaml));
  994. }
  995. /**
  996. * @group legacy
  997. * @expectedDeprecation Implicit casting of numeric key to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 2.
  998. */
  999. public function testFloatKeys()
  1000. {
  1001. $yaml = <<<'EOF'
  1002. foo:
  1003. 1.2: "bar"
  1004. 1.3: "baz"
  1005. EOF;
  1006. $expected = [
  1007. 'foo' => [
  1008. '1.2' => 'bar',
  1009. '1.3' => 'baz',
  1010. ],
  1011. ];
  1012. $this->assertEquals($expected, $this->parser->parse($yaml));
  1013. }
  1014. /**
  1015. * @group legacy
  1016. * @expectedDeprecation Implicit casting of non-string key to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1.
  1017. */
  1018. public function testBooleanKeys()
  1019. {
  1020. $yaml = <<<'EOF'
  1021. true: foo
  1022. false: bar
  1023. EOF;
  1024. $expected = [
  1025. 1 => 'foo',
  1026. 0 => 'bar',
  1027. ];
  1028. $this->assertEquals($expected, $this->parser->parse($yaml));
  1029. }
  1030. public function testExplicitStringCasting()
  1031. {
  1032. $yaml = <<<'EOF'
  1033. '1.2': "bar"
  1034. !!str 1.3: "baz"
  1035. 'true': foo
  1036. !!str false: bar
  1037. !!str null: 'null'
  1038. '~': 'null'
  1039. EOF;
  1040. $expected = [
  1041. '1.2' => 'bar',
  1042. '1.3' => 'baz',
  1043. 'true' => 'foo',
  1044. 'false' => 'bar',
  1045. 'null' => 'null',
  1046. '~' => 'null',
  1047. ];
  1048. $this->assertEquals($expected, $this->parser->parse($yaml));
  1049. }
  1050. public function testColonInMappingValueException()
  1051. {
  1052. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1053. $this->expectExceptionMessage('A colon cannot be used in an unquoted mapping value');
  1054. $yaml = <<<'EOF'
  1055. foo: bar: baz
  1056. EOF;
  1057. $this->parser->parse($yaml);
  1058. }
  1059. public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
  1060. {
  1061. $yaml = <<<'EOT'
  1062. foo:
  1063. bar: foobar # Note: a comment after a colon
  1064. EOT;
  1065. $this->assertSame(['foo' => ['bar' => 'foobar']], $this->parser->parse($yaml));
  1066. }
  1067. /**
  1068. * @dataProvider getCommentLikeStringInScalarBlockData
  1069. */
  1070. public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
  1071. {
  1072. $this->assertSame($expectedParserResult, $this->parser->parse($yaml));
  1073. }
  1074. public function getCommentLikeStringInScalarBlockData()
  1075. {
  1076. $tests = [];
  1077. $yaml = <<<'EOT'
  1078. pages:
  1079. -
  1080. title: some title
  1081. content: |
  1082. # comment 1
  1083. header
  1084. # comment 2
  1085. <body>
  1086. <h1>title</h1>
  1087. </body>
  1088. footer # comment3
  1089. EOT;
  1090. $expected = [
  1091. 'pages' => [
  1092. [
  1093. 'title' => 'some title',
  1094. 'content' => <<<'EOT'
  1095. # comment 1
  1096. header
  1097. # comment 2
  1098. <body>
  1099. <h1>title</h1>
  1100. </body>
  1101. footer # comment3
  1102. EOT
  1103. ,
  1104. ],
  1105. ],
  1106. ];
  1107. $tests[] = [$yaml, $expected];
  1108. $yaml = <<<'EOT'
  1109. test: |
  1110. foo
  1111. # bar
  1112. baz
  1113. collection:
  1114. - one: |
  1115. foo
  1116. # bar
  1117. baz
  1118. - two: |
  1119. foo
  1120. # bar
  1121. baz
  1122. EOT;
  1123. $expected = [
  1124. 'test' => <<<'EOT'
  1125. foo
  1126. # bar
  1127. baz
  1128. EOT
  1129. ,
  1130. 'collection' => [
  1131. [
  1132. 'one' => <<<'EOT'
  1133. foo
  1134. # bar
  1135. baz
  1136. EOT
  1137. ,
  1138. ],
  1139. [
  1140. 'two' => <<<'EOT'
  1141. foo
  1142. # bar
  1143. baz
  1144. EOT
  1145. ,
  1146. ],
  1147. ],
  1148. ];
  1149. $tests[] = [$yaml, $expected];
  1150. $yaml = <<<'EOT'
  1151. foo:
  1152. bar:
  1153. scalar-block: >
  1154. line1
  1155. line2>
  1156. baz:
  1157. # comment
  1158. foobar: ~
  1159. EOT;
  1160. $expected = [
  1161. 'foo' => [
  1162. 'bar' => [
  1163. 'scalar-block' => "line1 line2>\n",
  1164. ],
  1165. 'baz' => [
  1166. 'foobar' => null,
  1167. ],
  1168. ],
  1169. ];
  1170. $tests[] = [$yaml, $expected];
  1171. $yaml = <<<'EOT'
  1172. a:
  1173. b: hello
  1174. # c: |
  1175. # first row
  1176. # second row
  1177. d: hello
  1178. EOT;
  1179. $expected = [
  1180. 'a' => [
  1181. 'b' => 'hello',
  1182. 'd' => 'hello',
  1183. ],
  1184. ];
  1185. $tests[] = [$yaml, $expected];
  1186. return $tests;
  1187. }
  1188. public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
  1189. {
  1190. $yaml = <<<'EOT'
  1191. test: >
  1192. <h2>A heading</h2>
  1193. <ul>
  1194. <li>a list</li>
  1195. <li>may be a good example</li>
  1196. </ul>
  1197. EOT;
  1198. $this->assertSame(
  1199. [
  1200. 'test' => <<<'EOT'
  1201. <h2>A heading</h2>
  1202. <ul> <li>a list</li> <li>may be a good example</li> </ul>
  1203. EOT
  1204. ,
  1205. ],
  1206. $this->parser->parse($yaml)
  1207. );
  1208. }
  1209. public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
  1210. {
  1211. $yaml = <<<'EOT'
  1212. test: >
  1213. <h2>A heading</h2>
  1214. <ul>
  1215. <li>a list</li>
  1216. <li>may be a good example</li>
  1217. </ul>
  1218. EOT;
  1219. $this->assertSame(
  1220. [
  1221. 'test' => <<<'EOT'
  1222. <h2>A heading</h2>
  1223. <ul>
  1224. <li>a list</li>
  1225. <li>may be a good example</li>
  1226. </ul>
  1227. EOT
  1228. ,
  1229. ],
  1230. $this->parser->parse($yaml)
  1231. );
  1232. }
  1233. /**
  1234. * @dataProvider getBinaryData
  1235. */
  1236. public function testParseBinaryData($data)
  1237. {
  1238. $this->assertSame(['data' => 'Hello world'], $this->parser->parse($data));
  1239. }
  1240. public function getBinaryData()
  1241. {
  1242. return [
  1243. 'enclosed with double quotes' => ['data: !!binary "SGVsbG8gd29ybGQ="'],
  1244. 'enclosed with single quotes' => ["data: !!binary 'SGVsbG8gd29ybGQ='"],
  1245. 'containing spaces' => ['data: !!binary "SGVs bG8gd 29ybGQ="'],
  1246. 'in block scalar' => [
  1247. <<<'EOT'
  1248. data: !!binary |
  1249. SGVsbG8gd29ybGQ=
  1250. EOT
  1251. ],
  1252. 'containing spaces in block scalar' => [
  1253. <<<'EOT'
  1254. data: !!binary |
  1255. SGVs bG8gd 29ybGQ=
  1256. EOT
  1257. ],
  1258. ];
  1259. }
  1260. /**
  1261. * @dataProvider getInvalidBinaryData
  1262. */
  1263. public function testParseInvalidBinaryData($data, $expectedMessage)
  1264. {
  1265. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1266. $this->expectExceptionMessageMatches($expectedMessage);
  1267. $this->parser->parse($data);
  1268. }
  1269. public function getInvalidBinaryData()
  1270. {
  1271. return [
  1272. 'length not a multiple of four' => ['data: !!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'],
  1273. 'invalid characters' => ['!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'],
  1274. 'too many equals characters' => ['data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'],
  1275. 'misplaced equals character' => ['data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'],
  1276. 'length not a multiple of four in block scalar' => [
  1277. <<<'EOT'
  1278. data: !!binary |
  1279. SGVsbG8d29ybGQ=
  1280. EOT
  1281. ,
  1282. '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/',
  1283. ],
  1284. 'invalid characters in block scalar' => [
  1285. <<<'EOT'
  1286. data: !!binary |
  1287. SGVsbG8#d29ybGQ=
  1288. EOT
  1289. ,
  1290. '/The base64 encoded data \(.*\) contains invalid characters/',
  1291. ],
  1292. 'too many equals characters in block scalar' => [
  1293. <<<'EOT'
  1294. data: !!binary |
  1295. SGVsbG8gd29yb===
  1296. EOT
  1297. ,
  1298. '/The base64 encoded data \(.*\) contains invalid characters/',
  1299. ],
  1300. 'misplaced equals character in block scalar' => [
  1301. <<<'EOT'
  1302. data: !!binary |
  1303. SGVsbG8gd29ybG=Q
  1304. EOT
  1305. ,
  1306. '/The base64 encoded data \(.*\) contains invalid characters/',
  1307. ],
  1308. ];
  1309. }
  1310. public function testParseDateAsMappingValue()
  1311. {
  1312. $yaml = <<<'EOT'
  1313. date: 2002-12-14
  1314. EOT;
  1315. $expectedDate = new \DateTime();
  1316. $expectedDate->setTimeZone(new \DateTimeZone('UTC'));
  1317. $expectedDate->setDate(2002, 12, 14);
  1318. $expectedDate->setTime(0, 0, 0);
  1319. $this->assertEquals(['date' => $expectedDate], $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
  1320. }
  1321. /**
  1322. * @param $lineNumber
  1323. * @param $yaml
  1324. * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
  1325. */
  1326. public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
  1327. {
  1328. $this->expectException('\Symfony\Component\Yaml\Exception\ParseException');
  1329. $this->expectExceptionMessage(sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
  1330. $this->parser->parse($yaml);
  1331. }
  1332. public function parserThrowsExceptionWithCorrectLineNumberProvider()
  1333. {
  1334. return [
  1335. [
  1336. 4,
  1337. <<<'YAML'
  1338. foo:
  1339. -
  1340. # bar
  1341. bar: "123",
  1342. YAML
  1343. ],
  1344. [
  1345. 5,
  1346. <<<'YAML'
  1347. foo:
  1348. -
  1349. # bar
  1350. # bar
  1351. bar: "123",
  1352. YAML
  1353. ],
  1354. [
  1355. 8,
  1356. <<<'YAML'
  1357. foo:
  1358. -
  1359. # foobar
  1360. baz: 123
  1361. bar:
  1362. -
  1363. # bar
  1364. bar: "123",
  1365. YAML
  1366. ],
  1367. [
  1368. 10,
  1369. <<<'YAML'
  1370. foo:
  1371. -
  1372. # foobar
  1373. # foobar
  1374. baz: 123
  1375. bar:
  1376. -
  1377. # bar
  1378. # bar
  1379. bar: "123",
  1380. YAML
  1381. ],
  1382. ];
  1383. }
  1384. public function testParseMultiLineQuotedString()
  1385. {
  1386. $yaml = <<<EOT
  1387. foo: "bar
  1388. baz
  1389. foobar
  1390. foo"
  1391. bar: baz
  1392. EOT;
  1393. $this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml));
  1394. }
  1395. public function testMultiLineQuotedStringWithTrailingBackslash()
  1396. {
  1397. $yaml = <<<YAML
  1398. foobar:
  1399. "foo\
  1400. bar"
  1401. YAML;
  1402. $this->assertSame(['foobar' => 'foobar'], $this->parser->parse($yaml));
  1403. }
  1404. public function testCommentCharactersInMultiLineQuotedStrings()
  1405. {
  1406. $yaml = <<<YAML
  1407. foo:
  1408. foobar: 'foo
  1409. #bar'
  1410. bar: baz
  1411. YAML;
  1412. $expected = [
  1413. 'foo' => [
  1414. 'foobar' => 'foo #bar',
  1415. 'bar' => 'baz',
  1416. ],
  1417. ];
  1418. $this->assertSame($expected, $this->parser->parse($yaml));
  1419. }
  1420. public function testBlankLinesInQuotedMultiLineString()
  1421. {
  1422. $yaml = <<<YAML
  1423. foobar: 'foo
  1424. bar'
  1425. YAML;
  1426. $expected = [
  1427. 'foobar' => "foo\nbar",
  1428. ];
  1429. $this->assertSame($expected, $this->parser->parse($yaml));
  1430. }
  1431. public function testEscapedQuoteInQuotedMultiLineString()
  1432. {
  1433. $yaml = <<<YAML
  1434. foobar: "foo
  1435. \\"bar\\"
  1436. baz"
  1437. YAML;
  1438. $expected = [
  1439. 'foobar' => 'foo "bar" baz',
  1440. ];
  1441. $this->assertSame($expected, $this->parser->parse($yaml));
  1442. }
  1443. public function testBackslashInQuotedMultiLineString()
  1444. {
  1445. $yaml = <<<YAML
  1446. foobar: "foo
  1447. bar\\\\"
  1448. YAML;
  1449. $expected = [
  1450. 'foobar' => 'foo bar\\',
  1451. ];
  1452. $this->assertSame($expected, $this->parser->parse($yaml));
  1453. }
  1454. public function testParseMultiLineUnquotedString()
  1455. {
  1456. $yaml = <<<EOT
  1457. foo: bar
  1458. baz
  1459. foobar
  1460. foo
  1461. bar: baz
  1462. EOT;
  1463. $this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml));
  1464. }
  1465. public function testParseMultiLineString()
  1466. {
  1467. $this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
  1468. }
  1469. /**
  1470. * @dataProvider multiLineDataProvider
  1471. */
  1472. public function testParseMultiLineMappingValue($yaml, $expected, $parseError)
  1473. {
  1474. $this->assertEquals($expected, $this->parser->parse($yaml));
  1475. }
  1476. public function multiLineDataProvider()
  1477. {
  1478. $tests = [];
  1479. $yaml = <<<'EOF'
  1480. foo:
  1481. - bar:
  1482. one
  1483. two
  1484. three
  1485. EOF;
  1486. $expected = [
  1487. 'foo' => [
  1488. [
  1489. 'bar' => "one\ntwo three",
  1490. ],
  1491. ],
  1492. ];
  1493. $tests[] = [$yaml, $expected, false];
  1494. $yaml = <<<'EOF'
  1495. bar
  1496. "foo"
  1497. EOF;
  1498. $expected = 'bar "foo"';
  1499. $tests[] = [$yaml, $expected, false];
  1500. $yaml = <<<'EOF'
  1501. bar
  1502. "foo
  1503. EOF;
  1504. $expected = 'bar "foo';
  1505. $tests[] = [$yaml, $expected, false];
  1506. $yaml = <<<'EOF'
  1507. bar
  1508. 'foo'
  1509. EOF;
  1510. $expected = "bar\n'foo'";
  1511. $tests[] = [$yaml, $expected, false];
  1512. $yaml = <<<'EOF'
  1513. bar
  1514. foo'
  1515. EOF;
  1516. $expected = "bar\nfoo'";
  1517. $tests[] = [$yaml, $expected, false];
  1518. return $tests;
  1519. }
  1520. public function testTaggedInlineMapping()
  1521. {
  1522. $this->assertEquals(new TaggedValue('foo', ['foo' => 'bar']), $this->parser->parse('!foo {foo: bar}', Yaml::PARSE_CUSTOM_TAGS));
  1523. }
  1524. /**
  1525. * @dataProvider taggedValuesProvider
  1526. */
  1527. public function testCustomTagSupport($expected, $yaml)
  1528. {
  1529. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
  1530. }
  1531. public function taggedValuesProvider()
  1532. {
  1533. return [
  1534. 'sequences' => [
  1535. [new TaggedValue('foo', ['yaml']), new TaggedValue('quz', ['bar'])],
  1536. <<<YAML
  1537. - !foo
  1538. - yaml
  1539. - !quz [bar]
  1540. YAML
  1541. ],
  1542. 'mappings' => [
  1543. new TaggedValue('foo', ['foo' => new TaggedValue('quz', ['bar']), 'quz' => new TaggedValue('foo', ['quz' => 'bar'])]),
  1544. <<<YAML
  1545. !foo
  1546. foo: !quz [bar]
  1547. quz: !foo
  1548. quz: bar
  1549. YAML
  1550. ],
  1551. 'inline' => [
  1552. [new TaggedValue('foo', ['foo', 'bar']), new TaggedValue('quz', ['foo' => 'bar', 'quz' => new TaggedValue('bar', ['one' => 'bar'])])],
  1553. <<<YAML
  1554. - !foo [foo, bar]
  1555. - !quz {foo: bar, quz: !bar {one: bar}}
  1556. YAML
  1557. ],
  1558. ];
  1559. }
  1560. public function testCustomTagsDisabled()
  1561. {
  1562. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1563. $this->expectExceptionMessage('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!iterator" at line 1 (near "!iterator [foo]").');
  1564. $this->parser->parse('!iterator [foo]');
  1565. }
  1566. /**
  1567. * @group legacy
  1568. * @expectedDeprecation Using the unquoted scalar value "!iterator foo" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it on line 1.
  1569. */
  1570. public function testUnsupportedTagWithScalar()
  1571. {
  1572. $this->assertEquals('!iterator foo', $this->parser->parse('!iterator foo'));
  1573. }
  1574. public function testExceptionWhenUsingUnsupportedBuiltInTags()
  1575. {
  1576. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1577. $this->expectExceptionMessage('The built-in tag "!!foo" is not implemented at line 1 (near "!!foo").');
  1578. $this->parser->parse('!!foo');
  1579. }
  1580. /**
  1581. * @group legacy
  1582. * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1.
  1583. */
  1584. public function testComplexMappingThrowsParseException()
  1585. {
  1586. $yaml = <<<YAML
  1587. ? "1"
  1588. :
  1589. name: végétalien
  1590. YAML;
  1591. $this->parser->parse($yaml);
  1592. }
  1593. /**
  1594. * @group legacy
  1595. * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 2.
  1596. */
  1597. public function testComplexMappingNestedInMappingThrowsParseException()
  1598. {
  1599. $yaml = <<<YAML
  1600. diet:
  1601. ? "1"
  1602. :
  1603. name: végétalien
  1604. YAML;
  1605. $this->parser->parse($yaml);
  1606. }
  1607. /**
  1608. * @group legacy
  1609. * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1.
  1610. */
  1611. public function testComplexMappingNestedInSequenceThrowsParseException()
  1612. {
  1613. $yaml = <<<YAML
  1614. - ? "1"
  1615. :
  1616. name: végétalien
  1617. YAML;
  1618. $this->parser->parse($yaml);
  1619. }
  1620. public function testParsingIniThrowsException()
  1621. {
  1622. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1623. $this->expectExceptionMessage('Unable to parse at line 1 (near "[parameters]").');
  1624. $ini = <<<INI
  1625. [parameters]
  1626. foo = bar
  1627. bar = %foo%
  1628. INI;
  1629. $this->parser->parse($ini);
  1630. }
  1631. private function loadTestsFromFixtureFiles($testsFile)
  1632. {
  1633. $parser = new Parser();
  1634. $tests = [];
  1635. $files = $parser->parseFile(__DIR__.'/Fixtures/'.$testsFile);
  1636. foreach ($files as $file) {
  1637. $yamls = file_get_contents(__DIR__.'/Fixtures/'.$file.'.yml');
  1638. // split YAMLs documents
  1639. foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
  1640. if (!$yaml) {
  1641. continue;
  1642. }
  1643. $test = $parser->parse($yaml);
  1644. if (isset($test['todo']) && $test['todo']) {
  1645. // TODO
  1646. } else {
  1647. eval('$expected = '.trim($test['php']).';');
  1648. $tests[] = [var_export($expected, true), $test['yaml'], $test['test'], isset($test['deprecated']) ? $test['deprecated'] : false];
  1649. }
  1650. }
  1651. }
  1652. return $tests;
  1653. }
  1654. public function testCanParseVeryLongValue()
  1655. {
  1656. $longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000);
  1657. $trickyVal = ['x' => $longStringWithSpaces];
  1658. $yamlString = Yaml::dump($trickyVal);
  1659. $arrayFromYaml = $this->parser->parse($yamlString);
  1660. $this->assertEquals($trickyVal, $arrayFromYaml);
  1661. }
  1662. public function testParserCleansUpReferencesBetweenRuns()
  1663. {
  1664. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1665. $this->expectExceptionMessage('Reference "foo" does not exist at line 2');
  1666. $yaml = <<<YAML
  1667. foo: &foo
  1668. baz: foobar
  1669. bar:
  1670. <<: *foo
  1671. YAML;
  1672. $this->parser->parse($yaml);
  1673. $yaml = <<<YAML
  1674. bar:
  1675. <<: *foo
  1676. YAML;
  1677. $this->parser->parse($yaml);
  1678. }
  1679. public function testPhpConstantTagMappingKey()
  1680. {
  1681. $yaml = <<<YAML
  1682. transitions:
  1683. !php/const 'Symfony\Component\Yaml\Tests\B::FOO':
  1684. from:
  1685. - !php/const 'Symfony\Component\Yaml\Tests\B::BAR'
  1686. to: !php/const 'Symfony\Component\Yaml\Tests\B::BAZ'
  1687. YAML;
  1688. $expected = [
  1689. 'transitions' => [
  1690. 'foo' => [
  1691. 'from' => [
  1692. 'bar',
  1693. ],
  1694. 'to' => 'baz',
  1695. ],
  1696. ],
  1697. ];
  1698. $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
  1699. }
  1700. /**
  1701. * @group legacy
  1702. * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 2.
  1703. * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 4.
  1704. * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 5.
  1705. */
  1706. public function testDeprecatedPhpConstantTagMappingKey()
  1707. {
  1708. $yaml = <<<YAML
  1709. transitions:
  1710. !php/const:Symfony\Component\Yaml\Tests\B::FOO:
  1711. from:
  1712. - !php/const:Symfony\Component\Yaml\Tests\B::BAR
  1713. to: !php/const:Symfony\Component\Yaml\Tests\B::BAZ
  1714. YAML;
  1715. $expected = [
  1716. 'transitions' => [
  1717. 'foo' => [
  1718. 'from' => [
  1719. 'bar',
  1720. ],
  1721. 'to' => 'baz',
  1722. ],
  1723. ],
  1724. ];
  1725. $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
  1726. }
  1727. /**
  1728. * @group legacy
  1729. * @expectedDeprecation Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.
  1730. */
  1731. public function testPhpConstantTagMappingKeyWithKeysCastToStrings()
  1732. {
  1733. $yaml = <<<YAML
  1734. transitions:
  1735. !php/const 'Symfony\Component\Yaml\Tests\B::FOO':
  1736. from:
  1737. - !php/const 'Symfony\Component\Yaml\Tests\B::BAR'
  1738. to: !php/const 'Symfony\Component\Yaml\Tests\B::BAZ'
  1739. YAML;
  1740. $expected = [
  1741. 'transitions' => [
  1742. 'foo' => [
  1743. 'from' => [
  1744. 'bar',
  1745. ],
  1746. 'to' => 'baz',
  1747. ],
  1748. ],
  1749. ];
  1750. $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT | Yaml::PARSE_KEYS_AS_STRINGS));
  1751. }
  1752. public function testPhpConstantTagMappingAsScalarKey()
  1753. {
  1754. $yaml = <<<YAML
  1755. map1:
  1756. - foo: 'value_0'
  1757. !php/const 'Symfony\Component\Yaml\Tests\B::BAR': 'value_1'
  1758. map2:
  1759. - !php/const 'Symfony\Component\Yaml\Tests\B::FOO': 'value_0'
  1760. bar: 'value_1'
  1761. YAML;
  1762. $this->assertSame([
  1763. 'map1' => [['foo' => 'value_0', 'bar' => 'value_1']],
  1764. 'map2' => [['foo' => 'value_0', 'bar' => 'value_1']],
  1765. ], $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
  1766. }
  1767. public function testTagMappingAsScalarKey()
  1768. {
  1769. $yaml = <<<YAML
  1770. map1:
  1771. - !!str 0: 'value_0'
  1772. !!str 1: 'value_1'
  1773. YAML;
  1774. $this->assertSame([
  1775. 'map1' => [['0' => 'value_0', '1' => 'value_1']],
  1776. ], $this->parser->parse($yaml));
  1777. }
  1778. public function testMergeKeysWhenMappingsAreParsedAsObjects()
  1779. {
  1780. $yaml = <<<YAML
  1781. foo: &FOO
  1782. bar: 1
  1783. bar: &BAR
  1784. baz: 2
  1785. <<: *FOO
  1786. baz:
  1787. baz_foo: 3
  1788. <<:
  1789. baz_bar: 4
  1790. foobar:
  1791. bar: ~
  1792. <<: [*FOO, *BAR]
  1793. YAML;
  1794. $expected = (object) [
  1795. 'foo' => (object) [
  1796. 'bar' => 1,
  1797. ],
  1798. 'bar' => (object) [
  1799. 'baz' => 2,
  1800. 'bar' => 1,
  1801. ],
  1802. 'baz' => (object) [
  1803. 'baz_foo' => 3,
  1804. 'baz_bar' => 4,
  1805. ],
  1806. 'foobar' => (object) [
  1807. 'bar' => null,
  1808. 'baz' => 2,
  1809. ],
  1810. ];
  1811. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  1812. }
  1813. public function testFilenamesAreParsedAsStringsWithoutFlag()
  1814. {
  1815. $file = __DIR__.'/Fixtures/index.yml';
  1816. $this->assertSame($file, $this->parser->parse($file));
  1817. }
  1818. public function testParseFile()
  1819. {
  1820. $this->assertIsArray($this->parser->parseFile(__DIR__.'/Fixtures/index.yml'));
  1821. }
  1822. public function testParsingNonExistentFilesThrowsException()
  1823. {
  1824. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1825. $this->expectExceptionMessageMatches('#^File ".+/Fixtures/nonexistent.yml" does not exist\.$#');
  1826. $this->parser->parseFile(__DIR__.'/Fixtures/nonexistent.yml');
  1827. }
  1828. public function testParsingNotReadableFilesThrowsException()
  1829. {
  1830. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1831. $this->expectExceptionMessageMatches('#^File ".+/Fixtures/not_readable.yml" cannot be read\.$#');
  1832. if ('\\' === \DIRECTORY_SEPARATOR) {
  1833. $this->markTestSkipped('chmod is not supported on Windows');
  1834. }
  1835. if (!getenv('USER') || 'root' === getenv('USER')) {
  1836. $this->markTestSkipped('This test will fail if run under superuser');
  1837. }
  1838. $file = __DIR__.'/Fixtures/not_readable.yml';
  1839. chmod($file, 0200);
  1840. $this->parser->parseFile($file);
  1841. }
  1842. public function testParseReferencesOnMergeKeys()
  1843. {
  1844. $yaml = <<<YAML
  1845. mergekeyrefdef:
  1846. a: foo
  1847. <<: &quux
  1848. b: bar
  1849. c: baz
  1850. mergekeyderef:
  1851. d: quux
  1852. <<: *quux
  1853. YAML;
  1854. $expected = [
  1855. 'mergekeyrefdef' => [
  1856. 'a' => 'foo',
  1857. 'b' => 'bar',
  1858. 'c' => 'baz',
  1859. ],
  1860. 'mergekeyderef' => [
  1861. 'd' => 'quux',
  1862. 'b' => 'bar',
  1863. 'c' => 'baz',
  1864. ],
  1865. ];
  1866. $this->assertSame($expected, $this->parser->parse($yaml));
  1867. }
  1868. public function testParseReferencesOnMergeKeysWithMappingsParsedAsObjects()
  1869. {
  1870. $yaml = <<<YAML
  1871. mergekeyrefdef:
  1872. a: foo
  1873. <<: &quux
  1874. b: bar
  1875. c: baz
  1876. mergekeyderef:
  1877. d: quux
  1878. <<: *quux
  1879. YAML;
  1880. $expected = (object) [
  1881. 'mergekeyrefdef' => (object) [
  1882. 'a' => 'foo',
  1883. 'b' => 'bar',
  1884. 'c' => 'baz',
  1885. ],
  1886. 'mergekeyderef' => (object) [
  1887. 'd' => 'quux',
  1888. 'b' => 'bar',
  1889. 'c' => 'baz',
  1890. ],
  1891. ];
  1892. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  1893. }
  1894. public function testEvalRefException()
  1895. {
  1896. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1897. $this->expectExceptionMessage('Reference "foo" does not exist');
  1898. $yaml = <<<EOE
  1899. foo: { &foo { a: Steve, <<: *foo} }
  1900. EOE;
  1901. $this->parser->parse($yaml);
  1902. }
  1903. /**
  1904. * @dataProvider circularReferenceProvider
  1905. */
  1906. public function testDetectCircularReferences($yaml)
  1907. {
  1908. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1909. $this->expectExceptionMessage('Circular reference [foo, bar, foo] detected');
  1910. $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS);
  1911. }
  1912. public function circularReferenceProvider()
  1913. {
  1914. $tests = [];
  1915. $yaml = <<<YAML
  1916. foo:
  1917. - &foo
  1918. - &bar
  1919. bar: foobar
  1920. baz: *foo
  1921. YAML;
  1922. $tests['sequence'] = [$yaml];
  1923. $yaml = <<<YAML
  1924. foo: &foo
  1925. bar: &bar
  1926. foobar: baz
  1927. baz: *foo
  1928. YAML;
  1929. $tests['mapping'] = [$yaml];
  1930. $yaml = <<<YAML
  1931. foo: &foo
  1932. bar: &bar
  1933. foobar: baz
  1934. <<: *foo
  1935. YAML;
  1936. $tests['mapping with merge key'] = [$yaml];
  1937. return $tests;
  1938. }
  1939. /**
  1940. * @dataProvider indentedMappingData
  1941. */
  1942. public function testParseIndentedMappings($yaml, $expected)
  1943. {
  1944. $this->assertSame($expected, $this->parser->parse($yaml));
  1945. }
  1946. public function indentedMappingData()
  1947. {
  1948. $tests = [];
  1949. $yaml = <<<YAML
  1950. foo:
  1951. - bar: "foobar"
  1952. # A comment
  1953. baz: "foobaz"
  1954. YAML;
  1955. $expected = [
  1956. 'foo' => [
  1957. [
  1958. 'bar' => 'foobar',
  1959. 'baz' => 'foobaz',
  1960. ],
  1961. ],
  1962. ];
  1963. $tests['comment line is first line in indented block'] = [$yaml, $expected];
  1964. $yaml = <<<YAML
  1965. foo:
  1966. - bar:
  1967. # comment
  1968. baz: [1, 2, 3]
  1969. YAML;
  1970. $expected = [
  1971. 'foo' => [
  1972. [
  1973. 'bar' => [
  1974. 'baz' => [1, 2, 3],
  1975. ],
  1976. ],
  1977. ],
  1978. ];
  1979. $tests['mapping value on new line starting with a comment line'] = [$yaml, $expected];
  1980. $yaml = <<<YAML
  1981. foo:
  1982. -
  1983. bar: foobar
  1984. YAML;
  1985. $expected = [
  1986. 'foo' => [
  1987. [
  1988. 'bar' => 'foobar',
  1989. ],
  1990. ],
  1991. ];
  1992. $tests['mapping in sequence starting on a new line'] = [$yaml, $expected];
  1993. $yaml = <<<YAML
  1994. foo:
  1995. bar: baz
  1996. YAML;
  1997. $expected = [
  1998. 'foo' => [
  1999. 'bar' => 'baz',
  2000. ],
  2001. ];
  2002. $tests['blank line at the beginning of an indented mapping value'] = [$yaml, $expected];
  2003. return $tests;
  2004. }
  2005. public function testMultiLineComment()
  2006. {
  2007. $yaml = <<<YAML
  2008. parameters:
  2009. abc
  2010. # Comment
  2011. YAML;
  2012. $this->assertSame(['parameters' => 'abc'], $this->parser->parse($yaml));
  2013. }
  2014. public function testParseValueWithModifiers()
  2015. {
  2016. $yaml = <<<YAML
  2017. parameters:
  2018. abc: |+5 # plus five spaces indent
  2019. one
  2020. two
  2021. three
  2022. four
  2023. five
  2024. YAML;
  2025. $this->assertSame(
  2026. [
  2027. 'parameters' => [
  2028. 'abc' => implode("\n", ['one', 'two', 'three', 'four', 'five']),
  2029. ],
  2030. ],
  2031. $this->parser->parse($yaml)
  2032. );
  2033. }
  2034. public function testParseValueWithNegativeModifiers()
  2035. {
  2036. $yaml = <<<YAML
  2037. parameters:
  2038. abc: |-3 # minus
  2039. one
  2040. two
  2041. three
  2042. four
  2043. five
  2044. YAML;
  2045. $this->assertSame(
  2046. [
  2047. 'parameters' => [
  2048. 'abc' => implode("\n", ['one', 'two', 'three', 'four', 'five']),
  2049. ],
  2050. ],
  2051. $this->parser->parse($yaml)
  2052. );
  2053. }
  2054. /**
  2055. * This is a regression test for a bug where a YAML block with a nested multiline string using | was parsed without
  2056. * a trailing \n when a shorter YAML document was parsed before.
  2057. *
  2058. * When a shorter document was parsed before, the nested string did not have a \n at the end of the string, because
  2059. * the Parser thought it was the end of the file, even though it is not.
  2060. */
  2061. public function testParsingMultipleDocuments()
  2062. {
  2063. $shortDocument = 'foo: bar';
  2064. $longDocument = <<<YAML
  2065. a:
  2066. b: |
  2067. row
  2068. row2
  2069. c: d
  2070. YAML;
  2071. // The first parsing set and fixed the totalNumberOfLines in the Parser before, so parsing the short document here
  2072. // to reproduce the issue. If the issue would not have been fixed, the next assertion will fail
  2073. $this->parser->parse($shortDocument);
  2074. // After the total number of lines has been reset the result will be the same as if a new parser was used
  2075. // (before, there was no \n after row2)
  2076. $this->assertSame(['a' => ['b' => "row\nrow2\n"], 'c' => 'd'], $this->parser->parse($longDocument));
  2077. }
  2078. }
  2079. class B
  2080. {
  2081. public $b = 'foo';
  2082. const FOO = 'foo';
  2083. const BAR = 'bar';
  2084. const BAZ = 'baz';
  2085. }