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

TemplateModifier.php

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