[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |