ParserTest.php 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346
  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->expectExceptionMessageRegExp('/^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->expectExceptionMessageRegExp($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 testMergeKeysWhenMappingsAreParsedAsObjects()
  1753. {
  1754. $yaml = <<<YAML
  1755. foo: &FOO
  1756. bar: 1
  1757. bar: &BAR
  1758. baz: 2
  1759. <<: *FOO
  1760. baz:
  1761. baz_foo: 3
  1762. <<:
  1763. baz_bar: 4
  1764. foobar:
  1765. bar: ~
  1766. <<: [*FOO, *BAR]
  1767. YAML;
  1768. $expected = (object) [
  1769. 'foo' => (object) [
  1770. 'bar' => 1,
  1771. ],
  1772. 'bar' => (object) [
  1773. 'baz' => 2,
  1774. 'bar' => 1,
  1775. ],
  1776. 'baz' => (object) [
  1777. 'baz_foo' => 3,
  1778. 'baz_bar' => 4,
  1779. ],
  1780. 'foobar' => (object) [
  1781. 'bar' => null,
  1782. 'baz' => 2,
  1783. ],
  1784. ];
  1785. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  1786. }
  1787. public function testFilenamesAreParsedAsStringsWithoutFlag()
  1788. {
  1789. $file = __DIR__.'/Fixtures/index.yml';
  1790. $this->assertSame($file, $this->parser->parse($file));
  1791. }
  1792. public function testParseFile()
  1793. {
  1794. $this->assertIsArray($this->parser->parseFile(__DIR__.'/Fixtures/index.yml'));
  1795. }
  1796. public function testParsingNonExistentFilesThrowsException()
  1797. {
  1798. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1799. $this->expectExceptionMessageRegExp('#^File ".+/Fixtures/nonexistent.yml" does not exist\.$#');
  1800. $this->parser->parseFile(__DIR__.'/Fixtures/nonexistent.yml');
  1801. }
  1802. public function testParsingNotReadableFilesThrowsException()
  1803. {
  1804. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1805. $this->expectExceptionMessageRegExp('#^File ".+/Fixtures/not_readable.yml" cannot be read\.$#');
  1806. if ('\\' === \DIRECTORY_SEPARATOR) {
  1807. $this->markTestSkipped('chmod is not supported on Windows');
  1808. }
  1809. if (!getenv('USER') || 'root' === getenv('USER')) {
  1810. $this->markTestSkipped('This test will fail if run under superuser');
  1811. }
  1812. $file = __DIR__.'/Fixtures/not_readable.yml';
  1813. chmod($file, 0200);
  1814. $this->parser->parseFile($file);
  1815. }
  1816. public function testParseReferencesOnMergeKeys()
  1817. {
  1818. $yaml = <<<YAML
  1819. mergekeyrefdef:
  1820. a: foo
  1821. <<: &quux
  1822. b: bar
  1823. c: baz
  1824. mergekeyderef:
  1825. d: quux
  1826. <<: *quux
  1827. YAML;
  1828. $expected = [
  1829. 'mergekeyrefdef' => [
  1830. 'a' => 'foo',
  1831. 'b' => 'bar',
  1832. 'c' => 'baz',
  1833. ],
  1834. 'mergekeyderef' => [
  1835. 'd' => 'quux',
  1836. 'b' => 'bar',
  1837. 'c' => 'baz',
  1838. ],
  1839. ];
  1840. $this->assertSame($expected, $this->parser->parse($yaml));
  1841. }
  1842. public function testParseReferencesOnMergeKeysWithMappingsParsedAsObjects()
  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 = (object) [
  1855. 'mergekeyrefdef' => (object) [
  1856. 'a' => 'foo',
  1857. 'b' => 'bar',
  1858. 'c' => 'baz',
  1859. ],
  1860. 'mergekeyderef' => (object) [
  1861. 'd' => 'quux',
  1862. 'b' => 'bar',
  1863. 'c' => 'baz',
  1864. ],
  1865. ];
  1866. $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
  1867. }
  1868. public function testEvalRefException()
  1869. {
  1870. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1871. $this->expectExceptionMessage('Reference "foo" does not exist');
  1872. $yaml = <<<EOE
  1873. foo: { &foo { a: Steve, <<: *foo} }
  1874. EOE;
  1875. $this->parser->parse($yaml);
  1876. }
  1877. /**
  1878. * @dataProvider circularReferenceProvider
  1879. */
  1880. public function testDetectCircularReferences($yaml)
  1881. {
  1882. $this->expectException('Symfony\Component\Yaml\Exception\ParseException');
  1883. $this->expectExceptionMessage('Circular reference [foo, bar, foo] detected');
  1884. $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS);
  1885. }
  1886. public function circularReferenceProvider()
  1887. {
  1888. $tests = [];
  1889. $yaml = <<<YAML
  1890. foo:
  1891. - &foo
  1892. - &bar
  1893. bar: foobar
  1894. baz: *foo
  1895. YAML;
  1896. $tests['sequence'] = [$yaml];
  1897. $yaml = <<<YAML
  1898. foo: &foo
  1899. bar: &bar
  1900. foobar: baz
  1901. baz: *foo
  1902. YAML;
  1903. $tests['mapping'] = [$yaml];
  1904. $yaml = <<<YAML
  1905. foo: &foo
  1906. bar: &bar
  1907. foobar: baz
  1908. <<: *foo
  1909. YAML;
  1910. $tests['mapping with merge key'] = [$yaml];
  1911. return $tests;
  1912. }
  1913. /**
  1914. * @dataProvider indentedMappingData
  1915. */
  1916. public function testParseIndentedMappings($yaml, $expected)
  1917. {
  1918. $this->assertSame($expected, $this->parser->parse($yaml));
  1919. }
  1920. public function indentedMappingData()
  1921. {
  1922. $tests = [];
  1923. $yaml = <<<YAML
  1924. foo:
  1925. - bar: "foobar"
  1926. # A comment
  1927. baz: "foobaz"
  1928. YAML;
  1929. $expected = [
  1930. 'foo' => [
  1931. [
  1932. 'bar' => 'foobar',
  1933. 'baz' => 'foobaz',
  1934. ],
  1935. ],
  1936. ];
  1937. $tests['comment line is first line in indented block'] = [$yaml, $expected];
  1938. $yaml = <<<YAML
  1939. foo:
  1940. - bar:
  1941. # comment
  1942. baz: [1, 2, 3]
  1943. YAML;
  1944. $expected = [
  1945. 'foo' => [
  1946. [
  1947. 'bar' => [
  1948. 'baz' => [1, 2, 3],
  1949. ],
  1950. ],
  1951. ],
  1952. ];
  1953. $tests['mapping value on new line starting with a comment line'] = [$yaml, $expected];
  1954. $yaml = <<<YAML
  1955. foo:
  1956. -
  1957. bar: foobar
  1958. YAML;
  1959. $expected = [
  1960. 'foo' => [
  1961. [
  1962. 'bar' => 'foobar',
  1963. ],
  1964. ],
  1965. ];
  1966. $tests['mapping in sequence starting on a new line'] = [$yaml, $expected];
  1967. $yaml = <<<YAML
  1968. foo:
  1969. bar: baz
  1970. YAML;
  1971. $expected = [
  1972. 'foo' => [
  1973. 'bar' => 'baz',
  1974. ],
  1975. ];
  1976. $tests['blank line at the beginning of an indented mapping value'] = [$yaml, $expected];
  1977. return $tests;
  1978. }
  1979. public function testMultiLineComment()
  1980. {
  1981. $yaml = <<<YAML
  1982. parameters:
  1983. abc
  1984. # Comment
  1985. YAML;
  1986. $this->assertSame(['parameters' => 'abc'], $this->parser->parse($yaml));
  1987. }
  1988. public function testParseValueWithModifiers()
  1989. {
  1990. $yaml = <<<YAML
  1991. parameters:
  1992. abc: |+5 # plus five spaces indent
  1993. one
  1994. two
  1995. three
  1996. four
  1997. five
  1998. YAML;
  1999. $this->assertSame(
  2000. [
  2001. 'parameters' => [
  2002. 'abc' => implode("\n", ['one', 'two', 'three', 'four', 'five']),
  2003. ],
  2004. ],
  2005. $this->parser->parse($yaml)
  2006. );
  2007. }
  2008. public function testParseValueWithNegativeModifiers()
  2009. {
  2010. $yaml = <<<YAML
  2011. parameters:
  2012. abc: |-3 # minus
  2013. one
  2014. two
  2015. three
  2016. four
  2017. five
  2018. YAML;
  2019. $this->assertSame(
  2020. [
  2021. 'parameters' => [
  2022. 'abc' => implode("\n", ['one', 'two', 'three', 'four', 'five']),
  2023. ],
  2024. ],
  2025. $this->parser->parse($yaml)
  2026. );
  2027. }
  2028. }
  2029. class B
  2030. {
  2031. public $b = 'foo';
  2032. const FOO = 'foo';
  2033. const BAR = 'bar';
  2034. const BAZ = 'baz';
  2035. }