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