123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Ui\Config\Reader;
- use Magento\Framework\Config\SchemaLocatorInterface;
- use Magento\Framework\Config\ValidationStateInterface;
- use Magento\Ui\Config\Converter;
- use Magento\Framework\Config\Dom as ConfigDom;
- /**
- * UI Component configuration file DOM object representation
- */
- class Dom extends ConfigDom
- {
- /**
- * Id attribute list
- *
- * @var array
- */
- private $idAttributes = [];
- /**
- * @var \DOMXPath
- */
- private $domXPath;
- /**
- * Path to corresponding XSD file with validation rules for separate config files
- *
- * @var string
- */
- private $schemaFile;
- /**
- * @var SchemaLocatorInterface
- */
- private $schemaLocator;
- /**
- * Dom constructor
- *
- * @param string $xml
- * @param ValidationStateInterface $validationState
- * @param SchemaLocatorInterface $schemaLocator
- * @param array $idAttributes
- * @param null $typeAttributeName
- * @param string $errorFormat
- */
- public function __construct(
- $xml,
- ValidationStateInterface $validationState,
- SchemaLocatorInterface $schemaLocator,
- array $idAttributes = [],
- $typeAttributeName = null,
- $errorFormat = ConfigDom::ERROR_FORMAT_DEFAULT
- ) {
- $this->idAttributes = array_values($idAttributes);
- $this->schemaFile = $schemaLocator->getPerFileSchema() && $validationState->isValidationRequired()
- ? $schemaLocator->getPerFileSchema() : null;
- parent::__construct($xml, $validationState, $idAttributes, $typeAttributeName, $this->schemaFile, $errorFormat);
- $this->schemaLocator = $schemaLocator;
- }
- /**
- * Merge $xml into DOM document
- *
- * @param string $xml
- * @return void
- */
- public function merge($xml)
- {
- $dom = $this->_initDom($xml);
- $this->domXPath = new \DOMXPath($this->getDom());
- $this->nestedMerge($this->getDom()->documentElement, $dom->childNodes);
- }
- /**
- * Merge nested xml nodes
- *
- * @param \DOMNode $contextNode
- * @param \DOMNodeList $insertedNodes
- * @return void
- */
- private function nestedMerge(\DOMNode $contextNode, \DOMNodeList $insertedNodes)
- {
- foreach ($insertedNodes as $insertedItem) {
- switch ($insertedItem->nodeType) {
- case XML_COMMENT_NODE:
- break;
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- if (trim($insertedItem->textContent) !== '') {
- $importNode = $this->getDom()->importNode($insertedItem, true);
- $contextNode->insertBefore($importNode);
- }
- break;
- default:
- $insertedXPath = $this->createXPath($insertedItem);
- $rootMatchList = $this->domXPath->query($insertedXPath);
- $jLength = $rootMatchList->length;
- if ($jLength > 0) {
- $this->processMatchedNodes($rootMatchList, $insertedItem);
- } else {
- $this->appendNode($insertedItem, $contextNode);
- }
- break;
- }
- }
- }
- /**
- * Merge node to matched root elements
- *
- * @param \DOMNodeList $rootMatchList
- * @param \DOMElement $insertedItem
- * @return void
- */
- private function processMatchedNodes(\DOMNodeList $rootMatchList, \DOMElement $insertedItem)
- {
- foreach ($rootMatchList as $rootItem) {
- if ($this->_isTextNode($insertedItem) && $this->_isTextNode($rootItem)) {
- $rootItem->nodeValue = $insertedItem->nodeValue;
- } else {
- $this->nestedMerge($rootItem, $insertedItem->childNodes);
- $this->_mergeAttributes($rootItem, $insertedItem);
- }
- }
- }
- /**
- * Create XPath from node
- *
- * @param \DOMNode $node
- * @return string
- */
- private function createXPath(\DOMNode $node)
- {
- $parentXPath = '';
- $currentXPath = $node->getNodePath();
- if ($node->parentNode !== null && !$node->isSameNode($node->parentNode)) {
- $parentXPath = $this->createXPath($node->parentNode);
- $pathParts = explode('/', $currentXPath);
- $currentXPath = '/' . end($pathParts);
- }
- $attributesXPath = '';
- if ($node->hasAttributes()) {
- $attributes = [];
- foreach ($node->attributes as $name => $attribute) {
- if (in_array($name, $this->idAttributes)) {
- $attributes[] = sprintf('@%s="%s"', $name, $attribute->value);
- break;
- }
- }
- if (!empty($attributes)) {
- if (substr($currentXPath, -1) === ']') {
- $currentXPath = substr($currentXPath, 0, strrpos($currentXPath, '['));
- }
- $attributesXPath = '[' . implode(' and ', $attributes) . ']';
- }
- }
- return '/' . trim($parentXPath . $currentXPath . $attributesXPath, '/');
- }
- /**
- * Append $insertedNode to $contextNode
- *
- * @param \DOMNode $insertedNode
- * @param \DOMNode $contextNode
- * @return void
- */
- private function appendNode(\DOMNode $insertedNode, \DOMNode $contextNode)
- {
- $importNode = $this->getDom()->importNode($insertedNode, true);
- if (in_array($importNode->localName, [Converter::ARGUMENT_KEY, Converter::SETTINGS_KEY])) {
- $this->appendNodeToContext($contextNode, $importNode);
- } else {
- $contextNode->appendChild($importNode);
- }
- }
- /**
- * Append node to context node in correct position
- *
- * @param \DOMNode $contextNode
- * @param \DOMNode $importNode
- * @return void
- */
- private function appendNodeToContext(\DOMNode $contextNode, \DOMNode $importNode)
- {
- if (!$contextNode->hasChildNodes()) {
- $contextNode->appendChild($importNode);
- return;
- }
- $childContextNode = null;
- /** @var \DOMNode $child */
- foreach ($contextNode->childNodes as $child) {
- if ($child->nodeType != XML_ELEMENT_NODE) {
- continue;
- }
- switch ($child->localName) {
- case Converter::ARGUMENT_KEY:
- $childContextNode = $child->nextSibling;
- break;
- case Converter::SETTINGS_KEY:
- $childContextNode = $child;
- break;
- default:
- if (!$childContextNode) {
- $childContextNode = $child;
- }
- break;
- }
- }
- $contextNode->insertBefore($importNode, $childContextNode);
- }
- }
|