Skip to content

Commit 983d4de

Browse files
stefanfiskondrejmirtes
authored andcommitted
Make DOMNamedNodeMap generic
Resolves phpstan/phpstan#13365.
1 parent b3ae3d2 commit 983d4de

File tree

4 files changed

+64
-12
lines changed

4 files changed

+64
-12
lines changed

conf/config.neon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ parameters:
2626
bleedingEdge: false
2727
checkNonStringableDynamicAccess: false
2828
checkParameterCastableToNumberFunctions: false
29-
skipCheckGenericClasses: []
29+
skipCheckGenericClasses:
30+
- DOMNamedNodeMap
3031
stricterFunctionMap: false
3132
reportPreciseLineForUnusedFunctionParameter: false
3233
internalTag: false

stubs/dom.stub

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ class DOMNode
3131
{
3232

3333
/**
34-
* @var DOMNamedNodeMap|null
34+
* @var DOMNamedNodeMap<DOMAttr>|null
3535
*/
3636
public $attributes;
3737

3838
/**
39-
* @phpstan-assert-if-true =DOMNamedNodeMap $this->attributes
39+
* @phpstan-assert-if-true =DOMNamedNodeMap<DOMAttr> $this->attributes
4040
* @return bool
4141
*/
4242
public function hasAttributes() {}
@@ -46,7 +46,7 @@ class DOMNode
4646
class DOMElement extends DOMNode
4747
{
4848

49-
/** @var DOMNamedNodeMap */
49+
/** @var DOMNamedNodeMap<DOMAttr> */
5050
public $attributes;
5151

5252
/** @var DOMDocument */
@@ -69,10 +69,9 @@ class DOMElement extends DOMNode
6969

7070
/**
7171
* @template-covariant TNode as DOMNode
72-
* @implements Traversable<int, TNode>
7372
* @implements IteratorAggregate<int, TNode>
7473
*/
75-
class DOMNodeList implements Traversable, IteratorAggregate, Countable
74+
class DOMNodeList implements IteratorAggregate, Countable
7675
{
7776

7877
/**
@@ -96,7 +95,7 @@ class DOMXPath
9695

9796
}
9897

99-
class DOMAttr
98+
class DOMAttr extends DOMNode
10099
{
101100

102101
/** @var DOMDocument */
@@ -155,11 +154,36 @@ class DOMProcessingInstruction
155154
}
156155

157156
/**
157+
* @template-covariant TNode as DOMNode
158+
* @implements IteratorAggregate<int, TNode>
159+
*
158160
* @property-read int $length
159161
*/
160-
class DOMNamedNodeMap
162+
class DOMNamedNodeMap implements IteratorAggregate, Countable
161163
{
164+
/**
165+
* @return Iterator<TNode>
166+
*/
167+
public function getIterator(): Iterator {}
162168

169+
/**
170+
* @param string $qualifiedName
171+
* @return TNode|null
172+
*/
173+
public function getNamedItem($qualifiedName): ?DOMNode {}
174+
175+
/**
176+
* @param string|null $namespace
177+
* @param string $localName
178+
* @return TNode|null
179+
*/
180+
public function getNamedItemNS($namespace, $localName): ?DOMNode {}
181+
182+
/**
183+
* @param int $index
184+
* @return TNode|null
185+
*/
186+
public function item($index): ?DOMNode {}
163187
}
164188

165189
class DOMText

tests/PHPStan/Analyser/nsrt/bug-13076.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ class Foo
99
public function test(\DOMNode $node): void
1010
{
1111
if ($node->hasAttributes()) {
12-
assertType('DOMNamedNodeMap', $node->attributes);
12+
assertType('DOMNamedNodeMap<DOMAttr>', $node->attributes);
1313
} else {
14-
assertType('DOMNamedNodeMap|null', $node->attributes);
14+
assertType('DOMNamedNodeMap<DOMAttr>|null', $node->attributes);
1515
}
1616
}
1717

1818
public function testElement(\DOMElement $node): void
1919
{
2020
if ($node->hasAttributes()) {
21-
assertType('DOMNamedNodeMap', $node->attributes);
21+
assertType('DOMNamedNodeMap<DOMAttr>', $node->attributes);
2222
} else {
23-
assertType('DOMNamedNodeMap', $node->attributes);
23+
assertType('DOMNamedNodeMap<DOMAttr>', $node->attributes);
2424
}
2525
}
2626
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug13365;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
public function test(\DOMElement $element): void
10+
{
11+
$attributes = $element->attributes;
12+
13+
if ($attributes === null) {
14+
return;
15+
}
16+
17+
assertType('DOMNamedNodeMap<DOMAttr>', $attributes);
18+
assertType('Iterator<mixed, DOMAttr>', $attributes->getIterator());
19+
assertType('DOMAttr|null', $attributes->getNamedItem('foo'));
20+
assertType('DOMAttr|null', $attributes->getNamedItemNS('foo', 'bar'));
21+
assertType('DOMAttr|null', $attributes->item(0));
22+
23+
foreach ($element->attributes ?? [] as $attr) {
24+
assertType('DOMAttr', $attr);
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)