Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Optimizer.php
001 <?php
002
003 /**
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2022 The s9e authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Configurator\Helpers\TemplateParser;
009
010 use DOMDocument;
011 use DOMElement;
012 use DOMNode;
013
014 class Optimizer extends IRProcessor
015 {
016 /**
017 * Optimize an IR
018 *
019 * @param DOMDocument $ir
020 * @return void
021 */
022 public function optimize(DOMDocument $ir)
023 {
024 $this->createXPath($ir);
025
026 // Get a snapshot of current internal representation
027 $xml = $ir->saveXML();
028
029 // Set a maximum number of loops to ward against infinite loops
030 $remainingLoops = 10;
031
032 // From now on, keep looping until no further modifications are applied
033 do
034 {
035 $old = $xml;
036 $this->optimizeCloseTagElements();
037 $xml = $ir->saveXML();
038 }
039 while (--$remainingLoops > 0 && $xml !== $old);
040
041 $this->removeCloseTagSiblings();
042 $this->removeContentFromVoidElements();
043 $this->mergeConsecutiveLiteralOutputElements();
044 $this->removeEmptyDefaultCases();
045 }
046
047 /**
048 * Clone closeTag elements that follow a switch into said switch
049 *
050 * If there's a <closeTag/> right after a <switch/>, clone the <closeTag/> at the end of
051 * the every <case/> that does not end with a <closeTag/>
052 *
053 * @return void
054 */
055 protected function cloneCloseTagElementsIntoSwitch()
056 {
057 $query = '//switch[name(following-sibling::*[1]) = "closeTag"]';
058 foreach ($this->query($query) as $switch)
059 {
060 $closeTag = $switch->nextSibling;
061 foreach ($this->query('case', $switch) as $case)
062 {
063 if (!$case->lastChild || $case->lastChild->nodeName !== 'closeTag')
064 {
065 $case->appendChild($closeTag->cloneNode());
066 }
067 }
068 }
069 }
070
071 /**
072 * Clone closeTag elements from the head of a switch's cases before said switch
073 *
074 * If there's a <closeTag/> at the beginning of every <case/>, clone it and insert it
075 * right before the <switch/> unless there's already one
076 *
077 * @return void
078 */
079 protected function cloneCloseTagElementsOutOfSwitch()
080 {
081 $query = '//switch[case/closeTag][not(case[name(*[1]) != "closeTag"])]';
082 foreach ($this->query($query) as $switch)
083 {
084 $case = $this->query('case/closeTag', $switch)->item(0);
085 $switch->parentNode->insertBefore($case->cloneNode(), $switch);
086 }
087 }
088
089 /**
090 * Merge consecutive literal outputs
091 *
092 * @return void
093 */
094 protected function mergeConsecutiveLiteralOutputElements()
095 {
096 foreach ($this->query('//output[@type="literal"]') as $output)
097 {
098 $disableOutputEscaping = $output->getAttribute('disable-output-escaping');
099 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 }