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

TemplateModifier.php

Zuletzt modifiziert: 01.05.2026, 11:28 - Dateigröße: 5.10 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;
 009   
 010  use DOMAttr;
 011  use DOMDocument;
 012  use DOMText;
 013  use DOMXPath;
 014   
 015  abstract class TemplateModifier
 016  {
 017      /**
 018      * XSL namespace
 019      */
 020      const XMLNS_XSL = 'http://www.w3.org/1999/XSL/Transform';
 021   
 022      /**
 023      * Replace parts of a template that match given regexp
 024      *
 025      * Treats attribute values as plain text. Replacements within XPath expression is unsupported.
 026      * The callback must return an array with two elements. The first must be either of 'expression',
 027      * 'literal' or 'passthrough', and the second element depends on the first.
 028      *
 029      *  - 'expression' indicates that the replacement must be treated as an XPath expression such as
 030      *    '@foo', which must be passed as the second element.
 031      *
 032      *  - 'literal' indicates a literal (plain text) replacement, passed as its second element.
 033      *
 034      *  - 'passthrough' indicates that the replacement should the tag's content. It works differently
 035      *    whether it is inside an attribute's value or a text node. Within an attribute's value, the
 036      *    replacement will be the text content of the tag. Within a text node, the replacement
 037      *    becomes an <xsl:apply-templates/> node. A second optional argument can be passed to be used
 038      *    as its @select node-set.
 039      *
 040      * @param  string   $template Original template
 041      * @param  string   $regexp   Regexp for matching parts that need replacement
 042      * @param  callable $fn       Callback used to get the replacement
 043      * @return string             Processed template
 044      */
 045      public static function replaceTokens($template, $regexp, $fn)
 046      {
 047          $dom   = TemplateLoader::load($template);
 048          $xpath = new DOMXPath($dom);
 049          foreach ($xpath->query('//@*') as $attribute)
 050          {
 051              self::replaceTokensInAttribute($attribute, $regexp, $fn);
 052          }
 053          foreach ($xpath->query('//text()') as $node)
 054          {
 055              self::replaceTokensInText($node, $regexp, $fn);
 056          }
 057   
 058          return TemplateLoader::save($dom);
 059      }
 060   
 061      /**
 062      * Create a node that implements given replacement strategy
 063      *
 064      * @param  DOMDocument $dom
 065      * @param  array       $replacement
 066      * @return DOMNode
 067      */
 068      protected static function createReplacementNode(DOMDocument $dom, array $replacement)
 069      {
 070          if ($replacement[0] === 'expression')
 071          {
 072              $newNode = $dom->createElementNS(self::XMLNS_XSL, 'xsl:value-of');
 073              $newNode->setAttribute('select', $replacement[1]);
 074          }
 075          elseif ($replacement[0] === 'passthrough')
 076          {
 077              $newNode = $dom->createElementNS(self::XMLNS_XSL, 'xsl:apply-templates');
 078              if (isset($replacement[1]))
 079              {
 080                  $newNode->setAttribute('select', $replacement[1]);
 081              }
 082          }
 083          else
 084          {
 085              $newNode = $dom->createTextNode($replacement[1]);
 086          }
 087   
 088          return $newNode;
 089      }
 090   
 091      /**
 092      * Replace parts of an attribute that match given regexp
 093      *
 094      * @param  DOMAttr  $attribute Attribute
 095      * @param  string   $regexp    Regexp for matching parts that need replacement
 096      * @param  callable $fn        Callback used to get the replacement
 097      * @return void
 098      */
 099      protected static function replaceTokensInAttribute(DOMAttr $attribute, $regexp, $fn)
 100      {
 101          $attrValue = preg_replace_callback(
 102              $regexp,
 103              function ($m) use ($fn, $attribute)
 104              {
 105                  $replacement = $fn($m, $attribute);
 106                  if ($replacement[0] === 'expression' || $replacement[0] === 'passthrough')
 107                  {
 108                      // Use the node's text content as the default expression
 109                      $replacement[] = '.';
 110   
 111                      return '{' . $replacement[1] . '}';
 112                  }
 113                  else
 114                  {
 115                      return $replacement[1];
 116                  }
 117              },
 118              $attribute->value
 119          );
 120          $attribute->value = htmlspecialchars($attrValue, ENT_COMPAT, 'UTF-8');
 121      }
 122   
 123      /**
 124      * Replace parts of a text node that match given regexp
 125      *
 126      * @param  DOMText  $node   Text node
 127      * @param  string   $regexp Regexp for matching parts that need replacement
 128      * @param  callable $fn     Callback used to get the replacement
 129      * @return void
 130      */
 131      protected static function replaceTokensInText(DOMText $node, $regexp, $fn)
 132      {
 133          // Grab the node's parent so that we can rebuild the text with added variables right
 134          // before the node, using DOM's insertBefore(). Technically, it would make more sense
 135          // to create a document fragment, append nodes then replace the node with the fragment
 136          // but it leads to namespace redeclarations, which looks ugly
 137          $parentNode = $node->parentNode;
 138          $dom        = $node->ownerDocument;
 139   
 140          preg_match_all($regexp, $node->textContent, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
 141          $lastPos = 0;
 142          foreach ($matches as $m)
 143          {
 144              $pos = $m[0][1];
 145   
 146              // Catch-up to current position
 147              $text = substr($node->textContent, $lastPos, $pos - $lastPos);
 148              $parentNode->insertBefore($dom->createTextNode($text), $node);
 149              $lastPos = $pos + strlen($m[0][0]);
 150   
 151              // Get the replacement for this token
 152              $replacement = $fn(array_column($m, 0), $node);
 153              $newNode     = self::createReplacementNode($dom, $replacement);
 154              $parentNode->insertBefore($newNode, $node);
 155          }
 156   
 157          // Append the rest of the text
 158          $text = substr($node->textContent, $lastPos);
 159          $parentNode->insertBefore($dom->createTextNode($text), $node);
 160   
 161          // Now remove the old text node
 162          $parentNode->removeChild($node);
 163      }
 164  }