Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ parameters:
count: 5
path: src/Analyser/TypeSpecifier.php

-
rawMessage: 'Cannot call method getPathname() on SplFileInfo|string.'
identifier: method.nonObject
count: 1
path: src/Cache/FileCacheStorage.php
Comment on lines +123 to +127
Copy link
Copy Markdown
Contributor

@staabm staabm May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not baseline this error but add a explicit check into FileCacheStorage to make sure we are working on getPathname().

my understanding is, that we get this error now (and did not before), because we work with
string|SplFileInfo after this PR, but had mixed before.


-
rawMessage: 'Template type TNodeType is declared as covariant, but occurs in contravariant position in parameter node of method PHPStan\Collectors\Collector::processNode().'
identifier: generics.variance
Expand Down
47 changes: 33 additions & 14 deletions stubs/iterable.stub
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,10 @@ class ArrayIterator implements SeekableIterator, ArrayAccess, Countable
}

/**
* @template T of \RecursiveIterator|\IteratorAggregate
* @mixin T
* @implements SeekableIterator<string, DirectoryIterator>
*/
class RecursiveIteratorIterator
class DirectoryIterator extends SplFileInfo implements SeekableIterator
{
/**
* @param T $iterator
*/
public function __construct(
$iterator,
int $mode = RecursiveIteratorIterator::LEAVES_ONLY,
int $flags = 0
)
{

}

}

Expand Down Expand Up @@ -207,6 +195,29 @@ class IteratorIterator implements OuterIterator {
public function __construct(Traversable $iterator) {}
}

/**
* @template TIterator of \RecursiveIterator|\IteratorAggregate
*
* @implements OuterIterator<key-of<TIterator>, value-of<TIterator>>
*
* @mixin TIterator
*/
class RecursiveIteratorIterator implements OuterIterator
{
/**
* @param TIterator $iterator
*/
public function __construct(
$iterator,
int $mode = RecursiveIteratorIterator::LEAVES_ONLY,
int $flags = 0
)
{

}

}

/**
* @template-covariant TKey
* @template-covariant TValue
Expand Down Expand Up @@ -289,6 +300,14 @@ class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
public function uksort($cmp_function) { }
}

/**
* @implements RecursiveIterator<string, SplFileInfo|string>
*/
class RecursiveDirectoryIterator extends FilesystemIterator implements RecursiveIterator
Comment on lines +312 to +315
Copy link
Copy Markdown
Contributor

@staabm staabm May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at the above baseline.neon comment, I realized it might be a nice improvement to infer the RecursiveDirectoryIterator generics based on __construct $flags parameter.

if we could do this, we would not have the baseline entry (this might also be stuff for a future PR)

might be doable for more iterators which work with FilesystemIterator::* constants

{

}

/**
* @template TKey
* @template TValue
Expand Down
36 changes: 36 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-8435.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);

namespace Bug8435;

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use function PHPStan\Testing\assertType;

class HelloWorld
{
public function sayHello1(string $path): void
{
$iterator = new RecursiveDirectoryIterator($path);
foreach ($iterator as $fileinfo) {
assertType('SplFileInfo|string', $fileinfo);
}
}

public function sayHello2(string $path): void
{
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
foreach ($iterator as $fileinfo) {
assertType('SplFileInfo|string', $fileinfo);
}
}

/**
* @param RecursiveIteratorIterator<RecursiveDirectoryIterator> $iterator
*/
public function test(RecursiveIteratorIterator $iterator): void
{
foreach ($iterator as $fileinfo) {
assertType('SplFileInfo|string', $fileinfo);
}
}
}
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Classes/InstantiationRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ public function testBug3425(): void
{
$this->analyse([__DIR__ . '/data/bug-3425.php'], [
[
'Parameter #1 $iterator of class RecursiveIteratorIterator constructor expects T of IteratorAggregate|RecursiveIterator, Generator<int, int, mixed, void> given.',
'Parameter #1 $iterator of class RecursiveIteratorIterator constructor expects TIterator of IteratorAggregate|RecursiveIterator, Generator<int, int, mixed, void> given.',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid this change by not renaming the template from T to TIterator

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Fixed.

5,
],
]);
Expand Down
Loading