|
55 | 55 | use Kint\Value\Representation\ContainerRepresentation; |
56 | 56 | use Kint\Value\StringValue; |
57 | 57 | use LogicException; |
| 58 | +use ReflectionClass; |
58 | 59 |
|
59 | 60 | class DomPlugin extends AbstractPlugin implements PluginBeginInterface |
60 | 61 | { |
61 | 62 | /** |
62 | | - * Reflection doesn't work below 8.1, also it won't show readonly status. |
| 63 | + * Reflection doesn't show readonly status. |
63 | 64 | * |
64 | 65 | * In order to ensure this is stable enough we're only going to provide |
65 | 66 | * properties for element and node. If subclasses like attr or document |
@@ -102,12 +103,9 @@ class DomPlugin extends AbstractPlugin implements PluginBeginInterface |
102 | 103 | 'previousElementSibling' => true, |
103 | 104 | 'nextElementSibling' => true, |
104 | 105 | 'innerHTML' => false, |
105 | | - 'outerHTML' => false, |
| 106 | + 'outerHTML' => true, |
106 | 107 | 'substitutedNodeValue' => false, |
107 | | - ]; |
108 | | - |
109 | | - public const DOM_NS_VERSIONS = [ |
110 | | - 'outerHTML' => KINT_PHP85, |
| 108 | + 'children' => true, |
111 | 109 | ]; |
112 | 110 |
|
113 | 111 | /** |
@@ -208,6 +206,9 @@ class DomPlugin extends AbstractPlugin implements PluginBeginInterface |
208 | 206 | */ |
209 | 207 | public static bool $verbose = false; |
210 | 208 |
|
| 209 | + /** @psalm-var array<class-string, array<string, bool>> cache of properties for getKnownProperties */ |
| 210 | + protected static array $property_cache = []; |
| 211 | + |
211 | 212 | protected ClassMethodsPlugin $methods_plugin; |
212 | 213 | protected ClassStaticsPlugin $statics_plugin; |
213 | 214 |
|
@@ -259,12 +260,14 @@ public function parseBegin(&$var, ContextInterface $c): ?AbstractValue |
259 | 260 | /** @psalm-param Node|DOMNode $var */ |
260 | 261 | private function parseProperty(object $var, string $prop, ContextInterface $c): AbstractValue |
261 | 262 | { |
262 | | - if (!isset($var->{$prop})) { |
| 263 | + // Suppress deprecation message |
| 264 | + if (@!isset($var->{$prop})) { |
263 | 265 | return new FixedWidthValue($c, null); |
264 | 266 | } |
265 | 267 |
|
266 | 268 | $parser = $this->getParser(); |
267 | | - $value = $var->{$prop}; |
| 269 | + // Suppress deprecation message |
| 270 | + @$value = $var->{$prop}; |
268 | 271 |
|
269 | 272 | if (\is_scalar($value)) { |
270 | 273 | return $parser->parse($value, $c); |
@@ -450,40 +453,49 @@ private function parseNode(object $var, ContextInterface $c): DomNodeValue |
450 | 453 | */ |
451 | 454 | public static function getKnownProperties(object $var): array |
452 | 455 | { |
453 | | - if ($var instanceof Node) { |
454 | | - $known_properties = self::NODE_PROPS; |
455 | | - if ($var instanceof Element) { |
456 | | - $known_properties += self::ELEMENT_PROPS; |
457 | | - } |
458 | | - |
459 | | - if ($var instanceof Document) { |
460 | | - $known_properties['textContent'] = true; |
461 | | - } |
| 456 | + if (KINT_PHP81) { |
| 457 | + $r = new ReflectionClass($var); |
| 458 | + $classname = $r->getName(); |
| 459 | + |
| 460 | + if (!isset(self::$property_cache[$classname])) { |
| 461 | + self::$property_cache[$classname] = []; |
| 462 | + |
| 463 | + foreach ($r->getProperties() as $prop) { |
| 464 | + if ($prop->isStatic()) { |
| 465 | + continue; |
| 466 | + } |
| 467 | + |
| 468 | + $declaring = $prop->getDeclaringClass()->getName(); |
| 469 | + $name = $prop->name; |
| 470 | + |
| 471 | + if (\in_array($declaring, [Node::class, Element::class], true)) { |
| 472 | + $readonly = self::NODE_PROPS[$name] ?? self::ELEMENT_PROPS[$name]; |
| 473 | + } elseif (\in_array($declaring, [DOMNode::class, DOMElement::class], true)) { |
| 474 | + $readonly = self::DOMNODE_PROPS[$name] ?? self::DOMELEMENT_PROPS[$name]; |
| 475 | + } else { |
| 476 | + continue; |
| 477 | + } |
| 478 | + |
| 479 | + self::$property_cache[$classname][$prop->name] = $readonly; |
| 480 | + } |
462 | 481 |
|
463 | | - if ($var instanceof Attr || $var instanceof CharacterData) { |
464 | | - $known_properties['nodeValue'] = false; |
465 | | - } |
| 482 | + if ($var instanceof Document) { |
| 483 | + self::$property_cache[$classname]['textContent'] = true; |
| 484 | + } |
466 | 485 |
|
467 | | - foreach (self::DOM_NS_VERSIONS as $key => $val) { |
468 | | - /** |
469 | | - * @psalm-var bool $val |
470 | | - * Psalm bug #4509 |
471 | | - */ |
472 | | - if (false === $val) { |
473 | | - unset($known_properties[$key]); // @codeCoverageIgnore |
| 486 | + if ($var instanceof Attr || $var instanceof CharacterData) { |
| 487 | + self::$property_cache[$classname]['nodeValue'] = false; |
474 | 488 | } |
475 | 489 | } |
| 490 | + |
| 491 | + $known_properties = self::$property_cache[$classname]; |
476 | 492 | } else { |
477 | 493 | $known_properties = self::DOMNODE_PROPS; |
478 | 494 | if ($var instanceof DOMElement) { |
479 | 495 | $known_properties += self::DOMELEMENT_PROPS; |
480 | 496 | } |
481 | 497 |
|
482 | 498 | foreach (self::DOM_VERSIONS as $key => $val) { |
483 | | - /** |
484 | | - * @psalm-var bool $val |
485 | | - * Psalm bug #4509 |
486 | | - */ |
487 | 499 | if (false === $val) { |
488 | 500 | unset($known_properties[$key]); // @codeCoverageIgnore |
489 | 501 | } |
|
0 commit comments