isXsl($node, 'when')) { $branch = XPathHelper::parseEqualityExpr($node->getAttribute('test')); if ($branch === false || count($branch) !== 1) { // The expression is not entirely composed of equalities, or they have a different // variable part break; } if (isset($key) && key($branch) !== $key) { // Not the same variable as our branches break; } if (array_intersect($values, end($branch))) { // Duplicate values across branches, e.g. ".=1 or .=2" and ".=2 or .=3" break; } $key = key($branch); $values = array_merge($values, end($branch)); // Record this node then move on to the next sibling $nodes[] = $node; $node = $node->nextSibling; } return $nodes; } /** * Merge identical xsl:when elements from a list * * @param DOMElement[] $nodes * @return void */ protected function mergeBranches(array $nodes) { $sortedNodes = []; foreach ($nodes as $node) { $outerXML = $node->ownerDocument->saveXML($node); $innerXML = preg_replace('([^>]+>(.*)<[^<]+)s', '$1', $outerXML); $sortedNodes[$innerXML][] = $node; } foreach ($sortedNodes as $identicalNodes) { if (count($identicalNodes) < 2) { continue; } $expr = []; foreach ($identicalNodes as $i => $node) { $expr[] = $node->getAttribute('test'); if ($i > 0) { $node->parentNode->removeChild($node); } } $identicalNodes[0]->setAttribute('test', implode(' or ', $expr)); } } /** * Inspect the branches of an xsl:choose element and merge branches if their content is identical * and their order does not matter * * @param DOMElement $choose xsl:choose element * @return void */ protected function mergeCompatibleBranches(DOMElement $choose) { $node = $choose->firstChild; while ($node) { $nodes = $this->collectCompatibleBranches($node); if (count($nodes) > 1) { $node = end($nodes)->nextSibling; // Try to merge branches if there's more than one of them $this->mergeBranches($nodes); } else { $node = $node->nextSibling; } } } /** * Inspect the branches of an xsl:choose element and merge consecutive branches if their content * is identical * * @param DOMElement $choose xsl:choose element * @return void */ protected function mergeConsecutiveBranches(DOMElement $choose) { // Try to merge consecutive branches even if their test conditions are not compatible, // e.g. "@a=1" and "@b=2" $nodes = []; foreach ($choose->childNodes as $node) { if ($this->isXsl($node, 'when')) { $nodes[] = $node; } } $i = count($nodes); while (--$i > 0) { $this->mergeBranches([$nodes[$i - 1], $nodes[$i]]); } } /** * {@inheritdoc} */ protected function normalizeElement(DOMElement $element) { $this->mergeCompatibleBranches($element); $this->mergeConsecutiveBranches($element); } }