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

Parser.php

Zuletzt modifiziert: 01.05.2026, 11:28 - 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  }