Xml.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. /**
  3. * XML Renderer allows to format array or object as valid XML document.
  4. *
  5. * Copyright © Magento, Inc. All rights reserved.
  6. * See COPYING.txt for license details.
  7. */
  8. namespace Magento\Framework\Webapi\Rest\Response\Renderer;
  9. /**
  10. * Renders response data in Xml format.
  11. */
  12. class Xml implements \Magento\Framework\Webapi\Rest\Response\RendererInterface
  13. {
  14. /**
  15. * Renderer mime type.
  16. */
  17. const MIME_TYPE = 'application/xml';
  18. /**
  19. * Root node in XML output.
  20. */
  21. const XML_ROOT_NODE = 'response';
  22. /**
  23. * This value is used to replace numeric keys while formatting data for XML output.
  24. */
  25. const DEFAULT_ENTITY_ITEM_NAME = 'item';
  26. /**
  27. * @var \Magento\Framework\Xml\Generator
  28. */
  29. protected $_xmlGenerator;
  30. /**
  31. * Initialize dependencies.
  32. *
  33. * @param \Magento\Framework\Xml\Generator $xmlGenerator
  34. */
  35. public function __construct(\Magento\Framework\Xml\Generator $xmlGenerator)
  36. {
  37. $this->_xmlGenerator = $xmlGenerator;
  38. }
  39. /**
  40. * Get XML renderer MIME type.
  41. *
  42. * @return string
  43. */
  44. public function getMimeType()
  45. {
  46. return self::MIME_TYPE;
  47. }
  48. /**
  49. * Format object|array to valid XML.
  50. *
  51. * @param object|array|int|string|bool|float|null $data
  52. * @return string
  53. */
  54. public function render($data)
  55. {
  56. $formattedData = $this->_formatData($data, true);
  57. /** Wrap response in a single node. */
  58. $formattedData = [self::XML_ROOT_NODE => $formattedData];
  59. $this->_xmlGenerator->setIndexedArrayItemName(self::DEFAULT_ENTITY_ITEM_NAME)->arrayToXml($formattedData);
  60. return $this->_xmlGenerator->getDom()->saveXML();
  61. }
  62. /**
  63. * Reformat mixed data to multidimensional array.
  64. *
  65. * This method is recursive.
  66. *
  67. * @param array|\Magento\Framework\DataObject $data
  68. * @param bool $isRoot
  69. * @return array
  70. * @throws \InvalidArgumentException
  71. */
  72. protected function _formatData($data, $isRoot = false)
  73. {
  74. if (!is_array($data) && !is_object($data)) {
  75. if ($isRoot) {
  76. return $this->_formatValue($data);
  77. }
  78. } elseif ($data instanceof \Magento\Framework\DataObject) {
  79. $data = $data->toArray();
  80. } else {
  81. $data = (array)$data;
  82. }
  83. $isAssoc = !preg_match('/^\d+$/', implode(array_keys($data), ''));
  84. $formattedData = [];
  85. foreach ($data as $key => $value) {
  86. $value = is_array($value) || is_object($value) ? $this->_formatData($value) : $this->_formatValue($value);
  87. if ($isAssoc) {
  88. $formattedData[$this->_prepareKey($key)] = $value;
  89. } else {
  90. $formattedData[] = $value;
  91. }
  92. }
  93. return $formattedData;
  94. }
  95. /**
  96. * Prepare value in contrast with key.
  97. *
  98. * @param string $value
  99. * @return string
  100. */
  101. protected function _formatValue($value)
  102. {
  103. if (is_bool($value)) {
  104. /** Without the following transformation boolean values are rendered incorrectly */
  105. $value = $value ? 'true' : 'false';
  106. }
  107. return (string) $value;
  108. }
  109. /**
  110. * Format array key or field name to be valid array key name.
  111. *
  112. * Replaces characters that are invalid in array key names.
  113. *
  114. * @param string $key
  115. * @return string
  116. */
  117. protected function _prepareKey($key)
  118. {
  119. $replacementMap = [
  120. '!' => '',
  121. '"' => '',
  122. '#' => '',
  123. '$' => '',
  124. '%' => '',
  125. '&' => '',
  126. '\'' => '',
  127. '(' => '',
  128. ')' => '',
  129. '*' => '',
  130. '+' => '',
  131. ',' => '',
  132. '/' => '',
  133. ';' => '',
  134. '<' => '',
  135. '=' => '',
  136. '>' => '',
  137. '?' => '',
  138. '@' => '',
  139. '[' => '',
  140. '\\' => '',
  141. ']' => '',
  142. '^' => '',
  143. '`' => '',
  144. '{' => '',
  145. '|' => '',
  146. '}' => '',
  147. '~' => '',
  148. ' ' => '_',
  149. ':' => '_',
  150. ];
  151. $key = str_replace(array_keys($replacementMap), array_values($replacementMap), $key);
  152. $key = trim($key, '_');
  153. $prohibitedTagPattern = '/^[0-9,.-]/';
  154. if (preg_match($prohibitedTagPattern, $key)) {
  155. $key = self::DEFAULT_ENTITY_ITEM_NAME . '_' . $key;
  156. }
  157. return $key;
  158. }
  159. }