Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Serializer.php
001 <?php
002
003 /**
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2022 The s9e authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Configurator\RendererGenerators\PHP;
009
010 use DOMElement;
011 use DOMXPath;
012 use RuntimeException;
013 use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
014 use s9e\TextFormatter\Configurator\Helpers\TemplateParser;
015
016 class Serializer
017 {
018 /**
019 * @var XPathConvertor XPath-to-PHP convertor
020 */
021 public $convertor;
022
023 /**
024 * @var array Value of the "void" attribute of all elements, using the element's "id" as key
025 */
026 protected $isVoid;
027
028 /**
029 * @var DOMXPath
030 */
031 protected $xpath;
032
033 /**
034 * Constructor
035 */
036 public function __construct()
037 {
038 $this->convertor = new XPathConvertor;
039 }
040
041 /**
042 * Convert an XPath expression (used in a condition) into PHP code
043 *
044 * This method is similar to convertXPath() but it selectively replaces some simple conditions
045 * with the corresponding DOM method for performance reasons
046 *
047 * @param string $expr XPath expression
048 * @return string PHP code
049 */
050 public function convertCondition($expr)
051 {
052 return $this->convertor->convertCondition($expr);
053 }
054
055 /**
056 * Convert an XPath expression (used as value) into PHP code
057 *
058 * @param string $expr XPath expression
059 * @return string PHP code
060 */
061 public function convertXPath($expr)
062 {
063 $php = $this->convertor->convertXPath($expr);
064 if (is_numeric($php))
065 {
066 $php = "'" . $php . "'";
067 }
068
069 return $php;
070 }
071
072 /**
073 * Serialize the internal representation of a template into PHP
074 *
075 * @param DOMElement $ir Internal representation
076 * @return string
077 */
078 public function serialize(DOMElement $ir)
079 {
080 $this->xpath = new DOMXPath($ir->ownerDocument);
081 $this->isVoid = [];
082 foreach ($this->xpath->query('//element') as $element)
083 {
084 $this->isVoid[$element->getAttribute('id')] = $element->getAttribute('void');
085 }
086
087 return $this->serializeChildren($ir);
088 }
089
090 /**
091 * Convert an attribute value template into PHP
092 *
093 * NOTE: escaping must be performed by the caller
094 *
095 * @link https://www.w3.org/TR/1999/REC-xslt-19991116#dt-attribute-value-template
096 *
097 * @param string $attrValue Attribute value template
098 * @return string
099 */
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 }