ParserTest.php 43 KB

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