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
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ parameters:
checkParameterCastableToNumberFunctions: false
skipCheckGenericClasses:
- DOMNamedNodeMap
- ParentIterator
- RecursiveCachingIterator
- RecursiveFilterIterator
- RecursiveRegexIterator
- ReflectionObject
stricterFunctionMap: false
reportPreciseLineForUnusedFunctionParameter: false
checkPrintfParameterTypes: false
Expand Down
2 changes: 2 additions & 0 deletions src/PhpDoc/ReflectionClassStubFilesExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ public function getFiles(): array
if (!$this->phpVersion->supportsLazyObjects()) {
return [
__DIR__ . '/../../stubs/ReflectionClass.stub',
__DIR__ . '/../../stubs/ReflectionObject.stub',
];
}

return [
__DIR__ . '/../../stubs/ReflectionClassWithLazyObjects.stub',
__DIR__ . '/../../stubs/ReflectionObjectWithLazyObjects.stub',
];
}

Expand Down
15 changes: 15 additions & 0 deletions stubs/ReflectionObject.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/**
* @template-covariant T of object
* @extends ReflectionClass<T>
*/
class ReflectionObject extends ReflectionClass
{

/**
* @param T $argument
*/
public function __construct($argument) {}

}
15 changes: 15 additions & 0 deletions stubs/ReflectionObjectWithLazyObjects.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/**
* @template T of object
* @extends ReflectionClass<T>
*/
class ReflectionObject extends ReflectionClass
{

/**
* @param T $argument
*/
public function __construct($argument) {}

}
87 changes: 87 additions & 0 deletions stubs/iterable.stub
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,93 @@ class RegexIterator extends FilterIterator {
public function key() {}
}

/**
* @template-covariant TKey
* @template-covariant TValue
* @template TIterator as RecursiveIterator<TKey, TValue>
*
* @template-extends FilterIterator<TKey, TValue, TIterator>
* @template-implements RecursiveIterator<TKey, TValue>
*/
abstract class RecursiveFilterIterator extends FilterIterator implements RecursiveIterator {
/**
* @param TIterator $iterator
*/
public function __construct(RecursiveIterator $iterator) {}

/**
* @return bool
*/
public function hasChildren() {}

/**
* @return static
*/
public function getChildren() {}
}

/**
* @template-covariant TKey
* @template-covariant TValue
* @template TIterator as RecursiveIterator<TKey, TValue>
*
* @template-extends RecursiveFilterIterator<TKey, TValue, TIterator>
*/
class ParentIterator extends RecursiveFilterIterator {
}

/**
* @template TKey
* @template TValue
* @template TIterator as Iterator<TKey, TValue>
*
* @template-extends CachingIterator<TKey, TValue, TIterator>
* @template-implements RecursiveIterator<TKey, TValue>
*/
class RecursiveCachingIterator extends CachingIterator implements RecursiveIterator {
/**
* @param TIterator $iterator
* @param int-mask-of<CachingIterator::*> $flags
*/
public function __construct(Iterator $iterator, int $flags = CachingIterator::CALL_TOSTRING) {}

/**
* @return bool
*/
public function hasChildren() {}

/**
* @return static
*/
public function getChildren() {}
}

/**
* @template TKey
* @template TValue
* @template TIterator of RecursiveIterator<TKey, TValue>
*
* @template-extends RegexIterator<TKey, TValue, TIterator>
* @template-implements RecursiveIterator<TKey, TValue>
*/
class RecursiveRegexIterator extends RegexIterator implements RecursiveIterator {
/**
* @param TIterator $iterator
* @param RegexIterator::MATCH|RegexIterator::GET_MATCH|RegexIterator::ALL_MATCHES|RegexIterator::SPLIT|RegexIterator::REPLACE $mode
*/
public function __construct(RecursiveIterator $iterator, string $pattern, int $mode = RegexIterator::MATCH, int $flags = 0, int $preg_flags = 0) {}

/**
* @return bool
*/
public function hasChildren() {}

/**
* @return static
*/
public function getChildren() {}
}

/**
* @template-implements Iterator<never, never>
*/
Expand Down
33 changes: 33 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-15032.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php declare(strict_types = 1);

namespace Bug15032;

use ReflectionObject;
use function PHPStan\Testing\assertType;

/**
* @template T of object
* @param T $object
* @return T
*/
function createInstance(object $object): object
{
$ref = new ReflectionObject($object);
assertType('ReflectionObject<T of object (function Bug15032\createInstance(), argument)>', $ref);

$ret = $ref->newInstance();
assertType('T of object (function Bug15032\createInstance(), argument)', $ret);

return $ret;
}

function concreteObject(\Exception $e): void
{
$ref = new ReflectionObject($e);
assertType('ReflectionObject<Exception>', $ref);
assertType('class-string<Exception>', $ref->getName());
assertType('class-string<Exception>', $ref->name);
assertType('Exception', $ref->newInstance());
assertType('Exception', $ref->newInstanceArgs([]));
assertType('Exception', $ref->newInstanceWithoutConstructor());
}
61 changes: 61 additions & 0 deletions tests/PHPStan/Analyser/nsrt/recursive-iterators.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php declare(strict_types = 1);

namespace RecursiveIteratorsStub;

use ParentIterator;
use RecursiveArrayIterator;
use RecursiveCachingIterator;
use RecursiveRegexIterator;
use SplFileInfo;
use function PHPStan\Testing\assertType;

class Foo
{

/**
* @param ParentIterator<string, SplFileInfo, \RecursiveDirectoryIterator> $parent
* @param RecursiveCachingIterator<string, SplFileInfo, \RecursiveDirectoryIterator> $caching
* @param RecursiveRegexIterator<string, SplFileInfo, \RecursiveDirectoryIterator> $regex
*/
public function doFoo($parent, $caching, $regex): void
{
foreach ($parent as $key => $value) {
assertType('string', $key);
assertType('SplFileInfo', $value);
}
foreach ($caching as $key => $value) {
assertType('string', $key);
assertType('SplFileInfo', $value);
}
foreach ($regex as $key => $value) {
assertType('string', $key);
assertType('SplFileInfo', $value);
}

assertType('ParentIterator<string, SplFileInfo, RecursiveDirectoryIterator>', $parent->getChildren());
assertType('RecursiveCachingIterator<string, SplFileInfo, RecursiveDirectoryIterator>', $caching->getChildren());
assertType('RecursiveRegexIterator<string, SplFileInfo, RecursiveDirectoryIterator>', $regex->getChildren());

assertType('Iterator<string, SplFileInfo>', $parent->getInnerIterator());
}

/**
* @param RecursiveArrayIterator<int, string> $it
*/
public function doBar($it): void
{
$caching = new RecursiveCachingIterator($it);
assertType('RecursiveCachingIterator<int, string, RecursiveArrayIterator<int, string>>', $caching);
assertType('string', $caching->current());
assertType('int', $caching->key());

$parent = new ParentIterator($it);
assertType('ParentIterator<int, string, RecursiveArrayIterator<int, string>>', $parent);

$regex = new RecursiveRegexIterator($it, '~foo~');
assertType('RecursiveRegexIterator<int, string, RecursiveArrayIterator<int, string>>', $regex);
assertType('string', $regex->current());
assertType('int', $regex->key());
}

}
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ public function testBug9615(): void
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/bug-9615.php'], [
[
'Return type mixed of method Bug9615\ExpectComplaintsHere::accept() is not covariant with tentative return type bool of method FilterIterator<mixed,mixed,Traversable<mixed, mixed>>::accept().',
'Return type mixed of method Bug9615\ExpectComplaintsHere::accept() is not covariant with tentative return type bool of method FilterIterator<mixed,mixed,RecursiveIterator<mixed, mixed>>::accept().',
19,
$tipText,
],
Expand Down
Loading