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

Parser.php

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 10.90 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 DOMXPath;
013  use RuntimeException;
014  use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
015  use s9e\TextFormatter\Configurator\Helpers\TemplateLoader;
016   
017  class Parser extends IRProcessor
018  {
019      /**
020      * @var Normalizer
021      */
022      protected $normalizer;
023   
024      /**
025      * @param  Normalizer $normalizer
026      * @return void
027      */
028      public function __construct(Normalizer $normalizer)
029      {
030          $this->normalizer = $normalizer;
031      }
032   
033      /**
034      * Parse a template into an internal representation
035      *
036      * @param  string      $template Source template
037      * @return DOMDocument           Internal representation
038      */
039      public function parse($template)
040      {
041          $dom = TemplateLoader::load($template);
042   
043          $ir = new DOMDocument;
044          $ir->loadXML('<template/>');
045   
046          $this->createXPath($dom);
047          $this->parseChildren($ir->documentElement, $dom->documentElement);
048          $this->normalizer->normalize($ir);
049   
050          return $ir;
051      }
052   
053      /**
054      * Append <output/> elements corresponding to given AVT
055      *
056      * @param  DOMElement $parentNode Parent node
057      * @param  string     $avt        Attribute value template
058      * @return void
059      */
060      protected function appendAVT(DOMElement $parentNode, $avt)
061      {
062          foreach (AVTHelper::parse($avt) as $token)
063          {
064              if ($token[0] === 'expression')
065              {
066                  $this->appendXPathOutput($parentNode, $token[1]);
067              }
068              else
069              {
070                  $this->appendLiteralOutput($parentNode, $token[1]);
071              }
072          }
073      }
074   
075      /**
076      * Append an <output/> element with literal content to given node
077      *
078      * @param  DOMElement $parentNode Parent node
079      * @param  string     $content    Content to output
080      * @return void
081      */
082      protected function appendLiteralOutput(DOMElement $parentNode, $content)
083      {
084          if ($content === '')
085          {
086              return;
087          }
088   
089          $this->appendElement($parentNode, 'output', htmlspecialchars($content, ENT_COMPAT))
090               ->setAttribute('type', 'literal');
091      }
092   
093      /**
094      * Append the structure for a <xsl:copy-of/> element to given node
095      *
096      * @param  DOMElement $parentNode Parent node
097      * @param  string     $expr       Select expression, which is should only contain attributes
098      * @return void
099      */
100      protected function appendConditionalAttributes(DOMElement $parentNode, $expr)
101      {
102          preg_match_all('(@([-\\w]+))', $expr, $matches);
103          foreach ($matches[1] as $attrName)
104          {
105              // Create a switch element in the IR
106              $switch = $this->appendElement($parentNode, 'switch');
107              $case   = $this->appendElement($switch, 'case');
108              $case->setAttribute('test', '@' . $attrName);
109   
110              // Append an attribute element
111              $attribute = $this->appendElement($case, 'attribute');
112              $attribute->setAttribute('name', $attrName);
113   
114              // Set the attribute's content, which is simply the copied attribute's value
115              $this->appendXPathOutput($attribute, '@' . $attrName);
116          }
117      }
118   
119      /**
120      * Append an <output/> element for given XPath expression to given node
121      *
122      * @param  DOMElement $parentNode Parent node
123      * @param  string     $expr       XPath expression
124      * @return void
125      */
126      protected function appendXPathOutput(DOMElement $parentNode, $expr)
127      {
128          $this->appendElement($parentNode, 'output', htmlspecialchars(trim($expr), ENT_COMPAT))
129               ->setAttribute('type', 'xpath');
130      }
131   
132      /**
133      * Parse all the children of a given element
134      *
135      * @param  DOMElement $ir     Node in the internal representation that represents the parent node
136      * @param  DOMElement $parent Parent node
137      * @return void
138      */
139      protected function parseChildren(DOMElement $ir, DOMElement $parent)
140      {
141          foreach ($parent->childNodes as $child)
142          {
143              switch ($child->nodeType)
144              {
145                  case XML_COMMENT_NODE:
146                      // Do nothing
147                      break;
148   
149                  case XML_TEXT_NODE:
150                      if (trim($child->textContent) !== '')
151                      {
152                          $this->appendLiteralOutput($ir, $child->textContent);
153                      }
154                      break;
155   
156                  case XML_ELEMENT_NODE:
157                      $this->parseNode($ir, $child);
158                      break;
159   
160                  default:
161                      throw new RuntimeException("Cannot parse node '" . $child->nodeName . "''");
162              }
163          }
164      }
165   
166      /**
167      * Parse a given node into the internal representation
168      *
169      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
170      * @param  DOMElement $node Node to parse
171      * @return void
172      */
173      protected function parseNode(DOMElement $ir, DOMElement $node)
174      {
175          // XSL elements are parsed by the corresponding parseXsl* method
176          if ($node->namespaceURI === self::XMLNS_XSL)
177          {
178              $methodName = 'parseXsl' . str_replace(' ', '', ucwords(str_replace('-', ' ', $node->localName)));
179              if (!method_exists($this, $methodName))
180              {
181                  throw new RuntimeException("Element '" . $node->nodeName . "' is not supported");
182              }
183   
184              return $this->$methodName($ir, $node);
185          }
186   
187          // Create an <element/> with a name attribute equal to given node's name
188          $element = $this->appendElement($ir, 'element');
189          $element->setAttribute('name', $node->nodeName);
190   
191          // Append an <attribute/> element for each namespace declaration
192          $xpath = new DOMXPath($node->ownerDocument);
193          foreach ($xpath->query('namespace::*', $node) as $ns)
194          {
195              if ($node->hasAttribute($ns->nodeName))
196              {
197                  $irAttribute = $this->appendElement($element, 'attribute');
198                  $irAttribute->setAttribute('name', $ns->nodeName);
199                  $this->appendLiteralOutput($irAttribute, $ns->nodeValue);
200              }
201          }
202   
203          // Append an <attribute/> element for each of this node's attribute
204          foreach ($node->attributes as $attribute)
205          {
206              $irAttribute = $this->appendElement($element, 'attribute');
207              $irAttribute->setAttribute('name', $attribute->nodeName);
208   
209              // Append an <output/> element to represent the attribute's value
210              $this->appendAVT($irAttribute, $attribute->value);
211          }
212   
213          // Parse the content of this node
214          $this->parseChildren($element, $node);
215      }
216   
217      /**
218      * Parse an <xsl:apply-templates/> node into the internal representation
219      *
220      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
221      * @param  DOMElement $node <xsl:apply-templates/> node
222      * @return void
223      */
224      protected function parseXslApplyTemplates(DOMElement $ir, DOMElement $node)
225      {
226          $applyTemplates = $this->appendElement($ir, 'applyTemplates');
227          if ($node->hasAttribute('select'))
228          {
229              $applyTemplates->setAttribute('select', $node->getAttribute('select'));
230          }
231      }
232   
233      /**
234      * Parse an <xsl:attribute/> node into the internal representation
235      *
236      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
237      * @param  DOMElement $node <xsl:attribute/> node
238      * @return void
239      */
240      protected function parseXslAttribute(DOMElement $ir, DOMElement $node)
241      {
242          $attribute = $this->appendElement($ir, 'attribute');
243          $attribute->setAttribute('name', $node->getAttribute('name'));
244          $this->parseChildren($attribute, $node);
245      }
246   
247      /**
248      * Parse an <xsl:choose/> node and its <xsl:when/> and <xsl:otherwise/> children into the
249      * internal representation
250      *
251      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
252      * @param  DOMElement $node <xsl:choose/> node
253      * @return void
254      */
255      protected function parseXslChoose(DOMElement $ir, DOMElement $node)
256      {
257          $switch = $this->appendElement($ir, 'switch');
258          foreach ($this->query('./xsl:when', $node) as $when)
259          {
260              // Create a <case/> element with the original test condition in @test
261              $case = $this->appendElement($switch, 'case');
262              $case->setAttribute('test', $when->getAttribute('test'));
263              $this->parseChildren($case, $when);
264          }
265   
266          // Add the default branch, which is presumed to be last
267          foreach ($this->query('./xsl:otherwise', $node) as $otherwise)
268          {
269              $case = $this->appendElement($switch, 'case');
270              $this->parseChildren($case, $otherwise);
271   
272              // There should be only one <xsl:otherwise/> but we'll break anyway
273              break;
274          }
275      }
276   
277      /**
278      * Parse an <xsl:comment/> node into the internal representation
279      *
280      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
281      * @param  DOMElement $node <xsl:comment/> node
282      * @return void
283      */
284      protected function parseXslComment(DOMElement $ir, DOMElement $node)
285      {
286          $comment = $this->appendElement($ir, 'comment');
287          $this->parseChildren($comment, $node);
288      }
289   
290      /**
291      * Parse an <xsl:copy-of/> node into the internal representation
292      *
293      * NOTE: only attributes are supported
294      *
295      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
296      * @param  DOMElement $node <xsl:copy-of/> node
297      * @return void
298      */
299      protected function parseXslCopyOf(DOMElement $ir, DOMElement $node)
300      {
301          $expr = $node->getAttribute('select');
302          if (preg_match('#^@[-\\w]+(?:\\s*\\|\\s*@[-\\w]+)*$#', $expr, $m))
303          {
304              // <xsl:copy-of select="@foo"/>
305              $this->appendConditionalAttributes($ir, $expr);
306          }
307          elseif ($expr === '@*')
308          {
309              // <xsl:copy-of select="@*"/>
310              $this->appendElement($ir, 'copyOfAttributes');
311          }
312          else
313          {
314              throw new RuntimeException("Unsupported <xsl:copy-of/> expression '" . $expr . "'");
315          }
316      }
317   
318      /**
319      * Parse an <xsl:element/> node into the internal representation
320      *
321      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
322      * @param  DOMElement $node <xsl:element/> node
323      * @return void
324      */
325      protected function parseXslElement(DOMElement $ir, DOMElement $node)
326      {
327          $element = $this->appendElement($ir, 'element');
328          $element->setAttribute('name', $node->getAttribute('name'));
329          $this->parseChildren($element, $node);
330      }
331   
332      /**
333      * Parse an <xsl:if/> node into the internal representation
334      *
335      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
336      * @param  DOMElement $node <xsl:if/> node
337      * @return void
338      */
339      protected function parseXslIf(DOMElement $ir, DOMElement $node)
340      {
341          // An <xsl:if/> is represented by a <switch/> with only one <case/>
342          $switch = $this->appendElement($ir, 'switch');
343          $case   = $this->appendElement($switch, 'case');
344          $case->setAttribute('test', $node->getAttribute('test'));
345   
346          // Parse this branch's content
347          $this->parseChildren($case, $node);
348      }
349   
350      /**
351      * Parse an <xsl:text/> node into the internal representation
352      *
353      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
354      * @param  DOMElement $node <xsl:text/> node
355      * @return void
356      */
357      protected function parseXslText(DOMElement $ir, DOMElement $node)
358      {
359          $this->appendLiteralOutput($ir, $node->textContent);
360          if ($node->getAttribute('disable-output-escaping') === 'yes')
361          {
362              $ir->lastChild->setAttribute('disable-output-escaping', 'yes');
363          }
364      }
365   
366      /**
367      * Parse an <xsl:value-of/> node into the internal representation
368      *
369      * @param  DOMElement $ir   Node in the internal representation that represents the node's parent
370      * @param  DOMElement $node <xsl:value-of/> node
371      * @return void
372      */
373      protected function parseXslValueOf(DOMElement $ir, DOMElement $node)
374      {
375          $this->appendXPathOutput($ir, $node->getAttribute('select'));
376          if ($node->getAttribute('disable-output-escaping') === 'yes')
377          {
378              $ir->lastChild->setAttribute('disable-output-escaping', 'yes');
379          }
380      }
381  }