XmlTest.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 1.2.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Utility;
  16. use Cake\Collection\Collection;
  17. use Cake\Core\Configure;
  18. use Cake\ORM\Entity;
  19. use Cake\TestSuite\TestCase;
  20. use Cake\Utility\Exception\XmlException;
  21. use Cake\Utility\Xml;
  22. /**
  23. * XmlTest class
  24. */
  25. class XmlTest extends TestCase
  26. {
  27. /**
  28. * autoFixtures property
  29. *
  30. * @var bool
  31. */
  32. public $autoFixtures = false;
  33. /**
  34. * fixtures property
  35. * @var array
  36. */
  37. public $fixtures = [
  38. 'core.Articles', 'core.Users'
  39. ];
  40. /**
  41. * setUp method
  42. *
  43. * @return void
  44. */
  45. public function setUp()
  46. {
  47. parent::setUp();
  48. $this->_appEncoding = Configure::read('App.encoding');
  49. Configure::write('App.encoding', 'UTF-8');
  50. }
  51. /**
  52. * tearDown method
  53. *
  54. * @return void
  55. */
  56. public function tearDown()
  57. {
  58. parent::tearDown();
  59. Configure::write('App.encoding', $this->_appEncoding);
  60. }
  61. public function testExceptionChainingForInvalidInput()
  62. {
  63. try {
  64. $value = "invalid-xml-input<<";
  65. Xml::build($value);
  66. $this->fail('This line should not be executed because of exception above.');
  67. } catch (XmlException $exception) {
  68. $cause = $exception->getPrevious();
  69. $this->assertNotNull($cause);
  70. $this->assertInstanceOf(\Exception::class, $cause);
  71. }
  72. }
  73. /**
  74. * testBuild method
  75. *
  76. * @return void
  77. */
  78. public function testBuild()
  79. {
  80. $xml = '<tag>value</tag>';
  81. $obj = Xml::build($xml);
  82. $this->assertInstanceOf(\SimpleXMLElement::class, $obj);
  83. $this->assertEquals('tag', (string)$obj->getName());
  84. $this->assertEquals('value', (string)$obj);
  85. $xml = '<?xml version="1.0" encoding="UTF-8"?><tag>value</tag>';
  86. $this->assertEquals($obj, Xml::build($xml));
  87. $obj = Xml::build($xml, ['return' => 'domdocument']);
  88. $this->assertInstanceOf(\DOMDocument::class, $obj);
  89. $this->assertEquals('tag', $obj->firstChild->nodeName);
  90. $this->assertEquals('value', $obj->firstChild->nodeValue);
  91. $xml = CORE_TESTS . 'Fixture/sample.xml';
  92. $obj = Xml::build($xml);
  93. $this->assertEquals('tags', $obj->getName());
  94. $this->assertEquals(2, count($obj));
  95. $this->assertEquals(Xml::build($xml), Xml::build(file_get_contents($xml)));
  96. $obj = Xml::build($xml, ['return' => 'domdocument']);
  97. $this->assertEquals('tags', $obj->firstChild->nodeName);
  98. $this->assertEquals(
  99. Xml::build($xml, ['return' => 'domdocument']),
  100. Xml::build(file_get_contents($xml), ['return' => 'domdocument'])
  101. );
  102. $xml = ['tag' => 'value'];
  103. $obj = Xml::build($xml);
  104. $this->assertEquals('tag', $obj->getName());
  105. $this->assertEquals('value', (string)$obj);
  106. $obj = Xml::build($xml, ['return' => 'domdocument']);
  107. $this->assertEquals('tag', $obj->firstChild->nodeName);
  108. $this->assertEquals('value', $obj->firstChild->nodeValue);
  109. $obj = Xml::build($xml, ['return' => 'domdocument', 'encoding' => null]);
  110. $this->assertNotRegExp('/encoding/', $obj->saveXML());
  111. }
  112. /**
  113. * test build() method with huge option
  114. *
  115. * @return void
  116. */
  117. public function testBuildHuge()
  118. {
  119. $xml = '<tag>value</tag>';
  120. $obj = Xml::build($xml, ['parseHuge' => true]);
  121. $this->assertEquals('tag', $obj->getName());
  122. $this->assertEquals('value', (string)$obj);
  123. }
  124. /**
  125. * Test that the readFile option disables local file parsing.
  126. *
  127. * @return void
  128. */
  129. public function testBuildFromFileWhenDisabled()
  130. {
  131. $this->expectException(\Cake\Utility\Exception\XmlException::class);
  132. $xml = CORE_TESTS . 'Fixture/sample.xml';
  133. $obj = Xml::build($xml, ['readFile' => false]);
  134. }
  135. /**
  136. * Test build() with a Collection instance.
  137. *
  138. * @return void
  139. */
  140. public function testBuildCollection()
  141. {
  142. $xml = new Collection(['tag' => 'value']);
  143. $obj = Xml::build($xml);
  144. $this->assertEquals('tag', $obj->getName());
  145. $this->assertEquals('value', (string)$obj);
  146. $xml = new Collection([
  147. 'response' => [
  148. 'users' => new Collection(['leonardo', 'raphael'])
  149. ]
  150. ]);
  151. $obj = Xml::build($xml);
  152. $this->assertContains('<users>leonardo</users>', $obj->saveXML());
  153. }
  154. /**
  155. * Test build() with ORM\Entity instances wrapped in a Collection.
  156. *
  157. * @return void
  158. */
  159. public function testBuildOrmEntity()
  160. {
  161. $user = new Entity(['username' => 'mark', 'email' => 'mark@example.com']);
  162. $xml = new Collection([
  163. 'response' => [
  164. 'users' => new Collection([$user])
  165. ]
  166. ]);
  167. $obj = Xml::build($xml);
  168. $output = $obj->saveXML();
  169. $this->assertContains('<username>mark</username>', $output);
  170. $this->assertContains('<email>mark@example.com</email>', $output);
  171. }
  172. /**
  173. * data provider function for testBuildInvalidData
  174. *
  175. * @return array
  176. */
  177. public static function invalidDataProvider()
  178. {
  179. return [
  180. [null],
  181. [false],
  182. [''],
  183. ['http://localhost/notthere.xml'],
  184. ];
  185. }
  186. /**
  187. * testBuildInvalidData
  188. *
  189. * @dataProvider invalidDataProvider
  190. * @return void
  191. */
  192. public function testBuildInvalidData($value)
  193. {
  194. $this->expectException(\RuntimeException::class);
  195. Xml::build($value);
  196. }
  197. /**
  198. * Test that building SimpleXmlElement with invalid XML causes the right exception.
  199. *
  200. * @return void
  201. */
  202. public function testBuildInvalidDataSimpleXml()
  203. {
  204. $this->expectException(\Cake\Utility\Exception\XmlException::class);
  205. $input = '<derp';
  206. Xml::build($input, ['return' => 'simplexml']);
  207. }
  208. /**
  209. * test build with a single empty tag
  210. *
  211. * @return void
  212. */
  213. public function testBuildEmptyTag()
  214. {
  215. try {
  216. Xml::build('<tag>');
  217. $this->fail('No exception');
  218. } catch (\Exception $e) {
  219. $this->assertTrue(true, 'An exception was raised');
  220. }
  221. }
  222. /**
  223. * testLoadHtml method
  224. *
  225. * @return void
  226. */
  227. public function testLoadHtml()
  228. {
  229. $htmlFile = CORE_TESTS . 'Fixture/sample.html';
  230. $html = file_get_contents($htmlFile);
  231. $paragraph = 'Browsers usually indent blockquote elements.';
  232. $blockquote = "
  233. For 50 years, WWF has been protecting the future of nature.
  234. The world's leading conservation organization,
  235. WWF works in 100 countries and is supported by
  236. 1.2 million members in the United States and
  237. close to 5 million globally.
  238. ";
  239. $xml = Xml::loadHtml($html);
  240. $this->assertTrue(isset($xml->body->p), 'Paragraph present');
  241. $this->assertEquals($paragraph, (string)$xml->body->p);
  242. $this->assertTrue(isset($xml->body->blockquote), 'Blockquote present');
  243. $this->assertEquals($blockquote, (string)$xml->body->blockquote);
  244. $xml = Xml::loadHtml($html, ['parseHuge' => true]);
  245. $this->assertTrue(isset($xml->body->p), 'Paragraph present');
  246. $this->assertEquals($paragraph, (string)$xml->body->p);
  247. $this->assertTrue(isset($xml->body->blockquote), 'Blockquote present');
  248. $this->assertEquals($blockquote, (string)$xml->body->blockquote);
  249. $xml = Xml::loadHtml($html);
  250. $this->assertEquals($html, "<!DOCTYPE html>\n" . $xml->asXML() . "\n");
  251. $xml = Xml::loadHtml($html, ['return' => 'dom']);
  252. $this->assertEquals($html, $xml->saveHTML());
  253. }
  254. /**
  255. * test loadHtml with a empty html string
  256. *
  257. * @return void
  258. */
  259. public function testLoadHtmlEmptyHtml()
  260. {
  261. $this->expectException(XmlException::class);
  262. Xml::loadHtml(null);
  263. }
  264. /**
  265. * testFromArray method
  266. *
  267. * @return void
  268. */
  269. public function testFromArray()
  270. {
  271. $xml = ['tag' => 'value'];
  272. $obj = Xml::fromArray($xml);
  273. $this->assertEquals('tag', $obj->getName());
  274. $this->assertEquals('value', (string)$obj);
  275. $xml = ['tag' => null];
  276. $obj = Xml::fromArray($xml);
  277. $this->assertEquals('tag', $obj->getName());
  278. $this->assertEquals('', (string)$obj);
  279. $xml = ['tag' => ['@' => 'value']];
  280. $obj = Xml::fromArray($xml);
  281. $this->assertEquals('tag', $obj->getName());
  282. $this->assertEquals('value', (string)$obj);
  283. $xml = [
  284. 'tags' => [
  285. 'tag' => [
  286. [
  287. 'id' => '1',
  288. 'name' => 'defect'
  289. ],
  290. [
  291. 'id' => '2',
  292. 'name' => 'enhancement'
  293. ]
  294. ]
  295. ]
  296. ];
  297. $obj = Xml::fromArray($xml, 'attributes');
  298. $this->assertInstanceOf(\SimpleXMLElement::class, $obj);
  299. $this->assertEquals('tags', $obj->getName());
  300. $this->assertEquals(2, count($obj));
  301. $xmlText = <<<XML
  302. <?xml version="1.0" encoding="UTF-8"?>
  303. <tags>
  304. <tag id="1" name="defect"/>
  305. <tag id="2" name="enhancement"/>
  306. </tags>
  307. XML;
  308. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  309. $obj = Xml::fromArray($xml);
  310. $this->assertInstanceOf(\SimpleXMLElement::class, $obj);
  311. $this->assertEquals('tags', $obj->getName());
  312. $this->assertEquals(2, count($obj));
  313. $xmlText = <<<XML
  314. <?xml version="1.0" encoding="UTF-8"?>
  315. <tags>
  316. <tag>
  317. <id>1</id>
  318. <name>defect</name>
  319. </tag>
  320. <tag>
  321. <id>2</id>
  322. <name>enhancement</name>
  323. </tag>
  324. </tags>
  325. XML;
  326. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  327. $xml = [
  328. 'tags' => [
  329. ]
  330. ];
  331. $obj = Xml::fromArray($xml);
  332. $this->assertEquals('tags', $obj->getName());
  333. $this->assertEquals('', (string)$obj);
  334. $xml = [
  335. 'tags' => [
  336. 'bool' => true,
  337. 'int' => 1,
  338. 'float' => 10.2,
  339. 'string' => 'ok',
  340. 'null' => null,
  341. 'array' => []
  342. ]
  343. ];
  344. $obj = Xml::fromArray($xml, 'tags');
  345. $this->assertEquals(6, count($obj));
  346. $this->assertSame((string)$obj->bool, '1');
  347. $this->assertSame((string)$obj->int, '1');
  348. $this->assertSame((string)$obj->float, '10.2');
  349. $this->assertSame((string)$obj->string, 'ok');
  350. $this->assertSame((string)$obj->null, '');
  351. $this->assertSame((string)$obj->array, '');
  352. $xml = [
  353. 'tags' => [
  354. 'tag' => [
  355. [
  356. '@id' => '1',
  357. 'name' => 'defect'
  358. ],
  359. [
  360. '@id' => '2',
  361. 'name' => 'enhancement'
  362. ]
  363. ]
  364. ]
  365. ];
  366. $obj = Xml::fromArray($xml, 'tags');
  367. $xmlText = <<<XML
  368. <?xml version="1.0" encoding="UTF-8"?>
  369. <tags>
  370. <tag id="1">
  371. <name>defect</name>
  372. </tag>
  373. <tag id="2">
  374. <name>enhancement</name>
  375. </tag>
  376. </tags>
  377. XML;
  378. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  379. $xml = [
  380. 'tags' => [
  381. 'tag' => [
  382. [
  383. '@id' => '1',
  384. 'name' => 'defect',
  385. '@' => 'Tag 1'
  386. ],
  387. [
  388. '@id' => '2',
  389. 'name' => 'enhancement'
  390. ],
  391. ],
  392. '@' => 'All tags'
  393. ]
  394. ];
  395. $obj = Xml::fromArray($xml, 'tags');
  396. $xmlText = <<<XML
  397. <?xml version="1.0" encoding="UTF-8"?>
  398. <tags>All tags<tag id="1">Tag 1<name>defect</name></tag><tag id="2"><name>enhancement</name></tag></tags>
  399. XML;
  400. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  401. $xml = [
  402. 'tags' => [
  403. 'tag' => [
  404. 'id' => 1,
  405. '@' => 'defect'
  406. ]
  407. ]
  408. ];
  409. $obj = Xml::fromArray($xml, 'attributes');
  410. $xmlText = '<' . '?xml version="1.0" encoding="UTF-8"?><tags><tag id="1">defect</tag></tags>';
  411. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  412. }
  413. /**
  414. * Test fromArray() with zero values.
  415. *
  416. * @return void
  417. */
  418. public function testFromArrayZeroValue()
  419. {
  420. $xml = [
  421. 'tag' => [
  422. '@' => 0,
  423. '@test' => 'A test'
  424. ]
  425. ];
  426. $obj = Xml::fromArray($xml);
  427. $xmlText = <<<XML
  428. <?xml version="1.0" encoding="UTF-8"?>
  429. <tag test="A test">0</tag>
  430. XML;
  431. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  432. $xml = [
  433. 'tag' => ['0']
  434. ];
  435. $obj = Xml::fromArray($xml);
  436. $xmlText = <<<XML
  437. <?xml version="1.0" encoding="UTF-8"?>
  438. <tag>0</tag>
  439. XML;
  440. $this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
  441. }
  442. /**
  443. * Test non-sequential keys in list types.
  444. *
  445. * @return void
  446. */
  447. public function testFromArrayNonSequentialKeys()
  448. {
  449. $xmlArray = [
  450. 'Event' => [
  451. [
  452. 'id' => '235',
  453. 'Attribute' => [
  454. 0 => [
  455. 'id' => '9646',
  456. ],
  457. 2 => [
  458. 'id' => '9647',
  459. ]
  460. ]
  461. ]
  462. ]
  463. ];
  464. $obj = Xml::fromArray($xmlArray);
  465. $expected = <<<XML
  466. <?xml version="1.0" encoding="UTF-8"?>
  467. <Event>
  468. <id>235</id>
  469. <Attribute>
  470. <id>9646</id>
  471. </Attribute>
  472. <Attribute>
  473. <id>9647</id>
  474. </Attribute>
  475. </Event>
  476. XML;
  477. $this->assertXmlStringEqualsXmlString($expected, $obj->asXML());
  478. }
  479. /**
  480. * testFromArrayPretty method
  481. *
  482. * @return void
  483. */
  484. public function testFromArrayPretty()
  485. {
  486. $xml = [
  487. 'tags' => [
  488. 'tag' => [
  489. [
  490. 'id' => '1',
  491. 'name' => 'defect'
  492. ],
  493. [
  494. 'id' => '2',
  495. 'name' => 'enhancement'
  496. ]
  497. ]
  498. ]
  499. ];
  500. $expected = <<<XML
  501. <?xml version="1.0" encoding="UTF-8"?>
  502. <tags><tag><id>1</id><name>defect</name></tag><tag><id>2</id><name>enhancement</name></tag></tags>
  503. XML;
  504. $xmlResponse = Xml::fromArray($xml, ['pretty' => false]);
  505. $this->assertTextEquals($expected, $xmlResponse->asXML());
  506. $expected = <<<XML
  507. <?xml version="1.0" encoding="UTF-8"?>
  508. <tags>
  509. <tag>
  510. <id>1</id>
  511. <name>defect</name>
  512. </tag>
  513. <tag>
  514. <id>2</id>
  515. <name>enhancement</name>
  516. </tag>
  517. </tags>
  518. XML;
  519. $xmlResponse = Xml::fromArray($xml, ['pretty' => true]);
  520. $this->assertTextEquals($expected, $xmlResponse->asXML());
  521. $xml = [
  522. 'tags' => [
  523. 'tag' => [
  524. [
  525. 'id' => '1',
  526. 'name' => 'defect'
  527. ],
  528. [
  529. 'id' => '2',
  530. 'name' => 'enhancement'
  531. ]
  532. ]
  533. ]
  534. ];
  535. $expected = <<<XML
  536. <?xml version="1.0" encoding="UTF-8"?>
  537. <tags><tag id="1" name="defect"/><tag id="2" name="enhancement"/></tags>
  538. XML;
  539. $xmlResponse = Xml::fromArray($xml, ['pretty' => false, 'format' => 'attributes']);
  540. $this->assertTextEquals($expected, $xmlResponse->asXML());
  541. $expected = <<<XML
  542. <?xml version="1.0" encoding="UTF-8"?>
  543. <tags>
  544. <tag id="1" name="defect"/>
  545. <tag id="2" name="enhancement"/>
  546. </tags>
  547. XML;
  548. $xmlResponse = Xml::fromArray($xml, ['pretty' => true, 'format' => 'attributes']);
  549. $this->assertTextEquals($expected, $xmlResponse->asXML());
  550. }
  551. /**
  552. * data provider for fromArray() failures
  553. *
  554. * @return array
  555. */
  556. public static function invalidArrayDataProvider()
  557. {
  558. return [
  559. [''],
  560. [null],
  561. [false],
  562. [[]],
  563. [['numeric key as root']],
  564. [['item1' => '', 'item2' => '']],
  565. [['items' => ['item1', 'item2']]],
  566. [[
  567. 'tags' => [
  568. 'tag' => [
  569. [
  570. [
  571. 'string'
  572. ]
  573. ]
  574. ]
  575. ]
  576. ]],
  577. [[
  578. 'tags' => [
  579. '@tag' => [
  580. [
  581. '@id' => '1',
  582. 'name' => 'defect'
  583. ],
  584. [
  585. '@id' => '2',
  586. 'name' => 'enhancement'
  587. ]
  588. ]
  589. ]
  590. ]],
  591. [new \DateTime()]
  592. ];
  593. }
  594. /**
  595. * testFromArrayFail method
  596. *
  597. * @dataProvider invalidArrayDataProvider
  598. * @return void
  599. */
  600. public function testFromArrayFail($value)
  601. {
  602. $this->expectException(\Exception::class);
  603. Xml::fromArray($value);
  604. }
  605. /**
  606. * Test that there are not unterminated errors when building xml
  607. *
  608. * @return void
  609. */
  610. public function testFromArrayUnterminatedError()
  611. {
  612. $data = [
  613. 'product_ID' => 'GENERT-DL',
  614. 'deeplink' => 'http://example.com/deep',
  615. 'image_URL' => 'http://example.com/image',
  616. 'thumbnail_image_URL' => 'http://example.com/thumb',
  617. 'brand' => 'Malte Lange & Co',
  618. 'availability' => 'in stock',
  619. 'authors' => [
  620. 'author' => ['Malte Lange & Co']
  621. ]
  622. ];
  623. $xml = Xml::fromArray(['products' => $data], 'tags');
  624. $expected = <<<XML
  625. <?xml version="1.0" encoding="UTF-8"?>
  626. <products>
  627. <product_ID>GENERT-DL</product_ID>
  628. <deeplink>http://example.com/deep</deeplink>
  629. <image_URL>http://example.com/image</image_URL>
  630. <thumbnail_image_URL>http://example.com/thumb</thumbnail_image_URL>
  631. <brand>Malte Lange &amp; Co</brand>
  632. <availability>in stock</availability>
  633. <authors>
  634. <author>Malte Lange &amp; Co</author>
  635. </authors>
  636. </products>
  637. XML;
  638. $this->assertXmlStringEqualsXmlString($expected, $xml->asXML());
  639. }
  640. /**
  641. * testToArray method
  642. *
  643. * @return void
  644. */
  645. public function testToArray()
  646. {
  647. $xml = '<tag>name</tag>';
  648. $obj = Xml::build($xml);
  649. $this->assertEquals(['tag' => 'name'], Xml::toArray($obj));
  650. $xml = CORE_TESTS . 'Fixture/sample.xml';
  651. $obj = Xml::build($xml);
  652. $expected = [
  653. 'tags' => [
  654. 'tag' => [
  655. [
  656. '@id' => '1',
  657. 'name' => 'defect'
  658. ],
  659. [
  660. '@id' => '2',
  661. 'name' => 'enhancement'
  662. ]
  663. ]
  664. ]
  665. ];
  666. $this->assertEquals($expected, Xml::toArray($obj));
  667. $array = [
  668. 'tags' => [
  669. 'tag' => [
  670. [
  671. 'id' => '1',
  672. 'name' => 'defect'
  673. ],
  674. [
  675. 'id' => '2',
  676. 'name' => 'enhancement'
  677. ]
  678. ]
  679. ]
  680. ];
  681. $this->assertEquals(Xml::toArray(Xml::fromArray($array, 'tags')), $array);
  682. $expected = [
  683. 'tags' => [
  684. 'tag' => [
  685. [
  686. '@id' => '1',
  687. '@name' => 'defect'
  688. ],
  689. [
  690. '@id' => '2',
  691. '@name' => 'enhancement'
  692. ]
  693. ]
  694. ]
  695. ];
  696. $this->assertEquals($expected, Xml::toArray(Xml::fromArray($array, 'attributes')));
  697. $this->assertEquals($expected, Xml::toArray(Xml::fromArray($array, ['return' => 'domdocument', 'format' => 'attributes'])));
  698. $this->assertEquals(Xml::toArray(Xml::fromArray($array)), $array);
  699. $this->assertEquals(Xml::toArray(Xml::fromArray($array, ['return' => 'domdocument'])), $array);
  700. $array = [
  701. 'tags' => [
  702. 'tag' => [
  703. 'id' => '1',
  704. 'posts' => [
  705. ['id' => '1'],
  706. ['id' => '2']
  707. ]
  708. ],
  709. 'tagOther' => [
  710. 'subtag' => [
  711. 'id' => '1'
  712. ]
  713. ]
  714. ]
  715. ];
  716. $expected = [
  717. 'tags' => [
  718. 'tag' => [
  719. '@id' => '1',
  720. 'posts' => [
  721. ['@id' => '1'],
  722. ['@id' => '2']
  723. ]
  724. ],
  725. 'tagOther' => [
  726. 'subtag' => [
  727. '@id' => '1'
  728. ]
  729. ]
  730. ]
  731. ];
  732. $this->assertEquals($expected, Xml::toArray(Xml::fromArray($array, 'attributes')));
  733. $this->assertEquals($expected, Xml::toArray(Xml::fromArray($array, ['format' => 'attributes', 'return' => 'domdocument'])));
  734. $xml = <<<XML
  735. <root>
  736. <tag id="1">defect</tag>
  737. </root>
  738. XML;
  739. $obj = Xml::build($xml);
  740. $expected = [
  741. 'root' => [
  742. 'tag' => [
  743. '@id' => 1,
  744. '@' => 'defect'
  745. ]
  746. ]
  747. ];
  748. $this->assertEquals($expected, Xml::toArray($obj));
  749. $xml = <<<XML
  750. <root>
  751. <table xmlns="http://www.w3.org/TR/html4/"><tr><td>Apples</td><td>Bananas</td></tr></table>
  752. <table xmlns="http://www.cakephp.org"><name>CakePHP</name><license>MIT</license></table>
  753. <table>The book is on the table.</table>
  754. </root>
  755. XML;
  756. $obj = Xml::build($xml);
  757. $expected = [
  758. 'root' => [
  759. 'table' => [
  760. ['tr' => ['td' => ['Apples', 'Bananas']]],
  761. ['name' => 'CakePHP', 'license' => 'MIT'],
  762. 'The book is on the table.'
  763. ]
  764. ]
  765. ];
  766. $this->assertEquals($expected, Xml::toArray($obj));
  767. $xml = <<<XML
  768. <root xmlns:cake="http://www.cakephp.org/">
  769. <tag>defect</tag>
  770. <cake:bug>1</cake:bug>
  771. </root>
  772. XML;
  773. $obj = Xml::build($xml);
  774. $expected = [
  775. 'root' => [
  776. 'tag' => 'defect',
  777. 'cake:bug' => 1
  778. ]
  779. ];
  780. $this->assertEquals($expected, Xml::toArray($obj));
  781. $xml = '<tag type="myType">0</tag>';
  782. $obj = Xml::build($xml);
  783. $expected = [
  784. 'tag' => [
  785. '@type' => 'myType',
  786. '@' => 0
  787. ]
  788. ];
  789. $this->assertEquals($expected, Xml::toArray($obj));
  790. }
  791. /**
  792. * testRss
  793. *
  794. * @return void
  795. */
  796. public function testRss()
  797. {
  798. $rss = file_get_contents(CORE_TESTS . 'Fixture/rss.xml');
  799. $rssAsArray = Xml::toArray(Xml::build($rss));
  800. $this->assertEquals('2.0', $rssAsArray['rss']['@version']);
  801. $this->assertCount(2, $rssAsArray['rss']['channel']['item']);
  802. $atomLink = ['@href' => 'http://bakery.cakephp.org/articles/rss', '@rel' => 'self', '@type' => 'application/rss+xml'];
  803. $this->assertEquals($rssAsArray['rss']['channel']['atom:link'], $atomLink);
  804. $this->assertEquals('http://bakery.cakephp.org/', $rssAsArray['rss']['channel']['link']);
  805. $expected = [
  806. 'title' => 'Alertpay automated sales via IPN',
  807. 'link' => 'http://bakery.cakephp.org/articles/view/alertpay-automated-sales-via-ipn',
  808. 'description' => 'I\'m going to show you how I implemented a payment module via the Alertpay payment processor.',
  809. 'pubDate' => 'Tue, 31 Aug 2010 01:42:00 -0500',
  810. 'guid' => 'http://bakery.cakephp.org/articles/view/alertpay-automated-sales-via-ipn'
  811. ];
  812. $this->assertSame($expected, $rssAsArray['rss']['channel']['item'][1]);
  813. $rss = [
  814. 'rss' => [
  815. 'xmlns:atom' => 'http://www.w3.org/2005/Atom',
  816. '@version' => '2.0',
  817. 'channel' => [
  818. 'atom:link' => [
  819. '@href' => 'http://bakery.cakephp.org/articles/rss',
  820. '@rel' => 'self',
  821. '@type' => 'application/rss+xml'
  822. ],
  823. 'title' => 'The Bakery: ',
  824. 'link' => 'http://bakery.cakephp.org/',
  825. 'description' => 'Recent Articles at The Bakery.',
  826. 'pubDate' => 'Sun, 12 Sep 2010 04:18:26 -0500',
  827. 'item' => [
  828. [
  829. 'title' => 'CakePHP 1.3.4 released',
  830. 'link' => 'http://bakery.cakephp.org/articles/view/cakephp-1-3-4-released'
  831. ],
  832. [
  833. 'title' => 'Wizard Component 1.2 Tutorial',
  834. 'link' => 'http://bakery.cakephp.org/articles/view/wizard-component-1-2-tutorial'
  835. ]
  836. ]
  837. ]
  838. ]
  839. ];
  840. $rssAsSimpleXML = Xml::fromArray($rss);
  841. $xmlText = <<<XML
  842. <?xml version="1.0" encoding="UTF-8"?>
  843. <rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  844. <channel>
  845. <atom:link href="http://bakery.cakephp.org/articles/rss" rel="self" type="application/rss+xml"/>
  846. <title>The Bakery: </title>
  847. <link>http://bakery.cakephp.org/</link>
  848. <description>Recent Articles at The Bakery.</description>
  849. <pubDate>Sun, 12 Sep 2010 04:18:26 -0500</pubDate>
  850. <item>
  851. <title>CakePHP 1.3.4 released</title>
  852. <link>http://bakery.cakephp.org/articles/view/cakephp-1-3-4-released</link>
  853. </item>
  854. <item>
  855. <title>Wizard Component 1.2 Tutorial</title>
  856. <link>http://bakery.cakephp.org/articles/view/wizard-component-1-2-tutorial</link>
  857. </item>
  858. </channel>
  859. </rss>
  860. XML;
  861. $this->assertXmlStringEqualsXmlString($xmlText, $rssAsSimpleXML->asXML());
  862. }
  863. /**
  864. * testXmlRpc
  865. *
  866. * @return void
  867. */
  868. public function testXmlRpc()
  869. {
  870. $xml = Xml::build('<methodCall><methodName>test</methodName><params /></methodCall>');
  871. $expected = [
  872. 'methodCall' => [
  873. 'methodName' => 'test',
  874. 'params' => ''
  875. ]
  876. ];
  877. $this->assertSame($expected, Xml::toArray($xml));
  878. $xml = Xml::build('<methodCall><methodName>test</methodName><params><param><value><array><data><value><int>12</int></value><value><string>Egypt</string></value><value><boolean>0</boolean></value><value><int>-31</int></value></data></array></value></param></params></methodCall>');
  879. $expected = [
  880. 'methodCall' => [
  881. 'methodName' => 'test',
  882. 'params' => [
  883. 'param' => [
  884. 'value' => [
  885. 'array' => [
  886. 'data' => [
  887. 'value' => [
  888. ['int' => '12'],
  889. ['string' => 'Egypt'],
  890. ['boolean' => '0'],
  891. ['int' => '-31']
  892. ]
  893. ]
  894. ]
  895. ]
  896. ]
  897. ]
  898. ]
  899. ];
  900. $this->assertSame($expected, Xml::toArray($xml));
  901. $xmlText = <<<XML
  902. <?xml version="1.0" encoding="UTF-8"?>
  903. <methodResponse>
  904. <params>
  905. <param>
  906. <value>
  907. <array>
  908. <data>
  909. <value>
  910. <int>1</int>
  911. </value>
  912. <value>
  913. <string>testing</string>
  914. </value>
  915. </data>
  916. </array>
  917. </value>
  918. </param>
  919. </params>
  920. </methodResponse>
  921. XML;
  922. $xml = Xml::build($xmlText);
  923. $expected = [
  924. 'methodResponse' => [
  925. 'params' => [
  926. 'param' => [
  927. 'value' => [
  928. 'array' => [
  929. 'data' => [
  930. 'value' => [
  931. ['int' => '1'],
  932. ['string' => 'testing']
  933. ]
  934. ]
  935. ]
  936. ]
  937. ]
  938. ]
  939. ]
  940. ];
  941. $this->assertSame($expected, Xml::toArray($xml));
  942. $xml = Xml::fromArray($expected, 'tags');
  943. $this->assertXmlStringEqualsXmlString($xmlText, $xml->asXML());
  944. }
  945. /**
  946. * testSoap
  947. *
  948. * @return void
  949. */
  950. public function testSoap()
  951. {
  952. $xmlRequest = Xml::build(CORE_TESTS . 'Fixture/soap_request.xml');
  953. $expected = [
  954. 'Envelope' => [
  955. '@soap:encodingStyle' => 'http://www.w3.org/2001/12/soap-encoding',
  956. 'soap:Body' => [
  957. 'm:GetStockPrice' => [
  958. 'm:StockName' => 'IBM'
  959. ]
  960. ]
  961. ]
  962. ];
  963. $this->assertEquals($expected, Xml::toArray($xmlRequest));
  964. $xmlResponse = Xml::build(CORE_TESTS . DS . 'Fixture/soap_response.xml');
  965. $expected = [
  966. 'Envelope' => [
  967. '@soap:encodingStyle' => 'http://www.w3.org/2001/12/soap-encoding',
  968. 'soap:Body' => [
  969. 'm:GetStockPriceResponse' => [
  970. 'm:Price' => '34.5'
  971. ]
  972. ]
  973. ]
  974. ];
  975. $this->assertEquals($expected, Xml::toArray($xmlResponse));
  976. $xml = [
  977. 'soap:Envelope' => [
  978. 'xmlns:soap' => 'http://www.w3.org/2001/12/soap-envelope',
  979. '@soap:encodingStyle' => 'http://www.w3.org/2001/12/soap-encoding',
  980. 'soap:Body' => [
  981. 'xmlns:m' => 'http://www.example.org/stock',
  982. 'm:GetStockPrice' => [
  983. 'm:StockName' => 'IBM'
  984. ]
  985. ]
  986. ]
  987. ];
  988. $xmlRequest = Xml::fromArray($xml, ['encoding' => null]);
  989. $xmlText = <<<XML
  990. <?xml version="1.0"?>
  991. <soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
  992. <soap:Body xmlns:m="http://www.example.org/stock">
  993. <m:GetStockPrice>
  994. <m:StockName>IBM</m:StockName>
  995. </m:GetStockPrice>
  996. </soap:Body>
  997. </soap:Envelope>
  998. XML;
  999. $this->assertXmlStringEqualsXmlString($xmlText, $xmlRequest->asXML());
  1000. }
  1001. /**
  1002. * testNamespace
  1003. *
  1004. * @return void
  1005. */
  1006. public function testNamespace()
  1007. {
  1008. $xml = <<<XML
  1009. <root xmlns:ns="http://cakephp.org">
  1010. <ns:tag id="1">
  1011. <child>good</child>
  1012. <otherchild>bad</otherchild>
  1013. </ns:tag>
  1014. <tag>Tag without ns</tag>
  1015. </root>
  1016. XML;
  1017. $xmlResponse = Xml::build($xml);
  1018. $expected = [
  1019. 'root' => [
  1020. 'ns:tag' => [
  1021. '@id' => '1',
  1022. 'child' => 'good',
  1023. 'otherchild' => 'bad'
  1024. ],
  1025. 'tag' => 'Tag without ns'
  1026. ]
  1027. ];
  1028. $this->assertEquals($expected, Xml::toArray($xmlResponse));
  1029. $xmlResponse = Xml::build('<root xmlns:ns="http://cakephp.org"><ns:tag id="1" /><tag><id>1</id></tag></root>');
  1030. $expected = [
  1031. 'root' => [
  1032. 'ns:tag' => [
  1033. '@id' => '1'
  1034. ],
  1035. 'tag' => [
  1036. 'id' => '1'
  1037. ]
  1038. ]
  1039. ];
  1040. $this->assertEquals($expected, Xml::toArray($xmlResponse));
  1041. $xmlResponse = Xml::build('<root xmlns:ns="http://cakephp.org"><ns:attr>1</ns:attr></root>');
  1042. $expected = [
  1043. 'root' => [
  1044. 'ns:attr' => '1'
  1045. ]
  1046. ];
  1047. $this->assertEquals($expected, Xml::toArray($xmlResponse));
  1048. $xmlResponse = Xml::build('<root><ns:attr xmlns:ns="http://cakephp.org">1</ns:attr></root>');
  1049. $this->assertEquals($expected, Xml::toArray($xmlResponse));
  1050. $xml = [
  1051. 'root' => [
  1052. 'ns:attr' => [
  1053. 'xmlns:ns' => 'http://cakephp.org',
  1054. '@' => 1
  1055. ]
  1056. ]
  1057. ];
  1058. $expected = '<' . '?xml version="1.0" encoding="UTF-8"?><root><ns:attr xmlns:ns="http://cakephp.org">1</ns:attr></root>';
  1059. $xmlResponse = Xml::fromArray($xml);
  1060. $this->assertEquals($expected, str_replace(["\r", "\n"], '', $xmlResponse->asXML()));
  1061. $xml = [
  1062. 'root' => [
  1063. 'tag' => [
  1064. 'xmlns:pref' => 'http://cakephp.org',
  1065. 'pref:item' => [
  1066. 'item 1',
  1067. 'item 2'
  1068. ]
  1069. ]
  1070. ]
  1071. ];
  1072. $expected = <<<XML
  1073. <?xml version="1.0" encoding="UTF-8"?>
  1074. <root>
  1075. <tag xmlns:pref="http://cakephp.org">
  1076. <pref:item>item 1</pref:item>
  1077. <pref:item>item 2</pref:item>
  1078. </tag>
  1079. </root>
  1080. XML;
  1081. $xmlResponse = Xml::fromArray($xml);
  1082. $this->assertXmlStringEqualsXmlString($expected, $xmlResponse->asXML());
  1083. $xml = [
  1084. 'root' => [
  1085. 'tag' => [
  1086. 'xmlns:' => 'http://cakephp.org'
  1087. ]
  1088. ]
  1089. ];
  1090. $expected = '<' . '?xml version="1.0" encoding="UTF-8"?><root><tag xmlns="http://cakephp.org"/></root>';
  1091. $xmlResponse = Xml::fromArray($xml);
  1092. $this->assertXmlStringEqualsXmlString($expected, $xmlResponse->asXML());
  1093. $xml = [
  1094. 'root' => [
  1095. 'xmlns:' => 'http://cakephp.org'
  1096. ]
  1097. ];
  1098. $expected = '<' . '?xml version="1.0" encoding="UTF-8"?><root xmlns="http://cakephp.org"/>';
  1099. $xmlResponse = Xml::fromArray($xml);
  1100. $this->assertXmlStringEqualsXmlString($expected, $xmlResponse->asXML());
  1101. $xml = [
  1102. 'root' => [
  1103. 'xmlns:ns' => 'http://cakephp.org'
  1104. ]
  1105. ];
  1106. $expected = '<' . '?xml version="1.0" encoding="UTF-8"?><root xmlns:ns="http://cakephp.org"/>';
  1107. $xmlResponse = Xml::fromArray($xml);
  1108. $this->assertXmlStringEqualsXmlString($expected, $xmlResponse->asXML());
  1109. }
  1110. /**
  1111. * test that CDATA blocks don't get screwed up by SimpleXml
  1112. *
  1113. * @return void
  1114. */
  1115. public function testCdata()
  1116. {
  1117. $xml = '<' . '?xml version="1.0" encoding="UTF-8"?>' .
  1118. '<people><name><![CDATA[ Mark ]]></name></people>';
  1119. $result = Xml::build($xml);
  1120. $this->assertEquals(' Mark ', (string)$result->name);
  1121. }
  1122. /**
  1123. * data provider for toArray() failures
  1124. *
  1125. * @return array
  1126. */
  1127. public static function invalidToArrayDataProvider()
  1128. {
  1129. return [
  1130. [new \DateTime()],
  1131. [[]]
  1132. ];
  1133. }
  1134. /**
  1135. * testToArrayFail method
  1136. *
  1137. * @dataProvider invalidToArrayDataProvider
  1138. * @return void
  1139. */
  1140. public function testToArrayFail($value)
  1141. {
  1142. $this->expectException(\Cake\Utility\Exception\XmlException::class);
  1143. Xml::toArray($value);
  1144. }
  1145. /**
  1146. * Test ampersand in text elements.
  1147. *
  1148. * @return void
  1149. */
  1150. public function testAmpInText()
  1151. {
  1152. $data = [
  1153. 'outer' => [
  1154. 'inner' => ['name' => 'mark & mark']
  1155. ]
  1156. ];
  1157. $obj = Xml::build($data);
  1158. $result = $obj->asXml();
  1159. $this->assertContains('mark &amp; mark', $result);
  1160. }
  1161. /**
  1162. * Test that entity loading is disabled by default.
  1163. *
  1164. * @return void
  1165. */
  1166. public function testNoEntityLoading()
  1167. {
  1168. $file = str_replace(' ', '%20', CAKE . 'VERSION.txt');
  1169. $xml = <<<XML
  1170. <!DOCTYPE cakephp [
  1171. <!ENTITY payload SYSTEM "file://$file" >]>
  1172. <request>
  1173. <xxe>&payload;</xxe>
  1174. </request>
  1175. XML;
  1176. $result = Xml::build($xml);
  1177. $this->assertEquals('', (string)$result->xxe);
  1178. }
  1179. /**
  1180. * Test building Xml with valid class-name in value.
  1181. *
  1182. * @see https://github.com/cakephp/cakephp/pull/9754
  1183. * @return void
  1184. */
  1185. public function testClassnameInValueRegressionTest()
  1186. {
  1187. $classname = self::class; // Will always be a valid class name
  1188. $data = [
  1189. 'outer' => [
  1190. 'inner' => $classname
  1191. ]
  1192. ];
  1193. $obj = Xml::build($data);
  1194. $result = $obj->asXml();
  1195. $this->assertContains('<inner>' . $classname . '</inner>', $result);
  1196. }
  1197. /**
  1198. * Needed function for testClassnameInValueRegressionTest.
  1199. *
  1200. * @ignore
  1201. * @return array
  1202. */
  1203. public function toArray()
  1204. {
  1205. return [];
  1206. }
  1207. }