[ Index ]

PHP Cross Reference of phpBB-3.3.12-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/Helpers/TemplateParser/ -> Normalizer.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 DOMNode;
  13  use s9e\TextFormatter\Configurator\Helpers\XPathHelper;
  14  
  15  class Normalizer extends IRProcessor
  16  {
  17      /**
  18      * @var Optimizer
  19      */
  20      protected $optimizer;
  21  
  22      /**
  23      * @var string Regexp that matches the names of all void elements
  24      * @link http://www.w3.org/TR/html-markup/syntax.html#void-elements
  25      */
  26      public $voidRegexp = '/^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/Di';
  27  
  28      /**
  29      * @param  Optimizer $optimizer
  30      * @return void
  31      */
  32  	public function __construct(Optimizer $optimizer)
  33      {
  34          $this->optimizer = $optimizer;
  35      }
  36  
  37      /**
  38      * Normalize an IR
  39      *
  40      * @param  DOMDocument $ir
  41      * @return void
  42      */
  43  	public function normalize(DOMDocument $ir)
  44      {
  45          $this->createXPath($ir);
  46          $this->addDefaultCase();
  47          $this->addElementIds();
  48          $this->addCloseTagElements($ir);
  49          $this->markVoidElements();
  50          $this->optimizer->optimize($ir);
  51          $this->markConditionalCloseTagElements();
  52          $this->setOutputContext();
  53          $this->markBranchTables();
  54          $this->markBooleanAttributes();
  55      }
  56  
  57      /**
  58      * Add <closeTag/> elements everywhere an open start tag should be closed
  59      *
  60      * @param  DOMDocument $ir
  61      * @return void
  62      */
  63  	protected function addCloseTagElements(DOMDocument $ir)
  64      {
  65          $exprs = [
  66              '//applyTemplates[not(ancestor::attribute)]',
  67              '//comment',
  68              '//element',
  69              '//output[not(ancestor::attribute)]'
  70          ];
  71          foreach ($this->query(implode('|', $exprs)) as $node)
  72          {
  73              $parentElementId = $this->getParentElementId($node);
  74              if (isset($parentElementId))
  75              {
  76                  $node->parentNode
  77                       ->insertBefore($ir->createElement('closeTag'), $node)
  78                       ->setAttribute('id', $parentElementId);
  79              }
  80  
  81              // Append a <closeTag/> to <element/> nodes to ensure that empty elements get closed
  82              if ($node->nodeName === 'element')
  83              {
  84                  $id = $node->getAttribute('id');
  85                  $this->appendElement($node, 'closeTag')->setAttribute('id', $id);
  86              }
  87          }
  88      }
  89  
  90      /**
  91      * Add an empty default <case/> to <switch/> nodes that don't have one
  92      *
  93      * @return void
  94      */
  95  	protected function addDefaultCase()
  96      {
  97          foreach ($this->query('//switch[not(case[not(@test)])]') as $switch)
  98          {
  99              $this->appendElement($switch, 'case');
 100          }
 101      }
 102  
 103      /**
 104      * Add an id attribute to <element/> nodes
 105      *
 106      * @return void
 107      */
 108  	protected function addElementIds()
 109      {
 110          $id = 0;
 111          foreach ($this->query('//element') as $element)
 112          {
 113              $element->setAttribute('id', ++$id);
 114          }
 115      }
 116  
 117      /**
 118      * Get the context type for given output element
 119      *
 120      * @param  DOMNode $output
 121      * @return string
 122      */
 123  	protected function getOutputContext(DOMNode $output)
 124      {
 125          $contexts = [
 126              'boolean(ancestor::attribute)'             => 'attribute',
 127              '@disable-output-escaping="yes"'           => 'raw',
 128              'count(ancestor::element[@name="script"])' => 'raw'
 129          ];
 130          foreach ($contexts as $expr => $context)
 131          {
 132              if ($this->evaluate($expr, $output))
 133              {
 134                  return $context;
 135              }
 136          }
 137  
 138          return 'text';
 139      }
 140  
 141      /**
 142      * Get the ID of the closest "element" ancestor
 143      *
 144      * @param  DOMNode     $node Context node
 145      * @return string|null
 146      */
 147  	protected function getParentElementId(DOMNode $node)
 148      {
 149          $parentNode = $node->parentNode;
 150          while (isset($parentNode))
 151          {
 152              if ($parentNode->nodeName === 'element')
 153              {
 154                  return $parentNode->getAttribute('id');
 155              }
 156              $parentNode = $parentNode->parentNode;
 157          }
 158      }
 159  
 160      /**
 161      * Mark switch elements that are used as branch tables
 162      *
 163      * If a switch is used for a series of equality tests against the same attribute or variable, the
 164      * attribute/variable is stored within the switch as "branch-key" and the values it is compared
 165      * against are stored JSON-encoded in the case as "branch-values". It can be used to create
 166      * optimized branch tables
 167      *
 168      * @return void
 169      */
 170  	protected function markBranchTables()
 171      {
 172          // Iterate over switch elements that have at least two case children with a test attribute
 173          foreach ($this->query('//switch[case[2][@test]]') as $switch)
 174          {
 175              $this->markSwitchTable($switch);
 176          }
 177      }
 178  
 179      /**
 180      * Mark given switch element if it's used as a branch table
 181      *
 182      * @param  DOMElement $switch
 183      * @return void
 184      */
 185  	protected function markSwitchTable(DOMElement $switch)
 186      {
 187          $cases = [];
 188          $maps  = [];
 189          foreach ($this->query('./case[@test]', $switch) as $i => $case)
 190          {
 191              $map = XPathHelper::parseEqualityExpr($case->getAttribute('test'));
 192              if ($map === false)
 193              {
 194                  return;
 195              }
 196              $maps     += $map;
 197              $cases[$i] = [$case, end($map)];
 198          }
 199          if (count($maps) !== 1)
 200          {
 201              return;
 202          }
 203  
 204          $switch->setAttribute('branch-key', key($maps));
 205          foreach ($cases as list($case, $values))
 206          {
 207              sort($values);
 208              $case->setAttribute('branch-values', serialize($values));
 209          }
 210      }
 211  
 212      /**
 213      * Mark conditional <closeTag/> nodes
 214      *
 215      * @return void
 216      */
 217  	protected function markConditionalCloseTagElements()
 218      {
 219          foreach ($this->query('//closeTag') as $closeTag)
 220          {
 221              $id = $closeTag->getAttribute('id');
 222  
 223              // For each <switch/> ancestor, look for a <closeTag/> and that is either a sibling or
 224              // the descendant of a sibling, and that matches the id
 225              $query = 'ancestor::switch/'
 226                     . 'following-sibling::*/'
 227                     . 'descendant-or-self::closeTag[@id = "' . $id . '"]';
 228              foreach ($this->query($query, $closeTag) as $following)
 229              {
 230                  // Mark following <closeTag/> nodes to indicate that the status of this tag must
 231                  // be checked before it is closed
 232                  $following->setAttribute('check', '');
 233  
 234                  // Mark the current <closeTag/> to indicate that it must set a flag to indicate
 235                  // that its tag has been closed
 236                  $closeTag->setAttribute('set', '');
 237              }
 238          }
 239      }
 240  
 241      /**
 242      * Mark boolean attributes
 243      *
 244      * The test is case-sensitive and only covers attribute that are minimized by libxslt
 245      *
 246      * @return void
 247      */
 248  	protected function markBooleanAttributes(): void
 249      {
 250          $attrNames = ['checked', 'compact', 'declare', 'defer', 'disabled', 'ismap', 'multiple', 'nohref', 'noresize', 'noshade', 'nowrap', 'readonly', 'selected'];
 251          foreach ($this->query('//attribute') as $attribute)
 252          {
 253              if (in_array($attribute->getAttribute('name'), $attrNames, true))
 254              {
 255                  $attribute->setAttribute('boolean', 'yes');
 256              }
 257          }
 258      }
 259  
 260      /**
 261      * Mark void elements
 262      *
 263      * @return void
 264      */
 265  	protected function markVoidElements()
 266      {
 267          foreach ($this->query('//element') as $element)
 268          {
 269              // Test whether this element is (maybe) void
 270              $elName = $element->getAttribute('name');
 271              if (strpos($elName, '{') !== false)
 272              {
 273                  // Dynamic element names must be checked at runtime
 274                  $element->setAttribute('void', 'maybe');
 275              }
 276              elseif (preg_match($this->voidRegexp, $elName))
 277              {
 278                  // Static element names can be checked right now
 279                  $element->setAttribute('void', 'yes');
 280              }
 281          }
 282      }
 283  
 284      /**
 285      * Fill in output context
 286      *
 287      * @return void
 288      */
 289  	protected function setOutputContext()
 290      {
 291          foreach ($this->query('//output') as $output)
 292          {
 293              $output->setAttribute('escape', $this->getOutputContext($output));
 294          }
 295      }
 296  }


Generated: Sun Jun 23 12:25:44 2024 Cross-referenced by PHPXref 0.7.1