diff --git a/stubs/dom.stub b/stubs/dom.stub index 074bdd5102..436fff0281 100644 --- a/stubs/dom.stub +++ b/stubs/dom.stub @@ -31,12 +31,12 @@ class DOMNode { /** - * @var DOMNamedNodeMap|null + * @var DOMNamedNodeMap|null */ public $attributes; /** - * @phpstan-assert-if-true =DOMNamedNodeMap $this->attributes + * @phpstan-assert-if-true =DOMNamedNodeMap $this->attributes * @return bool */ public function hasAttributes() {} @@ -46,7 +46,7 @@ class DOMNode class DOMElement extends DOMNode { - /** @var DOMNamedNodeMap */ + /** @var DOMNamedNodeMap */ public $attributes; /** @var DOMDocument */ @@ -96,7 +96,7 @@ class DOMXPath } -class DOMAttr +class DOMAttr extends DOMNode { /** @var DOMDocument */ @@ -155,11 +155,37 @@ class DOMProcessingInstruction } /** + * @template-covariant TNode as DOMNode + * @implements Traversable + * @implements IteratorAggregate + * * @property-read int $length */ -class DOMNamedNodeMap +class DOMNamedNodeMap implements Traversable, IteratorAggregate, Countable { + /** + * @return Iterator + */ + public function getIterator(): Iterator {} + + /** + * @param string $qualifiedName + * @return TNode|null + */ + public function getNamedItem($qualifiedName): ?DOMNode {} + /** + * @param string|null $namespace + * @param string $localName + * @return TNode|null + */ + public function getNamedItemNS($namespace, $localName): ?DOMNode {} + + /** + * @param int $index + * @return TNode|null + */ + public function item($index): ?DOMNode {} } class DOMText diff --git a/tests/PHPStan/Analyser/nsrt/bug-13076.php b/tests/PHPStan/Analyser/nsrt/bug-13076.php index dc6d25a8b6..b91cdeb5b7 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-13076.php +++ b/tests/PHPStan/Analyser/nsrt/bug-13076.php @@ -9,18 +9,18 @@ class Foo public function test(\DOMNode $node): void { if ($node->hasAttributes()) { - assertType('DOMNamedNodeMap', $node->attributes); + assertType('DOMNamedNodeMap', $node->attributes); } else { - assertType('DOMNamedNodeMap|null', $node->attributes); + assertType('DOMNamedNodeMap|null', $node->attributes); } } public function testElement(\DOMElement $node): void { if ($node->hasAttributes()) { - assertType('DOMNamedNodeMap', $node->attributes); + assertType('DOMNamedNodeMap', $node->attributes); } else { - assertType('DOMNamedNodeMap', $node->attributes); + assertType('DOMNamedNodeMap', $node->attributes); } } } diff --git a/tests/PHPStan/Analyser/nsrt/bug-13365.php b/tests/PHPStan/Analyser/nsrt/bug-13365.php new file mode 100644 index 0000000000..b2ef7c11f9 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13365.php @@ -0,0 +1,27 @@ +attributes; + + if ($attributes === null) { + return; + } + + assertType('DOMNamedNodeMap', $attributes); + assertType('Iterator', $attributes->getIterator()); + assertType('DOMAttr|null', $attributes->getNamedItem('foo')); + assertType('DOMAttr|null', $attributes->getNamedItemNS('foo', 'bar')); + assertType('DOMAttr|null', $attributes->item(0)); + + foreach ($element->attributes ?? [] as $attr) { + assertType('DOMAttr', $attr); + } + } +}