From e71f7f5c7e5393f0919b798f2ce4c32f4749ceb2 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 c5f142a5fcca9ee2c93f40f7f259203ccf8252ba 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 61c7b08520084e0d4a66773dba490132ae3466bf 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 9fb14dda00d47453b18022956097aa02e9948c8c 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 5622691c3a76858af96232c02ffab78b21473cec 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 0db4b3c1a926006d5f210edf7a78df96db685b71 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 31c6841bbe710bc75ba810b29055a2e4f3534f4d 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