From 01014255ad1205ca5f70f6e62094047db14242c5 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 14:58:18 +0100 Subject: [PATCH 1/8] SplObjectStorage iterates over objects --- tests/PHPStan/Analyser/nsrt/bug-13985.php | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-13985.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-13985.php b/tests/PHPStan/Analyser/nsrt/bug-13985.php new file mode 100644 index 0000000000..0224007d36 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13985.php @@ -0,0 +1,32 @@ + $value) { + assertType('int', $key); + assertType('object', $value); + } + } +} + +class X {} + +/** + * @param SplObjectStorage $splObjectStorage + * @return void + */ +function genericExample(SplObjectStorage $splObjectStorage): void +{ + foreach ($splObjectStorage as $key => $value) { + assertType('int', $key); + assertType('Bug13985\X', $value); + } + assertType('int', $splObjectStorage->getInfo()); + +} From e4d1433a3c50ef3100e4c76a10053baa27fa020d Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 15:08:50 +0100 Subject: [PATCH 2/8] Revert "Cache ClassReflections" reverts https://github.com/phpstan/phpstan-src/commit/4a143bdda18c535d66ea5afbcea885c7cce63abd --- src/Type/ObjectType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 3e755bab60..ca75971914 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,10 +1746,10 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { - return $this->classReflection = $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); + return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } - return $this->classReflection = $classReflection; + return $classReflection; } public function getAncestorWithClassName(string $className): ?self From 9d04de3d0181b1208b6660f360379d962e80476a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 15:16:46 +0100 Subject: [PATCH 3/8] Update ObjectType.php --- src/Type/ObjectType.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index ca75971914..3e2960a67c 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,10 +1746,11 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { + // withTypes creates a new object, don't cache it in $this->classReflection return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } - return $classReflection; + return $this->classReflection = $classReflection; } public function getAncestorWithClassName(string $className): ?self From d33406222d610ffee4fa6705f9814036f65698f4 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 15:16:54 +0100 Subject: [PATCH 4/8] Update ObjectType.php --- src/Type/ObjectType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 3e2960a67c..0e7626426d 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,7 +1746,7 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { - // withTypes creates a new object, don't cache it in $this->classReflection + // withTypes() creates a new object, don't cache it in $this->classReflection return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } From 50d5ff19f92364c21d74e6b954ea34893485a9c1 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 19:13:46 +0100 Subject: [PATCH 5/8] Update ObjectType.php --- src/Type/ObjectType.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 0e7626426d..b69fd0e5d1 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,7 +1746,6 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { - // withTypes() creates a new object, don't cache it in $this->classReflection return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } From 759c1c419f3ba8a8e9224e3031b6dd5b814c4a73 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 2 Feb 2026 17:00:26 +0100 Subject: [PATCH 6/8] Create bug-4789.php --- tests/PHPStan/Analyser/nsrt/bug-4789.php | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-4789.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-4789.php b/tests/PHPStan/Analyser/nsrt/bug-4789.php new file mode 100644 index 0000000000..ae4aa20bb6 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-4789.php @@ -0,0 +1,11 @@ + Date: Mon, 2 Feb 2026 17:08:33 +0100 Subject: [PATCH 7/8] Update bug-4789.php --- tests/PHPStan/Analyser/nsrt/bug-4789.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/nsrt/bug-4789.php b/tests/PHPStan/Analyser/nsrt/bug-4789.php index ae4aa20bb6..4f47b1859d 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-4789.php +++ b/tests/PHPStan/Analyser/nsrt/bug-4789.php @@ -1,4 +1,4 @@ -= 8.0 namespace Bug4789; From b32a638e0166c9b24527ede4c3a0cbb54caf68f7 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 5 Feb 2026 10:09:13 +0100 Subject: [PATCH 8/8] Update ObjectType.php --- src/Type/ObjectType.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index b69fd0e5d1..85fcc4bf05 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1735,21 +1735,20 @@ public function getNakedClassReflection(): ?ClassReflection public function getClassReflection(): ?ClassReflection { - if ($this->classReflection !== null) { - return $this->classReflection; - } + if ($this->classReflection === null) { + $reflectionProvider = ReflectionProviderStaticAccessor::getInstance(); + if (!$reflectionProvider->hasClass($this->className)) { + return null; + } - $reflectionProvider = ReflectionProviderStaticAccessor::getInstance(); - if (!$reflectionProvider->hasClass($this->className)) { - return null; + $this->classReflection = $reflectionProvider->getClass($this->className); } - $classReflection = $reflectionProvider->getClass($this->className); - if ($classReflection->isGeneric()) { - return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); + if ($this->classReflection->isGeneric()) { + return $this->classReflection->withTypes(array_values($this->classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } - return $this->classReflection = $classReflection; + return $this->classReflection; } public function getAncestorWithClassName(string $className): ?self