From 1b5440707f5850268be76940001568274ea83cf4 Mon Sep 17 00:00:00 2001 From: staabm <120441+staabm@users.noreply.github.com> Date: Mon, 22 Jun 2026 12:38:22 +0000 Subject: [PATCH 1/6] Re-register the runtime container in `setUp()` of type tests with PHP-version-dependent reflection assertions - Add `setUp()` to `HasPropertyTypeTest`, `ObjectTypeTest`, `TypeCombinatorTest` and `GenericObjectTypeTest` that calls `self::getContainer()`. These tests call `Type` methods directly (without going through the analyser), so they resolve reflection through the global `ReflectionProviderStaticAccessor`. Another test can leave a container configured with a different `PhpVersion` registered there; re-registering the default (runtime) container before each test makes the version-dependent assertions deterministic, independent of test ordering or the test runner (the existing PHPUnit event subscriber does not fire reliably under paratest). - Add a deterministic regression test `testIsSuperTypeOfClosureRespectsActivePhpVersion` that exercises both sides of the dynamic-properties boundary (PHP 8.1 -> `Maybe`, PHP 8.2 -> `No`) by building containers with explicit `PhpVersion`s, and restores the default container afterwards so it never leaks global state. - The flaky `hasProperty(foo) -> isSuperTypeOf(Closure)` assertion was caused by the result of `ObjectType::hasInstanceProperty()` for a final class depending on `ClassReflection::allowsDynamicProperties()`, which is version-gated, while the expected value was derived from `PHP_VERSION_ID`; the two diverged when the active container's `PhpVersion` did not match the runtime. - Probed `HasMethodTypeTest`: its `Closure` case is `No` regardless of version (method existence is not affected by dynamic properties), so it is not affected. --- .../Type/Accessory/HasPropertyTypeTest.php | 62 +++++++++++++++++++ .../Type/Generic/GenericObjectTypeTest.php | 13 ++++ tests/PHPStan/Type/ObjectTypeTest.php | 13 ++++ tests/PHPStan/Type/TypeCombinatorTest.php | 12 ++++ 4 files changed, 100 insertions(+) diff --git a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php index 8219bc5eab3..76a41e2586e 100644 --- a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php +++ b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php @@ -4,6 +4,9 @@ use Closure; use DateInterval; +use Override; +use PHPStan\DependencyInjection\ContainerFactory; +use PHPStan\File\FileHelper; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\CallableType; @@ -17,12 +20,25 @@ use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use PHPUnit\Framework\Attributes\DataProvider; +use function file_put_contents; use function sprintf; +use function sys_get_temp_dir; use const PHP_VERSION_ID; class HasPropertyTypeTest extends PHPStanTestCase { + #[Override] + protected function setUp(): void + { + // Re-register the default (runtime) container as the global static reflection + // provider before every test. Without this, a container configured with a + // different PhpVersion - registered by another test (e.g. through its data + // provider) - can leak in and make the version-dependent Closure data set + // below flaky. See https://github.com/phpstan/phpstan/issues/14860 + self::getContainer(); + } + public static function dataIsSuperTypeOf(): array { return [ @@ -108,6 +124,31 @@ public static function dataIsSuperTypeOf(): array ]; } + public function testIsSuperTypeOfClosureRespectsActivePhpVersion(): void + { + $type = new HasPropertyType('foo'); + + // Whether a final class without the property is a possible subtype of + // hasProperty() depends on whether the PHP version still allows dynamic + // properties, so the result must follow the active container's PhpVersion + // rather than the global PHP_VERSION_ID constant. This coupling is what made + // the Closure data set above flaky when another test left a container + // configured with a different PhpVersion registered as the global static + // reflection provider. See https://github.com/phpstan/phpstan/issues/14860 + // A fresh ObjectType is used each time because it caches its ClassReflection. + try { + self::registerContainerWithPhpVersion(80100); + $this->assertSame('Maybe', $type->isSuperTypeOf(new ObjectType(Closure::class))->describe()); + + self::registerContainerWithPhpVersion(80200); + $this->assertSame('No', $type->isSuperTypeOf(new ObjectType(Closure::class))->describe()); + } finally { + // Restore the default (runtime) container so this test does not leak its + // foreign container into other tests. + self::getContainer(); + } + } + #[DataProvider('dataIsSuperTypeOf')] public function testIsSuperTypeOf(HasPropertyType $type, Type $otherType, TrinaryLogic $expectedResult): void { @@ -174,4 +215,25 @@ public function testIsSubTypeOfInversed(HasPropertyType $type, Type $otherType, ); } + private static function registerContainerWithPhpVersion(int $versionId): void + { + $fileHelper = new FileHelper(__DIR__ . '/../../../..'); + $rootDir = $fileHelper->normalizePath(__DIR__ . '/../../../..', '/'); + + // The directory is already created by self::getContainer() (called in setUp()). + $tmpDir = sys_get_temp_dir() . '/phpstan-tests'; + + $configFile = $tmpDir . '/has-property-php-version-' . $versionId . '.neon'; + file_put_contents($configFile, sprintf("parameters:\n\tphpVersion: %d\n", $versionId)); + + $containerFactory = new ContainerFactory($rootDir); + $container = $containerFactory->create($tmpDir, [ + $containerFactory->getConfigDirectory() . '/config.level8.neon', + __DIR__ . '/../../../../src/Testing/TestCase.neon', + $configFile, + ], []); + + ContainerFactory::postInitializeContainer($container); + } + } diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index 2f3733c363b..05722806c55 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -6,6 +6,7 @@ use DateTimeInterface; use Exception; use Iterator; +use Override; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\IntegerType; @@ -32,6 +33,18 @@ class GenericObjectTypeTest extends PHPStanTestCase { + #[Override] + protected function setUp(): void + { + // Re-register the default (runtime) container as the global static reflection + // provider before every test. Without this, a container configured with a + // different PhpVersion - registered by another test - can leak in and make the + // version-dependent data sets (which depend on the reflected, version-specific + // variance of built-in generics) flaky. + // See https://github.com/phpstan/phpstan/issues/14860 + self::getContainer(); + } + public static function dataIsSuperTypeOf(): array { return [ diff --git a/tests/PHPStan/Type/ObjectTypeTest.php b/tests/PHPStan/Type/ObjectTypeTest.php index 3a0344983ba..3c8e43c22e0 100644 --- a/tests/PHPStan/Type/ObjectTypeTest.php +++ b/tests/PHPStan/Type/ObjectTypeTest.php @@ -23,6 +23,7 @@ use Iterator; use LogicException; use ObjectTypeEnums\FooEnum; +use Override; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\HasMethodType; @@ -51,6 +52,18 @@ class ObjectTypeTest extends PHPStanTestCase { + #[Override] + protected function setUp(): void + { + // Re-register the default (runtime) container as the global static reflection + // provider before every test. Without this, a container configured with a + // different PhpVersion - registered by another test - can leak in and make the + // version-dependent Closure data sets (which depend on whether dynamic + // properties are still allowed) flaky. + // See https://github.com/phpstan/phpstan/issues/14860 + self::getContainer(); + } + public static function dataIsIterable(): array { return [ diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index 996beeeed75..009bacbf39f 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -14,6 +14,7 @@ use InvalidArgumentException; use Iterator; use ObjectShapesAcceptance\ClassWithFooIntProperty; +use Override; use PHPStan\DependencyInjection\BleedingEdgeToggle; use PHPStan\Fixture\FinalClass; use PHPStan\Generics\FunctionsAssertType\C; @@ -73,6 +74,17 @@ class TypeCombinatorTest extends PHPStanTestCase { + #[Override] + protected function setUp(): void + { + // Re-register the default (runtime) container as the global static reflection + // provider before every test. Without this, a container configured with a + // different PhpVersion - registered by another test - can leak in and make the + // version-dependent data sets (e.g. dynamic-property handling of final classes) + // flaky. See https://github.com/phpstan/phpstan/issues/14860 + self::getContainer(); + } + public static function dataAddNull(): array { return [ From 92cdee6470a295f43bf243ea4431c631f055fa4e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 23 Jun 2026 06:57:00 +0200 Subject: [PATCH 2/6] cleanup --- .../Type/Accessory/HasPropertyTypeTest.php | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php index 76a41e2586e..61e11f671f8 100644 --- a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php +++ b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php @@ -124,31 +124,6 @@ public static function dataIsSuperTypeOf(): array ]; } - public function testIsSuperTypeOfClosureRespectsActivePhpVersion(): void - { - $type = new HasPropertyType('foo'); - - // Whether a final class without the property is a possible subtype of - // hasProperty() depends on whether the PHP version still allows dynamic - // properties, so the result must follow the active container's PhpVersion - // rather than the global PHP_VERSION_ID constant. This coupling is what made - // the Closure data set above flaky when another test left a container - // configured with a different PhpVersion registered as the global static - // reflection provider. See https://github.com/phpstan/phpstan/issues/14860 - // A fresh ObjectType is used each time because it caches its ClassReflection. - try { - self::registerContainerWithPhpVersion(80100); - $this->assertSame('Maybe', $type->isSuperTypeOf(new ObjectType(Closure::class))->describe()); - - self::registerContainerWithPhpVersion(80200); - $this->assertSame('No', $type->isSuperTypeOf(new ObjectType(Closure::class))->describe()); - } finally { - // Restore the default (runtime) container so this test does not leak its - // foreign container into other tests. - self::getContainer(); - } - } - #[DataProvider('dataIsSuperTypeOf')] public function testIsSuperTypeOf(HasPropertyType $type, Type $otherType, TrinaryLogic $expectedResult): void { @@ -215,25 +190,4 @@ public function testIsSubTypeOfInversed(HasPropertyType $type, Type $otherType, ); } - private static function registerContainerWithPhpVersion(int $versionId): void - { - $fileHelper = new FileHelper(__DIR__ . '/../../../..'); - $rootDir = $fileHelper->normalizePath(__DIR__ . '/../../../..', '/'); - - // The directory is already created by self::getContainer() (called in setUp()). - $tmpDir = sys_get_temp_dir() . '/phpstan-tests'; - - $configFile = $tmpDir . '/has-property-php-version-' . $versionId . '.neon'; - file_put_contents($configFile, sprintf("parameters:\n\tphpVersion: %d\n", $versionId)); - - $containerFactory = new ContainerFactory($rootDir); - $container = $containerFactory->create($tmpDir, [ - $containerFactory->getConfigDirectory() . '/config.level8.neon', - __DIR__ . '/../../../../src/Testing/TestCase.neon', - $configFile, - ], []); - - ContainerFactory::postInitializeContainer($container); - } - } From 375fa8b437511acc5aee8b89e2051974d9a0bb20 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 23 Jun 2026 07:07:53 +0200 Subject: [PATCH 3/6] cs --- tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php index 61e11f671f8..c6153e6b5ca 100644 --- a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php +++ b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php @@ -5,8 +5,6 @@ use Closure; use DateInterval; use Override; -use PHPStan\DependencyInjection\ContainerFactory; -use PHPStan\File\FileHelper; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\CallableType; @@ -20,9 +18,7 @@ use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use PHPUnit\Framework\Attributes\DataProvider; -use function file_put_contents; use function sprintf; -use function sys_get_temp_dir; use const PHP_VERSION_ID; class HasPropertyTypeTest extends PHPStanTestCase From 38dd3818739f60c8b8e225af43cb07ed61ec02b8 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Tue, 23 Jun 2026 05:25:20 +0000 Subject: [PATCH 4/6] Shorten setUp() comments in type unit tests Co-Authored-By: Claude Opus 4.8 --- tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php | 8 +++----- tests/PHPStan/Type/Generic/GenericObjectTypeTest.php | 9 +++------ tests/PHPStan/Type/ObjectTypeTest.php | 9 +++------ tests/PHPStan/Type/TypeCombinatorTest.php | 8 +++----- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php index c6153e6b5ca..43dcec21ae8 100644 --- a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php +++ b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php @@ -27,11 +27,9 @@ class HasPropertyTypeTest extends PHPStanTestCase #[Override] protected function setUp(): void { - // Re-register the default (runtime) container as the global static reflection - // provider before every test. Without this, a container configured with a - // different PhpVersion - registered by another test (e.g. through its data - // provider) - can leak in and make the version-dependent Closure data set - // below flaky. See https://github.com/phpstan/phpstan/issues/14860 + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent Closure data set below. + // See https://github.com/phpstan/phpstan/issues/14860 self::getContainer(); } diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index 05722806c55..73a6aae1170 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -36,12 +36,9 @@ class GenericObjectTypeTest extends PHPStanTestCase #[Override] protected function setUp(): void { - // Re-register the default (runtime) container as the global static reflection - // provider before every test. Without this, a container configured with a - // different PhpVersion - registered by another test - can leak in and make the - // version-dependent data sets (which depend on the reflected, version-specific - // variance of built-in generics) flaky. - // See https://github.com/phpstan/phpstan/issues/14860 + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent data sets (reflected variance of built-in + // generics). See https://github.com/phpstan/phpstan/issues/14860 self::getContainer(); } diff --git a/tests/PHPStan/Type/ObjectTypeTest.php b/tests/PHPStan/Type/ObjectTypeTest.php index 3c8e43c22e0..73b587d4de7 100644 --- a/tests/PHPStan/Type/ObjectTypeTest.php +++ b/tests/PHPStan/Type/ObjectTypeTest.php @@ -55,12 +55,9 @@ class ObjectTypeTest extends PHPStanTestCase #[Override] protected function setUp(): void { - // Re-register the default (runtime) container as the global static reflection - // provider before every test. Without this, a container configured with a - // different PhpVersion - registered by another test - can leak in and make the - // version-dependent Closure data sets (which depend on whether dynamic - // properties are still allowed) flaky. - // See https://github.com/phpstan/phpstan/issues/14860 + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent Closure data sets (dynamic-property + // handling). See https://github.com/phpstan/phpstan/issues/14860 self::getContainer(); } diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index 009bacbf39f..86ec7b35012 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -77,11 +77,9 @@ class TypeCombinatorTest extends PHPStanTestCase #[Override] protected function setUp(): void { - // Re-register the default (runtime) container as the global static reflection - // provider before every test. Without this, a container configured with a - // different PhpVersion - registered by another test - can leak in and make the - // version-dependent data sets (e.g. dynamic-property handling of final classes) - // flaky. See https://github.com/phpstan/phpstan/issues/14860 + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent data sets (dynamic-property handling of + // final classes). See https://github.com/phpstan/phpstan/issues/14860 self::getContainer(); } From 4f03332fa9b03a41efbd82107f59c962c35543e3 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Tue, 23 Jun 2026 14:21:13 +0000 Subject: [PATCH 5/6] Move container re-registration into PHPStanTestCase behind an opt-in flag Replace the per-class setUp() duplication with a $reinitializeContainerBeforeEachTest flag (default false) on PHPStanTestCase. Tests with PHP-version-dependent reflection assertions opt in by setting it to true, so the runtime container is pinned before each test without each class re-implementing setUp(). Co-Authored-By: Claude Opus 4.8 --- src/Testing/PHPStanTestCase.php | 19 +++++++++++++++++++ .../Type/Accessory/HasPropertyTypeTest.php | 13 ++++--------- .../Type/Generic/GenericObjectTypeTest.php | 13 ++++--------- tests/PHPStan/Type/ObjectTypeTest.php | 13 ++++--------- tests/PHPStan/Type/TypeCombinatorTest.php | 13 ++++--------- 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/Testing/PHPStanTestCase.php b/src/Testing/PHPStanTestCase.php index 03b2e4335c7..f70ec82fcff 100644 --- a/src/Testing/PHPStanTestCase.php +++ b/src/Testing/PHPStanTestCase.php @@ -2,6 +2,7 @@ namespace PHPStan\Testing; +use Override; use PHPStan\Analyser\ConstantResolver; use PHPStan\Analyser\DirectInternalScopeFactoryFactory; use PHPStan\Analyser\Error; @@ -40,6 +41,24 @@ abstract class PHPStanTestCase extends TestCase use PHPStanTestCaseTrait; + /** + * Re-register the runtime container as the global static reflection provider before + * every test. Enable this in tests that construct Type objects directly and assert + * PHP-version-dependent reflection results, so a foreign PhpVersion leaked by another + * test can't flake them. See https://github.com/phpstan/phpstan/issues/14860 + */ + protected bool $reinitializeContainerBeforeEachTest = false; + + #[Override] + protected function setUp(): void + { + if (!$this->reinitializeContainerBeforeEachTest) { + return; + } + + self::getContainer(); + } + public static function getParser(): Parser { /** @var Parser $parser */ diff --git a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php index 43dcec21ae8..5771e7d52da 100644 --- a/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php +++ b/tests/PHPStan/Type/Accessory/HasPropertyTypeTest.php @@ -4,7 +4,6 @@ use Closure; use DateInterval; -use Override; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\CallableType; @@ -24,14 +23,10 @@ class HasPropertyTypeTest extends PHPStanTestCase { - #[Override] - protected function setUp(): void - { - // Pin the runtime container so a foreign PhpVersion leaked by another test - // can't flake the version-dependent Closure data set below. - // See https://github.com/phpstan/phpstan/issues/14860 - self::getContainer(); - } + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent Closure data set below. + // See https://github.com/phpstan/phpstan/issues/14860 + protected bool $reinitializeContainerBeforeEachTest = true; public static function dataIsSuperTypeOf(): array { diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index 73a6aae1170..7564cbe9f33 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -6,7 +6,6 @@ use DateTimeInterface; use Exception; use Iterator; -use Override; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\IntegerType; @@ -33,14 +32,10 @@ class GenericObjectTypeTest extends PHPStanTestCase { - #[Override] - protected function setUp(): void - { - // Pin the runtime container so a foreign PhpVersion leaked by another test - // can't flake the version-dependent data sets (reflected variance of built-in - // generics). See https://github.com/phpstan/phpstan/issues/14860 - self::getContainer(); - } + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent data sets (reflected variance of built-in + // generics). See https://github.com/phpstan/phpstan/issues/14860 + protected bool $reinitializeContainerBeforeEachTest = true; public static function dataIsSuperTypeOf(): array { diff --git a/tests/PHPStan/Type/ObjectTypeTest.php b/tests/PHPStan/Type/ObjectTypeTest.php index 73b587d4de7..c9a9688c215 100644 --- a/tests/PHPStan/Type/ObjectTypeTest.php +++ b/tests/PHPStan/Type/ObjectTypeTest.php @@ -23,7 +23,6 @@ use Iterator; use LogicException; use ObjectTypeEnums\FooEnum; -use Override; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\HasMethodType; @@ -52,14 +51,10 @@ class ObjectTypeTest extends PHPStanTestCase { - #[Override] - protected function setUp(): void - { - // Pin the runtime container so a foreign PhpVersion leaked by another test - // can't flake the version-dependent Closure data sets (dynamic-property - // handling). See https://github.com/phpstan/phpstan/issues/14860 - self::getContainer(); - } + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent Closure data sets (dynamic-property + // handling). See https://github.com/phpstan/phpstan/issues/14860 + protected bool $reinitializeContainerBeforeEachTest = true; public static function dataIsIterable(): array { diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index 86ec7b35012..c5499d47447 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -14,7 +14,6 @@ use InvalidArgumentException; use Iterator; use ObjectShapesAcceptance\ClassWithFooIntProperty; -use Override; use PHPStan\DependencyInjection\BleedingEdgeToggle; use PHPStan\Fixture\FinalClass; use PHPStan\Generics\FunctionsAssertType\C; @@ -74,14 +73,10 @@ class TypeCombinatorTest extends PHPStanTestCase { - #[Override] - protected function setUp(): void - { - // Pin the runtime container so a foreign PhpVersion leaked by another test - // can't flake the version-dependent data sets (dynamic-property handling of - // final classes). See https://github.com/phpstan/phpstan/issues/14860 - self::getContainer(); - } + // Pin the runtime container so a foreign PhpVersion leaked by another test + // can't flake the version-dependent data sets (dynamic-property handling of + // final classes). See https://github.com/phpstan/phpstan/issues/14860 + protected bool $reinitializeContainerBeforeEachTest = true; public static function dataAddNull(): array { From b8e8ddf8c49c67329e8af39a7ac47cfc702326fd Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Tue, 23 Jun 2026 14:21:18 +0000 Subject: [PATCH 6/6] Call parent::setUp() in subclasses overriding setUp() PHPStanTestCase now defines setUp(), so subclasses that override it must call parent::setUp() to run the (opt-in) container re-registration. Co-Authored-By: Claude Opus 4.8 --- tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php | 1 + tests/PHPStan/Analyser/TypeSpecifierTest.php | 1 + tests/PHPStan/Command/ErrorFormatter/RawErrorFormatterTest.php | 1 + tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php | 1 + tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php | 1 + tests/PHPStan/Rules/Functions/PrintfHelperTest.php | 1 + .../PHPStan/Type/Php/GmpOperatorTypeSpecifyingExtensionTest.php | 1 + 7 files changed, 7 insertions(+) diff --git a/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php index 4680f7fccb2..eb5fbc4d67a 100644 --- a/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php @@ -22,6 +22,7 @@ class AnalyserTraitsIntegrationTest extends PHPStanTestCase #[Override] protected function setUp(): void { + parent::setUp(); $this->fileHelper = self::getContainer()->getByType(FileHelper::class); } diff --git a/tests/PHPStan/Analyser/TypeSpecifierTest.php b/tests/PHPStan/Analyser/TypeSpecifierTest.php index aa66c940016..faeacb2f9fc 100644 --- a/tests/PHPStan/Analyser/TypeSpecifierTest.php +++ b/tests/PHPStan/Analyser/TypeSpecifierTest.php @@ -61,6 +61,7 @@ class TypeSpecifierTest extends PHPStanTestCase #[Override] protected function setUp(): void { + parent::setUp(); $reflectionProvider = self::createReflectionProvider(); $this->printer = new Printer(); $this->typeSpecifier = self::getContainer()->getService('typeSpecifier'); diff --git a/tests/PHPStan/Command/ErrorFormatter/RawErrorFormatterTest.php b/tests/PHPStan/Command/ErrorFormatter/RawErrorFormatterTest.php index cf0c0711631..72718f0a28a 100644 --- a/tests/PHPStan/Command/ErrorFormatter/RawErrorFormatterTest.php +++ b/tests/PHPStan/Command/ErrorFormatter/RawErrorFormatterTest.php @@ -19,6 +19,7 @@ class RawErrorFormatterTest extends ErrorFormatterTestCase #[Override] protected function setUp(): void { + parent::setUp(); foreach (AgentDetector::ENV_VARS as $var) { $this->originalEnvVars[$var] = getenv($var); putenv($var); diff --git a/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php b/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php index 2ec0679fabd..8396630f7c6 100644 --- a/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php +++ b/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php @@ -23,6 +23,7 @@ class TableErrorFormatterTest extends ErrorFormatterTestCase #[Override] protected function setUp(): void { + parent::setUp(); putenv('GITHUB_ACTIONS'); $this->terminalEmulator = getenv('TERMINAL_EMULATOR'); diff --git a/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php b/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php index a1e0faf4a2a..eef6033cfdd 100644 --- a/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php +++ b/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php @@ -17,6 +17,7 @@ class DefaultStubFilesProviderTest extends PHPStanTestCase #[Override] protected function setUp(): void { + parent::setUp(); $this->currentWorkingDirectory = $this->getContainer()->getParameter('currentWorkingDirectory'); } diff --git a/tests/PHPStan/Rules/Functions/PrintfHelperTest.php b/tests/PHPStan/Rules/Functions/PrintfHelperTest.php index c8175508082..c2dae58ec4e 100644 --- a/tests/PHPStan/Rules/Functions/PrintfHelperTest.php +++ b/tests/PHPStan/Rules/Functions/PrintfHelperTest.php @@ -15,6 +15,7 @@ class PrintfHelperTest extends PHPStanTestCase #[Override] protected function setUp(): void { + parent::setUp(); $this->printf = new PrintfHelper(new PhpVersion(PHP_VERSION_ID)); } diff --git a/tests/PHPStan/Type/Php/GmpOperatorTypeSpecifyingExtensionTest.php b/tests/PHPStan/Type/Php/GmpOperatorTypeSpecifyingExtensionTest.php index 97c50184e0f..9e8aee6d8b2 100644 --- a/tests/PHPStan/Type/Php/GmpOperatorTypeSpecifyingExtensionTest.php +++ b/tests/PHPStan/Type/Php/GmpOperatorTypeSpecifyingExtensionTest.php @@ -24,6 +24,7 @@ class GmpOperatorTypeSpecifyingExtensionTest extends PHPStanTestCase #[Override] protected function setUp(): void { + parent::setUp(); $this->extension = new GmpOperatorTypeSpecifyingExtension(); }