[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/Helpers/ -> TemplateModifier.php (source)

   1  <?php
   2  
   3  /**
   4  * @package   s9e\TextFormatter
   5  * @copyright Copyright (c) 2010-2022 The s9e authors
   6  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
   7  */
   8  namespace s9e\TextFormatter\Configurator\Helpers;
   9  
  10  use DOMAttr;
  11  use DOMDocument;
  12  use DOMText;
  13  use DOMXPath;
  14  
  15  abstract class TemplateModifier
  16  {
  17      /**
  18      * XSL namespace
  19      */
  20      const XMLNS_XSL = 'http://www.w3.org/1999/XSL/Transform';
  21  
  22      /**
  23      * Replace parts of a template that match given regexp
  24      *
  25      * Treats attribute values as plain text. Replacements within XPath expression is unsupported.
  26      * The callback must return an array with two elements. The first must be either of 'expression',
  27      * 'literal' or 'passthrough', and the second element depends on the first.
  28      *
  29      *  - 'expression' indicates that the replacement must be treated as an XPath expression such as
  30      *    '@foo', which must be passed as the second element.
  31      *
  32      *  - 'literal' indicates a literal (plain text) replacement, passed as its second element.
  33      *
  34      *  - 'passthrough' indicates that the replacement should the tag's content. It works differently
  35      *    whether it is inside an attribute's value or a text node. Within an attribute's value, the
  36      *    replacement will be the text content of the tag. Within a text node, the replacement
  37      *    becomes an <xsl:apply-templates/> node. A second optional argument can be passed to be used
  38      *    as its @select node-set.
  39      *
  40      * @param  string   $template Original template
  41      * @param  string   $regexp   Regexp for matching parts that need replacement
  42      * @param  callable $fn       Callback used to get the replacement
  43      * @return string             Processed template
  44      */
  45  	public static function replaceTokens($template, $regexp, $fn)
  46      {
  47          $dom   = TemplateLoader::load($template);
  48          $xpath = new DOMXPath($dom);
  49          foreach ($xpath->query('//@*') as $attribute)
  50          {
  51              self::replaceTokensInAttribute($attribute, $regexp, $fn);
  52          }
  53          foreach ($xpath->query('//text()') as $node)
  54          {
  55              self::replaceTokensInText($node, $regexp, $fn);
  56          }
  57  
  58          return TemplateLoader::save($dom);
  59      }
  60  
  61      /**
  62      * Create a node that implements given replacement strategy
  63      *
  64      * @param  DOMDocument $dom
  65      * @param  array       $replacement
  66      * @return DOMNode
  67      */
  68  	protected static function createReplacementNode(DOMDocument $dom, array $replacement)
  69      {
  70          if ($replacement[0] === 'expression')
  71          {
  72              $newNode = $dom->createElementNS(self::XMLNS_XSL, 'xsl:value-of');
  73              $newNode->setAttribute('select', $replacement[1]);
  74          }
  75          elseif ($replacement[0] === 'passthrough')
  76          {
  77              $newNode = $dom->createElementNS(self::XMLNS_XSL, 'xsl:apply-templates');
  78              if (isset($replacement[1]))
  79              {
  80                  $newNode->setAttribute('select', $replacement[1]);
  81              }
  82          }
  83          else
  84          {
  85              $newNode = $dom->createTextNode($replacement[1]);
  86          }
  87  
  88          return $newNode;
  89      }
  90  
  91      /**
  92      * Replace parts of an attribute that match given regexp
  93      *
  94      * @param  DOMAttr  $attribute Attribute
  95      * @param  string   $regexp    Regexp for matching parts that need replacement
  96      * @param  callable $fn        Callback used to get the replacement
  97      * @return void
  98      */
  99  	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  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1