From 255f02e673a27c971bd0ac55b27f83a81d940306 Mon Sep 17 00:00:00 2001 From: asumikam Date: Tue, 24 Feb 2026 22:43:41 +0900 Subject: [PATCH] feat: add IntersectionType support to PropertyCreateMockToCreateStubRector --- .../Fixture/intersection_type.php.inc | 55 +++++++++++++++++++ .../Fixture/skip_intersection_type.php.inc | 26 +++++++++ .../PropertyCreateMockToCreateStubRector.php | 21 ++++++- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/intersection_type.php.inc create mode 100644 rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/skip_intersection_type.php.inc diff --git a/rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/intersection_type.php.inc b/rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/intersection_type.php.inc new file mode 100644 index 00000000..69f53ae9 --- /dev/null +++ b/rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/intersection_type.php.inc @@ -0,0 +1,55 @@ +someMock = $this->createMock(\stdClass::class); + } + + public function testThis() + { + $this->assertSame('...', $this->someMock); + } + + public function testThat() + { + $this->assertSame('...', $this->someMock); + } +} + +?> +----- +someMock = $this->createStub(\stdClass::class); + } + + public function testThis() + { + $this->assertSame('...', $this->someMock); + } + + public function testThat() + { + $this->assertSame('...', $this->someMock); + } +} + +?> diff --git a/rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/skip_intersection_type.php.inc b/rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/skip_intersection_type.php.inc new file mode 100644 index 00000000..517a507c --- /dev/null +++ b/rules-tests/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector/Fixture/skip_intersection_type.php.inc @@ -0,0 +1,26 @@ +someMock = $this->createMock(\stdClass::class); + } + + public function testThis() + { + $this->someMock->expects($this->atLeastOnce())->method('something'); + $this->assertSame('...', $this->someMock); + } + + public function testThat() + { + $this->assertSame('...', $this->someMock); + } +} diff --git a/rules/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector.php b/rules/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector.php index 78e4af34..1033bcf2 100644 --- a/rules/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector.php +++ b/rules/PHPUnit120/Rector/Class_/PropertyCreateMockToCreateStubRector.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; @@ -71,7 +72,7 @@ public function refactor(Node $node): ?Class_ // update property type $property = $node->getProperty($propertyName); /** @var Property $property */ - $property->type = new FullyQualified(PHPUnitClassName::STUB); + $property->type = $this->updatePropertyType($property->type); } if (! $hasChanged) { @@ -150,4 +151,22 @@ private function shouldSkipClass(Class_ $class): bool // the setup class method must be here, so we have a place where the createMock() is used return ! $setUpClassMethod instanceof ClassMethod; } + + private function updatePropertyType(?Node $type): IntersectionType|FullyQualified + { + if ($type instanceof IntersectionType) { + $newTypes = []; + foreach ($type->types as $innerType) { + if ($innerType instanceof FullyQualified && $innerType->toString() === PHPUnitClassName::MOCK_OBJECT) { + $newTypes[] = new FullyQualified(PHPUnitClassName::STUB); + } else { + $newTypes[] = $innerType; + } + } + + return new IntersectionType($newTypes); + } + + return new FullyQualified(PHPUnitClassName::STUB); + } }