Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ parameters:
bleedingEdge: false
checkNonStringableDynamicAccess: false
checkParameterCastableToNumberFunctions: false
skipCheckGenericClasses: []
skipCheckGenericClasses:
- DOMNamedNodeMap
stricterFunctionMap: false
reportPreciseLineForUnusedFunctionParameter: false
internalTag: false
Expand Down
38 changes: 31 additions & 7 deletions stubs/dom.stub
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ class DOMNode
{

/**
* @var DOMNamedNodeMap|null
* @var DOMNamedNodeMap<DOMAttr>|null
*/
public $attributes;

/**
* @phpstan-assert-if-true =DOMNamedNodeMap $this->attributes
* @phpstan-assert-if-true =DOMNamedNodeMap<DOMAttr> $this->attributes
* @return bool
*/
public function hasAttributes() {}
Expand All @@ -46,7 +46,7 @@ class DOMNode
class DOMElement extends DOMNode
{

/** @var DOMNamedNodeMap */
/** @var DOMNamedNodeMap<DOMAttr> */
public $attributes;

/** @var DOMDocument */
Expand All @@ -69,10 +69,9 @@ class DOMElement extends DOMNode

/**
* @template-covariant TNode as DOMNode
* @implements Traversable<int, TNode>
* @implements IteratorAggregate<int, TNode>
*/
class DOMNodeList implements Traversable, IteratorAggregate, Countable
class DOMNodeList implements IteratorAggregate, Countable
{

/**
Expand All @@ -96,7 +95,7 @@ class DOMXPath

}

class DOMAttr
class DOMAttr extends DOMNode
{

/** @var DOMDocument */
Expand Down Expand Up @@ -155,11 +154,36 @@ class DOMProcessingInstruction
}

/**
* @template-covariant TNode as DOMNode
* @implements IteratorAggregate<int, TNode>
*
* @property-read int $length
*/
class DOMNamedNodeMap
class DOMNamedNodeMap implements IteratorAggregate, Countable
{
/**
* @return Iterator<TNode>
*/
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
Expand Down
8 changes: 4 additions & 4 deletions tests/PHPStan/Analyser/nsrt/bug-13076.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ class Foo
public function test(\DOMNode $node): void
{
if ($node->hasAttributes()) {
assertType('DOMNamedNodeMap', $node->attributes);
assertType('DOMNamedNodeMap<DOMAttr>', $node->attributes);
} else {
assertType('DOMNamedNodeMap|null', $node->attributes);
assertType('DOMNamedNodeMap<DOMAttr>|null', $node->attributes);
}
}

public function testElement(\DOMElement $node): void
{
if ($node->hasAttributes()) {
assertType('DOMNamedNodeMap', $node->attributes);
assertType('DOMNamedNodeMap<DOMAttr>', $node->attributes);
} else {
assertType('DOMNamedNodeMap', $node->attributes);
assertType('DOMNamedNodeMap<DOMAttr>', $node->attributes);
}
}
}
27 changes: 27 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-13365.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace Bug13365;

use function PHPStan\Testing\assertType;

class Foo
{
public function test(\DOMElement $element): void
{
$attributes = $element->attributes;

if ($attributes === null) {
return;
}

assertType('DOMNamedNodeMap<DOMAttr>', $attributes);
assertType('Iterator<mixed, DOMAttr>', $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);
}
}
}
Loading