[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/Helpers/TemplateParser/ -> Parser.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\TemplateParser;
   9  
  10  use DOMDocument;
  11  use DOMElement;
  12  use DOMXPath;
  13  use RuntimeException;
  14  use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
  15  use s9e\TextFormatter\Configurator\Helpers\TemplateLoader;
  16  
  17  class Parser extends IRProcessor
  18  {
  19      /**
  20      * @var Normalizer
  21      */
  22      protected $normalizer;
  23  
  24      /**
  25      * @param  Normalizer $normalizer
  26      * @return void
  27      */
  28  	public function __construct(Normalizer $normalizer)
  29      {
  30          $this->normalizer = $normalizer;
  31      }
  32  
  33      /**
  34      * Parse a template into an internal representation
  35      *
  36      * @param  string      $template Source template
  37      * @return DOMDocument           Internal representation
  38      */
  39  	public function parse($template)
  40      {
  41          $dom = TemplateLoader::load($template);
  42  
  43          $ir = new DOMDocument;
  44          $ir->loadXML('<template/>');
  45  
  46          $this->createXPath($dom);
  47          $this->parseChildren($ir->documentElement, $dom->documentElement);
  48          $this->normalizer->normalize($ir);
  49  
  50          return $ir;
  51      }
  52  
  53      /**
  54      * Append <output/> elements corresponding to given AVT
  55      *
  56      * @param  DOMElement $parentNode Parent node
  57      * @param  string     $avt        Attribute value template
  58      * @return void
  59      */
  60  	protected function appendAVT(DOMElement $parentNode, $avt)
  61      {
  62          foreach (AVTHelper::parse($avt) as $token)
  63          {
  64              if ($token[0] === 'expression')
  65              {
  66                  $this->appendXPathOutput($parentNode, $token[1]);
  67              }
  68              else
  69              {
  70                  $this->appendLiteralOutput($parentNode, $token[1]);
  71              }
  72          }
  73      }
  74  
  75      /**
  76      * Append an <output/> element with literal content to given node
  77      *
  78      * @param  DOMElement $parentNode Parent node
  79      * @param  string     $content    Content to output
  80      * @return void
  81      */
  82  	protected function appendLiteralOutput(DOMElement $parentNode, $content)
  83      {
  84          if ($content === '')
  85          {
  86              return;
  87          }
  88  
  89          $this->appendElement($parentNode, 'output', htmlspecialchars($content, ENT_COMPAT))
  90               ->setAttribute('type', 'literal');
  91      }
  92  
  93      /**
  94      * Append the structure for a <xsl:copy-of/> element to given node
  95      *
  96      * @param  DOMElement $parentNode Parent node
  97      * @param  string     $expr       Select expression, which is should only contain attributes
  98      * @return void
  99      */
 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  }


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