Converter.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Ui\Config;
  7. use Magento\Framework\Config\ConverterInterface as ConfigConverterInterface;
  8. use Magento\Framework\Config\ReaderInterface;
  9. use Magento\Framework\View\Layout\Argument\Parser;
  10. use Magento\Ui\Config\Argument\ParserInterface;
  11. /**
  12. * Converter for UI Component instances configuration files
  13. */
  14. class Converter implements ConfigConverterInterface
  15. {
  16. /**
  17. * The key attributes of a node
  18. */
  19. const DATA_ATTRIBUTES_KEY = 'attributes';
  20. /**
  21. * The key for the data arguments
  22. */
  23. const DATA_ARGUMENTS_KEY = 'arguments';
  24. /**
  25. * The key of sub components
  26. */
  27. const DATA_COMPONENTS_KEY = 'children';
  28. /**
  29. * The key of the arguments node
  30. */
  31. const ARGUMENT_KEY = 'argument';
  32. /**
  33. * The key of the settings component
  34. */
  35. const SETTINGS_KEY = 'settings';
  36. /**
  37. * Key name attribute value
  38. */
  39. const NAME_ATTRIBUTE_KEY = 'name';
  40. /**
  41. * Key class attribute value
  42. */
  43. const CLASS_ATTRIBUTE_KEY = 'class';
  44. /**
  45. * @var Parser
  46. */
  47. private $argumentParser;
  48. /**
  49. * @var array
  50. */
  51. private $schemaMap = [];
  52. /**
  53. * @var ReaderInterface
  54. */
  55. private $reader;
  56. /**
  57. * @var ParserInterface
  58. */
  59. private $parser;
  60. /**
  61. * @var ConverterUtils
  62. */
  63. private $converterUtils;
  64. /**
  65. * @param Parser $argumentParser
  66. * @param ParserInterface $parser
  67. * @param ReaderInterface $reader
  68. * @param ConverterUtils $converterUtils
  69. */
  70. public function __construct(
  71. Parser $argumentParser,
  72. ParserInterface $parser,
  73. ReaderInterface $reader,
  74. ConverterUtils $converterUtils
  75. ) {
  76. $this->argumentParser = $argumentParser;
  77. $this->reader = $reader;
  78. $this->parser = $parser;
  79. $this->converterUtils = $converterUtils;
  80. }
  81. /**
  82. * Convert nodes and child nodes to array
  83. *
  84. * @param \DOMNode $node
  85. * @return array|string
  86. */
  87. private function toArray(\DOMNode $node)
  88. {
  89. $result = [];
  90. $attributes = [];
  91. // Collect data from attributes
  92. if ($node->hasAttributes()) {
  93. foreach ($node->attributes as $attribute) {
  94. if ($attribute->name == 'noNamespaceSchemaLocation') {
  95. continue;
  96. }
  97. $attributes[$attribute->name] = $attribute->value;
  98. }
  99. }
  100. switch ($node->nodeType) {
  101. case XML_TEXT_NODE:
  102. case XML_COMMENT_NODE:
  103. case XML_CDATA_SECTION_NODE:
  104. break;
  105. default:
  106. if ($node->localName === static::ARGUMENT_KEY) {
  107. if (!isset($attributes[static::NAME_ATTRIBUTE_KEY])) {
  108. throw new \InvalidArgumentException(
  109. 'Attribute "' . static::NAME_ATTRIBUTE_KEY . '" is absent in the attributes node.'
  110. );
  111. }
  112. $result[$attributes[static::NAME_ATTRIBUTE_KEY]] = $this->argumentParser->parse($node);
  113. } else {
  114. $resultComponent = $this->convertNode($node);
  115. list($arguments, $childResult) = $this->convertChildNodes($node);
  116. $result = array_merge(
  117. $this->processArguments($arguments, $resultComponent),
  118. $this->processAttributes($attributes),
  119. $this->processChildResult($node, $childResult)
  120. );
  121. }
  122. break;
  123. }
  124. return $result;
  125. }
  126. /**
  127. * Convert configuration to array
  128. *
  129. * @param \DOMDocument|null $source
  130. * @return array
  131. */
  132. public function convert($source)
  133. {
  134. if ($source === null) {
  135. return [];
  136. }
  137. if (!$this->schemaMap) {
  138. $this->schemaMap = $this->reader->read();
  139. }
  140. $result = $this->toArray($source);
  141. return empty($result) ? $result : reset($result);
  142. }
  143. /**
  144. * Convert and parse node to array according to definition.map.xml
  145. *
  146. * @param \DOMNode $node
  147. * @return array
  148. */
  149. private function convertNode(\DOMNode $node)
  150. {
  151. $resultComponent = [];
  152. if (empty($node->localName)
  153. || !$this->converterUtils->isUiComponent($node)
  154. || !isset($this->schemaMap[$node->localName])
  155. ) {
  156. return $resultComponent;
  157. }
  158. foreach ($this->schemaMap[$node->localName] as $componentData) {
  159. $result = [];
  160. foreach ($componentData as $dataKey => $dataValue) {
  161. $resultParser = $this->parser->parse($dataValue, $node);
  162. if ($resultParser) {
  163. $result[$dataKey] = $resultParser;
  164. }
  165. }
  166. $resultComponent = array_replace_recursive($resultComponent, $result);
  167. }
  168. return $resultComponent;
  169. }
  170. /**
  171. * Process component arguments
  172. *
  173. * @param array $arguments
  174. * @param array $resultComponent
  175. * @return array
  176. */
  177. private function processArguments(array $arguments, array $resultComponent)
  178. {
  179. $result = [];
  180. if (!empty($arguments) || !empty($resultComponent)) {
  181. $result[static::DATA_ARGUMENTS_KEY] = array_replace_recursive($resultComponent, $arguments);
  182. }
  183. return $result;
  184. }
  185. /**
  186. * Process component attributes
  187. *
  188. * @param array $attributes
  189. * @return array
  190. */
  191. private function processAttributes(array $attributes)
  192. {
  193. $result = [];
  194. if (!empty($attributes)) {
  195. $result[static::DATA_ATTRIBUTES_KEY] = $attributes;
  196. }
  197. return $result;
  198. }
  199. /**
  200. * @param \DOMNode $node
  201. * @param array $childResult
  202. * @return array
  203. */
  204. private function processChildResult(\DOMNode $node, array $childResult)
  205. {
  206. $result = [];
  207. if ($node->parentNode !== null) {
  208. $result[static::DATA_COMPONENTS_KEY] = $childResult;
  209. } else {
  210. $result = $childResult;
  211. }
  212. return $result;
  213. }
  214. /**
  215. * Convert child nodes of $node
  216. *
  217. * @param \DOMNode $node
  218. * @return array
  219. */
  220. private function convertChildNodes(\DOMNode $node)
  221. {
  222. $arguments = [];
  223. $childResult = [];
  224. for ($i = 0, $iLength = $node->childNodes->length; $i < $iLength; ++$i) {
  225. $itemNode = $node->childNodes->item($i);
  226. if ($itemNode->localName == null) {
  227. continue;
  228. }
  229. if ($itemNode->localName === static::ARGUMENT_KEY) {
  230. $arguments += $this->toArray($itemNode);
  231. } elseif ($this->converterUtils->isUiComponent($itemNode)
  232. && isset($this->schemaMap[$itemNode->localName])
  233. ) {
  234. $itemNodeName = $this->converterUtils->getComponentName($itemNode);
  235. $childResult[$itemNodeName] = $this->toArray($itemNode);
  236. // 'uiComponentType' is needed this for Reader to merge default values from definition
  237. $childResult[$itemNodeName]['uiComponentType'] = $itemNode->localName;
  238. } else {
  239. continue;
  240. }
  241. }
  242. return [$arguments, $childResult];
  243. }
  244. }