[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2022 The s9e authors 6 * @license http://www.opensource.org/licenses/mit-license.php The MIT License 7 */ 8 namespace s9e\TextFormatter\Configurator\Helpers\TemplateParser; 9 10 use DOMDocument; 11 use DOMElement; 12 use DOMNode; 13 14 class Optimizer extends IRProcessor 15 { 16 /** 17 * Optimize an IR 18 * 19 * @param DOMDocument $ir 20 * @return void 21 */ 22 public function optimize(DOMDocument $ir) 23 { 24 $this->createXPath($ir); 25 26 // Get a snapshot of current internal representation 27 $xml = $ir->saveXML(); 28 29 // Set a maximum number of loops to ward against infinite loops 30 $remainingLoops = 10; 31 32 // From now on, keep looping until no further modifications are applied 33 do 34 { 35 $old = $xml; 36 $this->optimizeCloseTagElements(); 37 $xml = $ir->saveXML(); 38 } 39 while (--$remainingLoops > 0 && $xml !== $old); 40 41 $this->removeCloseTagSiblings(); 42 $this->removeContentFromVoidElements(); 43 $this->mergeConsecutiveLiteralOutputElements(); 44 $this->removeEmptyDefaultCases(); 45 } 46 47 /** 48 * Clone closeTag elements that follow a switch into said switch 49 * 50 * If there's a <closeTag/> right after a <switch/>, clone the <closeTag/> at the end of 51 * the every <case/> that does not end with a <closeTag/> 52 * 53 * @return void 54 */ 55 protected function cloneCloseTagElementsIntoSwitch() 56 { 57 $query = '//switch[name(following-sibling::*[1]) = "closeTag"]'; 58 foreach ($this->query($query) as $switch) 59 { 60 $closeTag = $switch->nextSibling; 61 foreach ($this->query('case', $switch) as $case) 62 { 63 if (!$case->lastChild || $case->lastChild->nodeName !== 'closeTag') 64 { 65 $case->appendChild($closeTag->cloneNode()); 66 } 67 } 68 } 69 } 70 71 /** 72 * Clone closeTag elements from the head of a switch's cases before said switch 73 * 74 * If there's a <closeTag/> at the beginning of every <case/>, clone it and insert it 75 * right before the <switch/> unless there's already one 76 * 77 * @return void 78 */ 79 protected function cloneCloseTagElementsOutOfSwitch() 80 { 81 $query = '//switch[case/closeTag][not(case[name(*[1]) != "closeTag"])]'; 82 foreach ($this->query($query) as $switch) 83 { 84 $case = $this->query('case/closeTag', $switch)->item(0); 85 $switch->parentNode->insertBefore($case->cloneNode(), $switch); 86 } 87 } 88 89 /** 90 * Merge consecutive literal outputs 91 * 92 * @return void 93 */ 94 protected function mergeConsecutiveLiteralOutputElements() 95 { 96 foreach ($this->query('//output[@type="literal"]') as $output) 97 { 98 $disableOutputEscaping = $output->getAttribute('disable-output-escaping'); 99 while ($this->nextSiblingIsLiteralOutput($output, $disableOutputEscaping)) 100 { 101 $output->nodeValue = htmlspecialchars($output->nodeValue . $output->nextSibling->nodeValue, ENT_COMPAT); 102 $output->parentNode->removeChild($output->nextSibling); 103 } 104 } 105 } 106 107 /** 108 * Test whether the next sibling of an element is a literal output element with matching escaping 109 * 110 * @param DOMElement $node 111 * @param string $disableOutputEscaping 112 * @return bool 113 */ 114 protected function nextSiblingIsLiteralOutput(DOMElement $node, $disableOutputEscaping) 115 { 116 return isset($node->nextSibling) && $node->nextSibling->nodeName === 'output' && $node->nextSibling->getAttribute('type') === 'literal' && $node->nextSibling->getAttribute('disable-output-escaping') === $disableOutputEscaping; 117 } 118 119 /** 120 * Optimize closeTags elements 121 * 122 * @return void 123 */ 124 protected function optimizeCloseTagElements() 125 { 126 $this->cloneCloseTagElementsIntoSwitch(); 127 $this->cloneCloseTagElementsOutOfSwitch(); 128 $this->removeRedundantCloseTagElementsInSwitch(); 129 $this->removeRedundantCloseTagElements(); 130 } 131 132 /** 133 * Remove redundant closeTag siblings after a switch 134 * 135 * If all branches of a switch have a closeTag we can remove any closeTag siblings of the switch 136 * 137 * @return void 138 */ 139 protected function removeCloseTagSiblings() 140 { 141 $query = '//switch[not(case[not(closeTag)])]/following-sibling::closeTag'; 142 $this->removeNodes($query); 143 } 144 145 /** 146 * Remove content from void elements 147 * 148 * For each void element, we find whichever <closeTag/> elements close it and remove everything 149 * after 150 * 151 * @return void 152 */ 153 protected function removeContentFromVoidElements() 154 { 155 foreach ($this->query('//element[@void="yes"]') as $element) 156 { 157 $id = $element->getAttribute('id'); 158 $query = './/closeTag[@id="' . $id . '"]/following-sibling::*'; 159 160 $this->removeNodes($query, $element); 161 } 162 } 163 164 /** 165 * Remove empty default cases (no test and no descendants) 166 * 167 * @return void 168 */ 169 protected function removeEmptyDefaultCases() 170 { 171 $query = '//case[not(@test)][not(*)][. = ""]'; 172 $this->removeNodes($query); 173 } 174 175 /** 176 * Remove all nodes that match given XPath query 177 * 178 * @param string $query 179 * @param DOMNode $contextNode 180 * @return void 181 */ 182 protected function removeNodes($query, DOMNode $contextNode = null) 183 { 184 foreach ($this->query($query, $contextNode) as $node) 185 { 186 if ($node->parentNode instanceof DOMElement) 187 { 188 $node->parentNode->removeChild($node); 189 } 190 } 191 } 192 193 /** 194 * Remove redundant closeTag elements from the tail of a switch's cases 195 * 196 * For each <closeTag/> remove duplicate <closeTag/> nodes that are either siblings or 197 * descendants of a sibling 198 * 199 * @return void 200 */ 201 protected function removeRedundantCloseTagElements() 202 { 203 foreach ($this->query('//closeTag') as $closeTag) 204 { 205 $id = $closeTag->getAttribute('id'); 206 $query = 'following-sibling::*/descendant-or-self::closeTag[@id="' . $id . '"]'; 207 208 $this->removeNodes($query, $closeTag); 209 } 210 } 211 212 /** 213 * Remove redundant closeTag elements from the tail of a switch's cases 214 * 215 * If there's a <closeTag/> right after a <switch/>, remove all <closeTag/> nodes at the 216 * end of every <case/> 217 * 218 * @return void 219 */ 220 protected function removeRedundantCloseTagElementsInSwitch() 221 { 222 $query = '//switch[name(following-sibling::*[1]) = "closeTag"]'; 223 foreach ($this->query($query) as $switch) 224 { 225 foreach ($this->query('case', $switch) as $case) 226 { 227 while ($case->lastChild && $case->lastChild->nodeName === 'closeTag') 228 { 229 $case->removeChild($case->lastChild); 230 } 231 } 232 } 233 } 234 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |