[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/Helpers/TemplateParser/ -> Normalizer.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  use s9e\TextFormatter\Configurator\Helpers\XPathHelper;
  14  
  15  class Normalizer extends IRProcessor
  16  {
  17      /**
  18      * @var Optimizer
  19      */
  20      protected $optimizer;
  21  
  22      /**
  23      * @var string Regexp that matches the names of all void elements
  24      * @link http://www.w3.org/TR/html-markup/syntax.html#void-elements
  25      */
  26      public $voidRegexp = '/^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/Di';
  27  
  28      /**
  29      * @param  Optimizer $optimizer
  30      * @return void
  31      */
  32  	public function __construct(Optimizer $optimizer)
  33      {
  34          $this->optimizer = $optimizer;
  35      }
  36  
  37      /**
  38      * Normalize an IR
  39      *
  40      * @param  DOMDocument $ir
  41      * @return void
  42      */
  43  	public function normalize(DOMDocument $ir)
  44      {
  45          $this->createXPath($ir);
  46          $this->addDefaultCase($ir);
  47          $this->addElementIds($ir);
  48          $this->addCloseTagElements($ir);
  49          $this->markVoidElements($ir);
  50          $this->optimizer->optimize($ir);
  51          $this->markConditionalCloseTagElements($ir);
  52          $this->setOutputContext($ir);
  53          $this->markBranchTables($ir);
  54      }
  55  
  56      /**
  57      * Add <closeTag/> elements everywhere an open start tag should be closed
  58      *
  59      * @param  DOMDocument $ir
  60      * @return void
  61      */
  62  	protected function addCloseTagElements(DOMDocument $ir)
  63      {
  64          $exprs = [
  65              '//applyTemplates[not(ancestor::attribute)]',
  66              '//comment',
  67              '//element',
  68              '//output[not(ancestor::attribute)]'
  69          ];
  70          foreach ($this->query(implode('|', $exprs)) as $node)
  71          {
  72              $parentElementId = $this->getParentElementId($node);
  73              if (isset($parentElementId))
  74              {
  75                  $node->parentNode
  76                       ->insertBefore($ir->createElement('closeTag'), $node)
  77                       ->setAttribute('id', $parentElementId);
  78              }
  79  
  80              // Append a <closeTag/> to <element/> nodes to ensure that empty elements get closed
  81              if ($node->nodeName === 'element')
  82              {
  83                  $id = $node->getAttribute('id');
  84                  $this->appendElement($node, 'closeTag')->setAttribute('id', $id);
  85              }
  86          }
  87      }
  88  
  89      /**
  90      * Add an empty default <case/> to <switch/> nodes that don't have one
  91      *
  92      * @param  DOMDocument $ir
  93      * @return void
  94      */
  95  	protected function addDefaultCase(DOMDocument $ir)
  96      {
  97          foreach ($this->query('//switch[not(case[not(@test)])]') as $switch)
  98          {
  99              $this->appendElement($switch, 'case');
 100          }
 101      }
 102  
 103      /**
 104      * Add an id attribute to <element/> nodes
 105      *
 106      * @param  DOMDocument $ir
 107      * @return void
 108      */
 109  	protected function addElementIds(DOMDocument $ir)
 110      {
 111          $id = 0;
 112          foreach ($this->query('//element') as $element)
 113          {
 114              $element->setAttribute('id', ++$id);
 115          }
 116      }
 117  
 118      /**
 119      * Get the context type for given output element
 120      *
 121      * @param  DOMNode $output
 122      * @return string
 123      */
 124  	protected function getOutputContext(DOMNode $output)
 125      {
 126          $contexts = [
 127              'boolean(ancestor::attribute)'             => 'attribute',
 128              '@disable-output-escaping="yes"'           => 'raw',
 129              'count(ancestor::element[@name="script"])' => 'raw'
 130          ];
 131          foreach ($contexts as $expr => $context)
 132          {
 133              if ($this->evaluate($expr, $output))
 134              {
 135                  return $context;
 136              }
 137          }
 138  
 139          return 'text';
 140      }
 141  
 142      /**
 143      * Get the ID of the closest "element" ancestor
 144      *
 145      * @param  DOMNode     $node Context node
 146      * @return string|null
 147      */
 148  	protected function getParentElementId(DOMNode $node)
 149      {
 150          $parentNode = $node->parentNode;
 151          while (isset($parentNode))
 152          {
 153              if ($parentNode->nodeName === 'element')
 154              {
 155                  return $parentNode->getAttribute('id');
 156              }
 157              $parentNode = $parentNode->parentNode;
 158          }
 159      }
 160  
 161      /**
 162      * Mark switch elements that are used as branch tables
 163      *
 164      * If a switch is used for a series of equality tests against the same attribute or variable, the
 165      * attribute/variable is stored within the switch as "branch-key" and the values it is compared
 166      * against are stored JSON-encoded in the case as "branch-values". It can be used to create
 167      * optimized branch tables
 168      *
 169      * @param  DOMDocument $ir
 170      * @return void
 171      */
 172  	protected function markBranchTables(DOMDocument $ir)
 173      {
 174          // Iterate over switch elements that have at least two case children with a test attribute
 175          foreach ($this->query('//switch[case[2][@test]]') as $switch)
 176          {
 177              $this->markSwitchTable($switch);
 178          }
 179      }
 180  
 181      /**
 182      * Mark given switch element if it's used as a branch table
 183      *
 184      * @param  DOMElement $switch
 185      * @return void
 186      */
 187  	protected function markSwitchTable(DOMElement $switch)
 188      {
 189          $cases = [];
 190          $maps  = [];
 191          foreach ($this->query('./case[@test]', $switch) as $i => $case)
 192          {
 193              $map = XPathHelper::parseEqualityExpr($case->getAttribute('test'));
 194              if ($map === false)
 195              {
 196                  return;
 197              }
 198              $maps     += $map;
 199              $cases[$i] = [$case, end($map)];
 200          }
 201          if (count($maps) !== 1)
 202          {
 203              return;
 204          }
 205  
 206          $switch->setAttribute('branch-key', key($maps));
 207          foreach ($cases as list($case, $values))
 208          {
 209              sort($values);
 210              $case->setAttribute('branch-values', serialize($values));
 211          }
 212      }
 213  
 214      /**
 215      * Mark conditional <closeTag/> nodes
 216      *
 217      * @param  DOMDocument $ir
 218      * @return void
 219      */
 220  	protected function markConditionalCloseTagElements(DOMDocument $ir)
 221      {
 222          foreach ($this->query('//closeTag') as $closeTag)
 223          {
 224              $id = $closeTag->getAttribute('id');
 225  
 226              // For each <switch/> ancestor, look for a <closeTag/> and that is either a sibling or
 227              // the descendant of a sibling, and that matches the id
 228              $query = 'ancestor::switch/'
 229                     . 'following-sibling::*/'
 230                     . 'descendant-or-self::closeTag[@id = "' . $id . '"]';
 231              foreach ($this->query($query, $closeTag) as $following)
 232              {
 233                  // Mark following <closeTag/> nodes to indicate that the status of this tag must
 234                  // be checked before it is closed
 235                  $following->setAttribute('check', '');
 236  
 237                  // Mark the current <closeTag/> to indicate that it must set a flag to indicate
 238                  // that its tag has been closed
 239                  $closeTag->setAttribute('set', '');
 240              }
 241          }
 242      }
 243  
 244      /**
 245      * Mark void elements
 246      *
 247      * @param  DOMDocument $ir
 248      * @return void
 249      */
 250  	protected function markVoidElements(DOMDocument $ir)
 251      {
 252          foreach ($this->query('//element') as $element)
 253          {
 254              // Test whether this element is (maybe) void
 255              $elName = $element->getAttribute('name');
 256              if (strpos($elName, '{') !== false)
 257              {
 258                  // Dynamic element names must be checked at runtime
 259                  $element->setAttribute('void', 'maybe');
 260              }
 261              elseif (preg_match($this->voidRegexp, $elName))
 262              {
 263                  // Static element names can be checked right now
 264                  $element->setAttribute('void', 'yes');
 265              }
 266          }
 267      }
 268  
 269      /**
 270      * Fill in output context
 271      *
 272      * @param  DOMDocument $ir
 273      * @return void
 274      */
 275  	protected function setOutputContext(DOMDocument $ir)
 276      {
 277          foreach ($this->query('//output') as $output)
 278          {
 279              $output->setAttribute('escape', $this->getOutputContext($output));
 280          }
 281      }
 282  }


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