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

Normalizer.php

Zuletzt modifiziert: 01.05.2026, 11:28 - Dateigröße: 6.95 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  use s9e\TextFormatter\Configurator\Helpers\XPathHelper;
 014   
 015  class Normalizer extends IRProcessor
 016  {
 017      /**
 018      * @var Optimizer
 019      */
 020      protected $optimizer;
 021   
 022      /**
 023      * @var string Regexp that matches the names of all void elements
 024      * @link http://www.w3.org/TR/html-markup/syntax.html#void-elements
 025      */
 026      public $voidRegexp = '/^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/Di';
 027   
 028      /**
 029      * @param  Optimizer $optimizer
 030      * @return void
 031      */
 032      public function __construct(Optimizer $optimizer)
 033      {
 034          $this->optimizer = $optimizer;
 035      }
 036   
 037      /**
 038      * Normalize an IR
 039      *
 040      * @param  DOMDocument $ir
 041      * @return void
 042      */
 043      public function normalize(DOMDocument $ir)
 044      {
 045          $this->createXPath($ir);
 046          $this->addDefaultCase();
 047          $this->addElementIds();
 048          $this->addCloseTagElements($ir);
 049          $this->markVoidElements();
 050          $this->optimizer->optimize($ir);
 051          $this->markConditionalCloseTagElements();
 052          $this->setOutputContext();
 053          $this->markBranchTables();
 054          $this->markBooleanAttributes();
 055      }
 056   
 057      /**
 058      * Add <closeTag/> elements everywhere an open start tag should be closed
 059      *
 060      * @param  DOMDocument $ir
 061      * @return void
 062      */
 063      protected function addCloseTagElements(DOMDocument $ir)
 064      {
 065          $exprs = [
 066              '//applyTemplates[not(ancestor::attribute)]',
 067              '//comment',
 068              '//element',
 069              '//output[not(ancestor::attribute)]'
 070          ];
 071          foreach ($this->query(implode('|', $exprs)) as $node)
 072          {
 073              $parentElementId = $this->getParentElementId($node);
 074              if (isset($parentElementId))
 075              {
 076                  $node->parentNode
 077                       ->insertBefore($ir->createElement('closeTag'), $node)
 078                       ->setAttribute('id', $parentElementId);
 079              }
 080   
 081              // Append a <closeTag/> to <element/> nodes to ensure that empty elements get closed
 082              if ($node->nodeName === 'element')
 083              {
 084                  $id = $node->getAttribute('id');
 085                  $this->appendElement($node, 'closeTag')->setAttribute('id', $id);
 086              }
 087          }
 088      }
 089   
 090      /**
 091      * Add an empty default <case/> to <switch/> nodes that don't have one
 092      *
 093      * @return void
 094      */
 095      protected function addDefaultCase()
 096      {
 097          foreach ($this->query('//switch[not(case[not(@test)])]') as $switch)
 098          {
 099              $this->appendElement($switch, 'case');
 100          }
 101      }
 102   
 103      /**
 104      * Add an id attribute to <element/> nodes
 105      *
 106      * @return void
 107      */
 108      protected function addElementIds()
 109      {
 110          $id = 0;
 111          foreach ($this->query('//element') as $element)
 112          {
 113              $element->setAttribute('id', ++$id);
 114          }
 115      }
 116   
 117      /**
 118      * Get the context type for given output element
 119      *
 120      * @param  DOMNode $output
 121      * @return string
 122      */
 123      protected function getOutputContext(DOMNode $output)
 124      {
 125          $contexts = [
 126              'boolean(ancestor::attribute)'             => 'attribute',
 127              '@disable-output-escaping="yes"'           => 'raw',
 128              'count(ancestor::element[@name="script"])' => 'raw'
 129          ];
 130          foreach ($contexts as $expr => $context)
 131          {
 132              if ($this->evaluate($expr, $output))
 133              {
 134                  return $context;
 135              }
 136          }
 137   
 138          return 'text';
 139      }
 140   
 141      /**
 142      * Get the ID of the closest "element" ancestor
 143      *
 144      * @param  DOMNode     $node Context node
 145      * @return string|null
 146      */
 147      protected function getParentElementId(DOMNode $node)
 148      {
 149          $parentNode = $node->parentNode;
 150          while (isset($parentNode))
 151          {
 152              if ($parentNode->nodeName === 'element')
 153              {
 154                  return $parentNode->getAttribute('id');
 155              }
 156              $parentNode = $parentNode->parentNode;
 157          }
 158      }
 159   
 160      /**
 161      * Mark switch elements that are used as branch tables
 162      *
 163      * If a switch is used for a series of equality tests against the same attribute or variable, the
 164      * attribute/variable is stored within the switch as "branch-key" and the values it is compared
 165      * against are stored JSON-encoded in the case as "branch-values". It can be used to create
 166      * optimized branch tables
 167      *
 168      * @return void
 169      */
 170      protected function markBranchTables()
 171      {
 172          // Iterate over switch elements that have at least two case children with a test attribute
 173          foreach ($this->query('//switch[case[2][@test]]') as $switch)
 174          {
 175              $this->markSwitchTable($switch);
 176          }
 177      }
 178   
 179      /**
 180      * Mark given switch element if it's used as a branch table
 181      *
 182      * @param  DOMElement $switch
 183      * @return void
 184      */
 185      protected function markSwitchTable(DOMElement $switch)
 186      {
 187          $cases = [];
 188          $maps  = [];
 189          foreach ($this->query('./case[@test]', $switch) as $i => $case)
 190          {
 191              $map = XPathHelper::parseEqualityExpr($case->getAttribute('test'));
 192              if ($map === false)
 193              {
 194                  return;
 195              }
 196              $maps     += $map;
 197              $cases[$i] = [$case, end($map)];
 198          }
 199          if (count($maps) !== 1)
 200          {
 201              return;
 202          }
 203   
 204          $switch->setAttribute('branch-key', key($maps));
 205          foreach ($cases as list($case, $values))
 206          {
 207              sort($values);
 208              $case->setAttribute('branch-values', serialize($values));
 209          }
 210      }
 211   
 212      /**
 213      * Mark conditional <closeTag/> nodes
 214      *
 215      * @return void
 216      */
 217      protected function markConditionalCloseTagElements()
 218      {
 219          foreach ($this->query('//closeTag') as $closeTag)
 220          {
 221              $id = $closeTag->getAttribute('id');
 222   
 223              // For each <switch/> ancestor, look for a <closeTag/> and that is either a sibling or
 224              // the descendant of a sibling, and that matches the id
 225              $query = 'ancestor::switch/'
 226                     . 'following-sibling::*/'
 227                     . 'descendant-or-self::closeTag[@id = "' . $id . '"]';
 228              foreach ($this->query($query, $closeTag) as $following)
 229              {
 230                  // Mark following <closeTag/> nodes to indicate that the status of this tag must
 231                  // be checked before it is closed
 232                  $following->setAttribute('check', '');
 233   
 234                  // Mark the current <closeTag/> to indicate that it must set a flag to indicate
 235                  // that its tag has been closed
 236                  $closeTag->setAttribute('set', '');
 237              }
 238          }
 239      }
 240   
 241      /**
 242      * Mark boolean attributes
 243      *
 244      * The test is case-sensitive and only covers attribute that are minimized by libxslt
 245      *
 246      * @return void
 247      */
 248      protected function markBooleanAttributes(): void
 249      {
 250          $attrNames = ['checked', 'compact', 'declare', 'defer', 'disabled', 'ismap', 'multiple', 'nohref', 'noresize', 'noshade', 'nowrap', 'readonly', 'selected'];
 251          foreach ($this->query('//attribute') as $attribute)
 252          {
 253              if (in_array($attribute->getAttribute('name'), $attrNames, true))
 254              {
 255                  $attribute->setAttribute('boolean', 'yes');
 256              }
 257          }
 258      }
 259   
 260      /**
 261      * Mark void elements
 262      *
 263      * @return void
 264      */
 265      protected function markVoidElements()
 266      {
 267          foreach ($this->query('//element') as $element)
 268          {
 269              // Test whether this element is (maybe) void
 270              $elName = $element->getAttribute('name');
 271              if (strpos($elName, '{') !== false)
 272              {
 273                  // Dynamic element names must be checked at runtime
 274                  $element->setAttribute('void', 'maybe');
 275              }
 276              elseif (preg_match($this->voidRegexp, $elName))
 277              {
 278                  // Static element names can be checked right now
 279                  $element->setAttribute('void', 'yes');
 280              }
 281          }
 282      }
 283   
 284      /**
 285      * Fill in output context
 286      *
 287      * @return void
 288      */
 289      protected function setOutputContext()
 290      {
 291          foreach ($this->query('//output') as $output)
 292          {
 293              $output->setAttribute('escape', $this->getOutputContext($output));
 294          }
 295      }
 296  }