[ Index ]

PHP Cross Reference of phpBB-3.3.3-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-2020 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($ir);
  37              $xml = $ir->saveXML();
  38          }
  39          while (--$remainingLoops > 0 && $xml !== $old);
  40  
  41          $this->removeCloseTagSiblings($ir);
  42          $this->removeContentFromVoidElements($ir);
  43          $this->mergeConsecutiveLiteralOutputElements($ir);
  44          $this->removeEmptyDefaultCases($ir);
  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      * @param  DOMDocument $ir
  54      * @return void
  55      */
  56  	protected function cloneCloseTagElementsIntoSwitch(DOMDocument $ir)
  57      {
  58          $query = '//switch[name(following-sibling::*[1]) = "closeTag"]';
  59          foreach ($this->query($query) as $switch)
  60          {
  61              $closeTag = $switch->nextSibling;
  62              foreach ($this->query('case', $switch) as $case)
  63              {
  64                  if (!$case->lastChild || $case->lastChild->nodeName !== 'closeTag')
  65                  {
  66                      $case->appendChild($closeTag->cloneNode());
  67                  }
  68              }
  69          }
  70      }
  71  
  72      /**
  73      * Clone closeTag elements from the head of a switch's cases before said switch
  74      *
  75      * If there's a <closeTag/> at the beginning of every <case/>, clone it and insert it
  76      * right before the <switch/> unless there's already one
  77      *
  78      * @param  DOMDocument $ir
  79      * @return void
  80      */
  81  	protected function cloneCloseTagElementsOutOfSwitch(DOMDocument $ir)
  82      {
  83          $query = '//switch[case/closeTag][not(case[name(*[1]) != "closeTag"])]';
  84          foreach ($this->query($query) as $switch)
  85          {
  86              $case = $this->query('case/closeTag', $switch)->item(0);
  87              $switch->parentNode->insertBefore($case->cloneNode(), $switch);
  88          }
  89      }
  90  
  91      /**
  92      * Merge consecutive literal outputs
  93      *
  94      * @param  DOMDocument $ir
  95      * @return void
  96      */
  97  	protected function mergeConsecutiveLiteralOutputElements(DOMDocument $ir)
  98      {
  99          foreach ($this->query('//output[@type="literal"]') as $output)
 100          {
 101              $disableOutputEscaping = $output->getAttribute('disable-output-escaping');
 102              while ($this->nextSiblingIsLiteralOutput($output, $disableOutputEscaping))
 103              {
 104                  $output->nodeValue = htmlspecialchars($output->nodeValue . $output->nextSibling->nodeValue);
 105                  $output->parentNode->removeChild($output->nextSibling);
 106              }
 107          }
 108      }
 109  
 110      /**
 111      * Test whether the next sibling of an element is a literal output element with matching escaping
 112      *
 113      * @param  DOMElement $node
 114      * @param  string     $disableOutputEscaping
 115      * @return bool
 116      */
 117  	protected function nextSiblingIsLiteralOutput(DOMElement $node, $disableOutputEscaping)
 118      {
 119          return isset($node->nextSibling) && $node->nextSibling->nodeName === 'output' && $node->nextSibling->getAttribute('type') === 'literal' && $node->nextSibling->getAttribute('disable-output-escaping') === $disableOutputEscaping;
 120      }
 121  
 122      /**
 123      * Optimize closeTags elements
 124      *
 125      * @param  DOMDocument $ir
 126      * @return void
 127      */
 128  	protected function optimizeCloseTagElements(DOMDocument $ir)
 129      {
 130          $this->cloneCloseTagElementsIntoSwitch($ir);
 131          $this->cloneCloseTagElementsOutOfSwitch($ir);
 132          $this->removeRedundantCloseTagElementsInSwitch($ir);
 133          $this->removeRedundantCloseTagElements($ir);
 134      }
 135  
 136      /**
 137      * Remove redundant closeTag siblings after a switch
 138      *
 139      * If all branches of a switch have a closeTag we can remove any closeTag siblings of the switch
 140      *
 141      * @param  DOMDocument $ir
 142      * @return void
 143      */
 144  	protected function removeCloseTagSiblings(DOMDocument $ir)
 145      {
 146          $query = '//switch[not(case[not(closeTag)])]/following-sibling::closeTag';
 147          $this->removeNodes($ir, $query);
 148      }
 149  
 150      /**
 151      * Remove content from void elements
 152      *
 153      * For each void element, we find whichever <closeTag/> elements close it and remove everything
 154      * after
 155      *
 156      * @param  DOMDocument $ir
 157      * @return void
 158      */
 159  	protected function removeContentFromVoidElements(DOMDocument $ir)
 160      {
 161          foreach ($this->query('//element[@void="yes"]') as $element)
 162          {
 163              $id    = $element->getAttribute('id');
 164              $query = './/closeTag[@id="' . $id . '"]/following-sibling::*';
 165  
 166              $this->removeNodes($ir, $query, $element);
 167          }
 168      }
 169  
 170      /**
 171      * Remove empty default cases (no test and no descendants)
 172      *
 173      * @param  DOMDocument $ir
 174      * @return void
 175      */
 176  	protected function removeEmptyDefaultCases(DOMDocument $ir)
 177      {
 178          $query = '//case[not(@test)][not(*)][. = ""]';
 179          $this->removeNodes($ir, $query);
 180      }
 181  
 182      /**
 183      * Remove all nodes that match given XPath query
 184      *
 185      * @param  DOMDocument $ir
 186      * @param  string      $query
 187      * @param  DOMNode     $contextNode
 188      * @return void
 189      */
 190  	protected function removeNodes(DOMDocument $ir, $query, DOMNode $contextNode = null)
 191      {
 192          foreach ($this->query($query, $contextNode) as $node)
 193          {
 194              if ($node->parentNode instanceof DOMElement)
 195              {
 196                  $node->parentNode->removeChild($node);
 197              }
 198          }
 199      }
 200  
 201      /**
 202      * Remove redundant closeTag elements from the tail of a switch's cases
 203      *
 204      * For each <closeTag/> remove duplicate <closeTag/> nodes that are either siblings or
 205      * descendants of a sibling
 206      *
 207      * @param  DOMDocument $ir
 208      * @return void
 209      */
 210  	protected function removeRedundantCloseTagElements(DOMDocument $ir)
 211      {
 212          foreach ($this->query('//closeTag') as $closeTag)
 213          {
 214              $id    = $closeTag->getAttribute('id');
 215              $query = 'following-sibling::*/descendant-or-self::closeTag[@id="' . $id . '"]';
 216  
 217              $this->removeNodes($ir, $query, $closeTag);
 218          }
 219      }
 220  
 221      /**
 222      * Remove redundant closeTag elements from the tail of a switch's cases
 223      *
 224      * If there's a <closeTag/> right after a <switch/>, remove all <closeTag/> nodes at the
 225      * end of every <case/>
 226      *
 227      * @param  DOMDocument $ir
 228      * @return void
 229      */
 230  	protected function removeRedundantCloseTagElementsInSwitch(DOMDocument $ir)
 231      {
 232          $query = '//switch[name(following-sibling::*[1]) = "closeTag"]';
 233          foreach ($this->query($query) as $switch)
 234          {
 235              foreach ($this->query('case', $switch) as $case)
 236              {
 237                  while ($case->lastChild && $case->lastChild->nodeName === 'closeTag')
 238                  {
 239                      $case->removeChild($case->lastChild);
 240                  }
 241              }
 242          }
 243      }
 244  }


Generated: Sun Feb 14 20:08:31 2021 Cross-referenced by PHPXref 0.7.1