Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Optimizer.php

Zuletzt modifiziert: 01.05.2026, 11:28 - Dateigröße: 5.97 KiB


 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  }