[ Index ] |
PHP Cross Reference of phpBB-3.3.3-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2020 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($ir); 47 $this->addElementIds($ir); 48 $this->addCloseTagElements($ir); 49 $this->markVoidElements($ir); 50 $this->optimizer->optimize($ir); 51 $this->markConditionalCloseTagElements($ir); 52 $this->setOutputContext($ir); 53 $this->markBranchTables($ir); 54 } 55 56 /** 57 * Add <closeTag/> elements everywhere an open start tag should be closed 58 * 59 * @param DOMDocument $ir 60 * @return void 61 */ 62 protected function addCloseTagElements(DOMDocument $ir) 63 { 64 $exprs = [ 65 '//applyTemplates[not(ancestor::attribute)]', 66 '//comment', 67 '//element', 68 '//output[not(ancestor::attribute)]' 69 ]; 70 foreach ($this->query(implode('|', $exprs)) as $node) 71 { 72 $parentElementId = $this->getParentElementId($node); 73 if (isset($parentElementId)) 74 { 75 $node->parentNode 76 ->insertBefore($ir->createElement('closeTag'), $node) 77 ->setAttribute('id', $parentElementId); 78 } 79 80 // Append a <closeTag/> to <element/> nodes to ensure that empty elements get closed 81 if ($node->nodeName === 'element') 82 { 83 $id = $node->getAttribute('id'); 84 $this->appendElement($node, 'closeTag')->setAttribute('id', $id); 85 } 86 } 87 } 88 89 /** 90 * Add an empty default <case/> to <switch/> nodes that don't have one 91 * 92 * @param DOMDocument $ir 93 * @return void 94 */ 95 protected function addDefaultCase(DOMDocument $ir) 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 * @param DOMDocument $ir 107 * @return void 108 */ 109 protected function addElementIds(DOMDocument $ir) 110 { 111 $id = 0; 112 foreach ($this->query('//element') as $element) 113 { 114 $element->setAttribute('id', ++$id); 115 } 116 } 117 118 /** 119 * Get the context type for given output element 120 * 121 * @param DOMNode $output 122 * @return string 123 */ 124 protected function getOutputContext(DOMNode $output) 125 { 126 $contexts = [ 127 'boolean(ancestor::attribute)' => 'attribute', 128 '@disable-output-escaping="yes"' => 'raw', 129 'count(ancestor::element[@name="script"])' => 'raw' 130 ]; 131 foreach ($contexts as $expr => $context) 132 { 133 if ($this->evaluate($expr, $output)) 134 { 135 return $context; 136 } 137 } 138 139 return 'text'; 140 } 141 142 /** 143 * Get the ID of the closest "element" ancestor 144 * 145 * @param DOMNode $node Context node 146 * @return string|null 147 */ 148 protected function getParentElementId(DOMNode $node) 149 { 150 $parentNode = $node->parentNode; 151 while (isset($parentNode)) 152 { 153 if ($parentNode->nodeName === 'element') 154 { 155 return $parentNode->getAttribute('id'); 156 } 157 $parentNode = $parentNode->parentNode; 158 } 159 } 160 161 /** 162 * Mark switch elements that are used as branch tables 163 * 164 * If a switch is used for a series of equality tests against the same attribute or variable, the 165 * attribute/variable is stored within the switch as "branch-key" and the values it is compared 166 * against are stored JSON-encoded in the case as "branch-values". It can be used to create 167 * optimized branch tables 168 * 169 * @param DOMDocument $ir 170 * @return void 171 */ 172 protected function markBranchTables(DOMDocument $ir) 173 { 174 // Iterate over switch elements that have at least two case children with a test attribute 175 foreach ($this->query('//switch[case[2][@test]]') as $switch) 176 { 177 $this->markSwitchTable($switch); 178 } 179 } 180 181 /** 182 * Mark given switch element if it's used as a branch table 183 * 184 * @param DOMElement $switch 185 * @return void 186 */ 187 protected function markSwitchTable(DOMElement $switch) 188 { 189 $cases = []; 190 $maps = []; 191 foreach ($this->query('./case[@test]', $switch) as $i => $case) 192 { 193 $map = XPathHelper::parseEqualityExpr($case->getAttribute('test')); 194 if ($map === false) 195 { 196 return; 197 } 198 $maps += $map; 199 $cases[$i] = [$case, end($map)]; 200 } 201 if (count($maps) !== 1) 202 { 203 return; 204 } 205 206 $switch->setAttribute('branch-key', key($maps)); 207 foreach ($cases as list($case, $values)) 208 { 209 sort($values); 210 $case->setAttribute('branch-values', serialize($values)); 211 } 212 } 213 214 /** 215 * Mark conditional <closeTag/> nodes 216 * 217 * @param DOMDocument $ir 218 * @return void 219 */ 220 protected function markConditionalCloseTagElements(DOMDocument $ir) 221 { 222 foreach ($this->query('//closeTag') as $closeTag) 223 { 224 $id = $closeTag->getAttribute('id'); 225 226 // For each <switch/> ancestor, look for a <closeTag/> and that is either a sibling or 227 // the descendant of a sibling, and that matches the id 228 $query = 'ancestor::switch/' 229 . 'following-sibling::*/' 230 . 'descendant-or-self::closeTag[@id = "' . $id . '"]'; 231 foreach ($this->query($query, $closeTag) as $following) 232 { 233 // Mark following <closeTag/> nodes to indicate that the status of this tag must 234 // be checked before it is closed 235 $following->setAttribute('check', ''); 236 237 // Mark the current <closeTag/> to indicate that it must set a flag to indicate 238 // that its tag has been closed 239 $closeTag->setAttribute('set', ''); 240 } 241 } 242 } 243 244 /** 245 * Mark void elements 246 * 247 * @param DOMDocument $ir 248 * @return void 249 */ 250 protected function markVoidElements(DOMDocument $ir) 251 { 252 foreach ($this->query('//element') as $element) 253 { 254 // Test whether this element is (maybe) void 255 $elName = $element->getAttribute('name'); 256 if (strpos($elName, '{') !== false) 257 { 258 // Dynamic element names must be checked at runtime 259 $element->setAttribute('void', 'maybe'); 260 } 261 elseif (preg_match($this->voidRegexp, $elName)) 262 { 263 // Static element names can be checked right now 264 $element->setAttribute('void', 'yes'); 265 } 266 } 267 } 268 269 /** 270 * Fill in output context 271 * 272 * @param DOMDocument $ir 273 * @return void 274 */ 275 protected function setOutputContext(DOMDocument $ir) 276 { 277 foreach ($this->query('//output') as $output) 278 { 279 $output->setAttribute('escape', $this->getOutputContext($output)); 280 } 281 } 282 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Feb 14 20:08:31 2021 | Cross-referenced by PHPXref 0.7.1 |