[ Index ]

PHP Cross Reference of phpBB-3.3.7-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/Helpers/TemplateParser/ -> Optimizer.php (source)

   1  <?php
   2  
   3  /**
   4  * @package   s9e\TextFormatter
   5  * @copyright Copyright (c) 2010-2021 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  }


Generated: Thu Mar 24 21:31:15 2022 Cross-referenced by PHPXref 0.7.1