[ 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\RendererGenerators\PHP; 9 10 use DOMElement; 11 use DOMXPath; 12 use RuntimeException; 13 use s9e\TextFormatter\Configurator\Helpers\AVTHelper; 14 use s9e\TextFormatter\Configurator\Helpers\TemplateParser; 15 16 class Serializer 17 { 18 /** 19 * @var XPathConvertor XPath-to-PHP convertor 20 */ 21 public $convertor; 22 23 /** 24 * @var array Value of the "void" attribute of all elements, using the element's "id" as key 25 */ 26 protected $isVoid; 27 28 /** 29 * @var DOMXPath 30 */ 31 protected $xpath; 32 33 /** 34 * Constructor 35 */ 36 public function __construct() 37 { 38 $this->convertor = new XPathConvertor; 39 } 40 41 /** 42 * Convert an XPath expression (used in a condition) into PHP code 43 * 44 * This method is similar to convertXPath() but it selectively replaces some simple conditions 45 * with the corresponding DOM method for performance reasons 46 * 47 * @param string $expr XPath expression 48 * @return string PHP code 49 */ 50 public function convertCondition($expr) 51 { 52 return $this->convertor->convertCondition($expr); 53 } 54 55 /** 56 * Convert an XPath expression (used as value) into PHP code 57 * 58 * @param string $expr XPath expression 59 * @return string PHP code 60 */ 61 public function convertXPath($expr) 62 { 63 $php = $this->convertor->convertXPath($expr); 64 if (is_numeric($php)) 65 { 66 $php = "'" . $php . "'"; 67 } 68 69 return $php; 70 } 71 72 /** 73 * Serialize the internal representation of a template into PHP 74 * 75 * @param DOMElement $ir Internal representation 76 * @return string 77 */ 78 public function serialize(DOMElement $ir) 79 { 80 $this->xpath = new DOMXPath($ir->ownerDocument); 81 $this->isVoid = []; 82 foreach ($this->xpath->query('//element') as $element) 83 { 84 $this->isVoid[$element->getAttribute('id')] = $element->getAttribute('void'); 85 } 86 87 return $this->serializeChildren($ir); 88 } 89 90 /** 91 * Convert an attribute value template into PHP 92 * 93 * NOTE: escaping must be performed by the caller 94 * 95 * @link https://www.w3.org/TR/1999/REC-xslt-19991116#dt-attribute-value-template 96 * 97 * @param string $attrValue Attribute value template 98 * @return string 99 */ 100 protected function convertAttributeValueTemplate($attrValue) 101 { 102 $phpExpressions = []; 103 foreach (AVTHelper::parse($attrValue) as $token) 104 { 105 if ($token[0] === 'literal') 106 { 107 $phpExpressions[] = var_export($token[1], true); 108 } 109 else 110 { 111 $phpExpressions[] = $this->convertXPath($token[1]); 112 } 113 } 114 115 return implode('.', $phpExpressions); 116 } 117 118 /** 119 * Convert a dynamic xsl:attribute/xsl:element name into PHP 120 * 121 * @param string $attrValue Attribute value template 122 * @return string 123 */ 124 protected function convertDynamicNodeName(string $attrValue): string 125 { 126 if (strpos($attrValue, '{') === false) 127 { 128 return var_export(htmlspecialchars($attrValue, ENT_QUOTES), true); 129 } 130 131 return 'htmlspecialchars(' . $this->convertAttributeValueTemplate($attrValue) . ',' . ENT_QUOTES . ')'; 132 } 133 134 /** 135 * Escape given literal 136 * 137 * @param string $text Literal 138 * @param string $context Either "raw", "text" or "attribute" 139 * @return string Escaped literal 140 */ 141 protected function escapeLiteral($text, $context) 142 { 143 if ($context === 'raw') 144 { 145 return $text; 146 } 147 148 $escapeMode = ($context === 'attribute') ? ENT_COMPAT : ENT_NOQUOTES; 149 150 return htmlspecialchars($text, $escapeMode); 151 } 152 153 /** 154 * Escape the output of given PHP expression 155 * 156 * @param string $php PHP expression 157 * @param string $context Either "raw", "text" or "attribute" 158 * @return string PHP expression, including escaping mechanism 159 */ 160 protected function escapePHPOutput($php, $context) 161 { 162 if ($context === 'raw') 163 { 164 return $php; 165 } 166 167 $escapeMode = ($context === 'attribute') ? ENT_COMPAT : ENT_NOQUOTES; 168 169 return 'htmlspecialchars(' . $php . ',' . $escapeMode . ')'; 170 } 171 172 /** 173 * Test whether given switch has more than one non-default case 174 * 175 * @param DOMElement $switch <switch/> node 176 * @return bool 177 */ 178 protected function hasMultipleCases(DOMElement $switch) 179 { 180 return $this->xpath->evaluate('count(case[@test]) > 1', $switch); 181 } 182 183 /** 184 * Test whether given attribute declaration is a minimizable boolean attribute 185 * 186 * @param DOMElement $attribute <attribute/> node 187 * @param string $php Attribute content, in PHP 188 * @return boolean 189 */ 190 protected function isBooleanAttribute(DOMElement $attribute, string $php): bool 191 { 192 if ($attribute->getAttribute('boolean') !== 'yes') 193 { 194 return false; 195 } 196 197 return ($php === '' || $php === "\$this->out.='" . $attribute->getAttribute('name') . "';"); 198 } 199 200 /** 201 * Serialize an <applyTemplates/> node 202 * 203 * @param DOMElement $applyTemplates <applyTemplates/> node 204 * @return string 205 */ 206 protected function serializeApplyTemplates(DOMElement $applyTemplates) 207 { 208 $php = '$this->at($node'; 209 if ($applyTemplates->hasAttribute('select')) 210 { 211 $php .= ',' . var_export($applyTemplates->getAttribute('select'), true); 212 } 213 $php .= ');'; 214 215 return $php; 216 } 217 218 /** 219 * Serialize an <attribute/> node 220 * 221 * @param DOMElement $attribute <attribute/> node 222 * @return string 223 */ 224 protected function serializeAttribute(DOMElement $attribute) 225 { 226 $attrName = $attribute->getAttribute('name'); 227 228 // PHP representation of this attribute's name 229 $phpAttrName = $this->convertDynamicNodeName($attrName); 230 231 $php = "\$this->out.=' '." . $phpAttrName; 232 $content = $this->serializeChildren($attribute); 233 if (!$this->isBooleanAttribute($attribute, $content)) 234 { 235 $php .= ".'=\"';" . $content . "\$this->out.='\"'"; 236 } 237 $php .= ';'; 238 239 return $php; 240 } 241 242 /** 243 * Serialize all the children of given node into PHP 244 * 245 * @param DOMElement $ir Internal representation 246 * @return string 247 */ 248 protected function serializeChildren(DOMElement $ir) 249 { 250 $php = ''; 251 foreach ($ir->childNodes as $node) 252 { 253 if ($node instanceof DOMElement) 254 { 255 $methodName = 'serialize' . ucfirst($node->localName); 256 $php .= $this->$methodName($node); 257 } 258 } 259 260 return $php; 261 } 262 263 /** 264 * Serialize a <closeTag/> node 265 * 266 * @param DOMElement $closeTag <closeTag/> node 267 * @return string 268 */ 269 protected function serializeCloseTag(DOMElement $closeTag) 270 { 271 $php = "\$this->out.='>';"; 272 $id = $closeTag->getAttribute('id'); 273 274 if ($closeTag->hasAttribute('set')) 275 { 276 $php .= '$t' . $id . '=1;'; 277 } 278 279 if ($closeTag->hasAttribute('check')) 280 { 281 $php = 'if(!isset($t' . $id . ')){' . $php . '}'; 282 } 283 284 if ($this->isVoid[$id] === 'maybe') 285 { 286 // Check at runtime whether this element is not void 287 $php .= 'if(!$v' . $id . '){'; 288 } 289 290 return $php; 291 } 292 293 /** 294 * Serialize a <comment/> node 295 * 296 * @param DOMElement $comment <comment/> node 297 * @return string 298 */ 299 protected function serializeComment(DOMElement $comment) 300 { 301 return "\$this->out.='<!--';" 302 . $this->serializeChildren($comment) 303 . "\$this->out.='-->';"; 304 } 305 306 /** 307 * Serialize a <copyOfAttributes/> node 308 * 309 * @param DOMElement $copyOfAttributes <copyOfAttributes/> node 310 * @return string 311 */ 312 protected function serializeCopyOfAttributes(DOMElement $copyOfAttributes) 313 { 314 return 'foreach($node->attributes as $attribute)' 315 . '{' 316 . "\$this->out.=' ';" 317 . "\$this->out.=\$attribute->name;" 318 . "\$this->out.='=\"';" 319 . "\$this->out.=htmlspecialchars(\$attribute->value," . ENT_COMPAT . ");" 320 . "\$this->out.='\"';" 321 . '}'; 322 } 323 324 /** 325 * Serialize an <element/> node 326 * 327 * @param DOMElement $element <element/> node 328 * @return string 329 */ 330 protected function serializeElement(DOMElement $element) 331 { 332 $php = ''; 333 $elName = $element->getAttribute('name'); 334 $id = $element->getAttribute('id'); 335 $isVoid = $element->getAttribute('void'); 336 337 // Test whether this element name is dynamic 338 $isDynamic = (bool) (strpos($elName, '{') !== false); 339 340 // PHP representation of this element's name 341 $phpElName = $this->convertDynamicNodeName($elName); 342 343 // If the element name is dynamic, we cache its name for convenience and performance 344 if ($isDynamic) 345 { 346 $varName = '$e' . $id; 347 348 // Add the var declaration to the source 349 $php .= $varName . '=' . $phpElName . ';'; 350 351 // Replace the element name with the var 352 $phpElName = $varName; 353 } 354 355 // Test whether this element is void if we need this information 356 if ($isVoid === 'maybe') 357 { 358 $php .= '$v' . $id . '=preg_match(' . var_export(TemplateParser::$voidRegexp, true) . ',' . $phpElName . ');'; 359 } 360 361 // Open the start tag 362 $php .= "\$this->out.='<'." . $phpElName . ';'; 363 364 // Serialize this element's content 365 $php .= $this->serializeChildren($element); 366 367 // Close that element unless we know it's void 368 if ($isVoid !== 'yes') 369 { 370 $php .= "\$this->out.='</'." . $phpElName . ".'>';"; 371 } 372 373 // If this element was maybe void, serializeCloseTag() has put its content within an if 374 // block. We need to close that block 375 if ($isVoid === 'maybe') 376 { 377 $php .= '}'; 378 } 379 380 return $php; 381 } 382 383 /** 384 * Serialize a <switch/> node that has a branch-key attribute 385 * 386 * @param DOMElement $switch <switch/> node 387 * @return string 388 */ 389 protected function serializeHash(DOMElement $switch) 390 { 391 $statements = []; 392 foreach ($this->xpath->query('case[@branch-values]', $switch) as $case) 393 { 394 foreach (unserialize($case->getAttribute('branch-values')) as $value) 395 { 396 $statements[$value] = $this->serializeChildren($case); 397 } 398 } 399 if (!isset($case)) 400 { 401 throw new RuntimeException; 402 } 403 404 $defaultCase = $this->xpath->query('case[not(@branch-values)]', $switch)->item(0); 405 $defaultCode = ($defaultCase instanceof DOMElement) ? $this->serializeChildren($defaultCase) : ''; 406 $expr = $this->convertXPath($switch->getAttribute('branch-key')); 407 408 return SwitchStatement::generate($expr, $statements, $defaultCode); 409 } 410 411 /** 412 * Serialize an <output/> node 413 * 414 * @param DOMElement $output <output/> node 415 * @return string 416 */ 417 protected function serializeOutput(DOMElement $output) 418 { 419 $context = $output->getAttribute('escape'); 420 421 $php = '$this->out.='; 422 if ($output->getAttribute('type') === 'xpath') 423 { 424 $php .= $this->escapePHPOutput($this->convertXPath($output->textContent), $context); 425 } 426 else 427 { 428 $php .= var_export($this->escapeLiteral($output->textContent, $context), true); 429 } 430 $php .= ';'; 431 432 return $php; 433 } 434 435 /** 436 * Serialize a <switch/> node 437 * 438 * @param DOMElement $switch <switch/> node 439 * @return string 440 */ 441 protected function serializeSwitch(DOMElement $switch) 442 { 443 // Use a specialized branch table if the minimum number of branches is reached 444 if ($switch->hasAttribute('branch-key') && $this->hasMultipleCases($switch)) 445 { 446 return $this->serializeHash($switch); 447 } 448 449 $php = ''; 450 $if = 'if'; 451 foreach ($this->xpath->query('case', $switch) as $case) 452 { 453 if ($case->hasAttribute('test')) 454 { 455 $php .= $if . '(' . $this->convertCondition($case->getAttribute('test')) . ')'; 456 } 457 else 458 { 459 $php .= 'else'; 460 } 461 462 $php .= '{' . $this->serializeChildren($case) . '}'; 463 $if = 'elseif'; 464 } 465 466 return $php; 467 } 468 }
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 |