Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:04 - 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  }