From e27a7657f69a10ac2406b4c73040045ed99f94da Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 24 Jan 2020 07:55:20 +0100 Subject: [PATCH 1/3] Revert "Provide the same functionality in simpler way with stubs" This reverts commit 846a5fa9d0737757891ccbceff2c63f9773e51c8. --- extension.neon | 10 ++- .../CreateMockDynamicReturnTypeExtension.php | 57 +++++++++++++ ...tMockBuilderDynamicReturnTypeExtension.php | 47 +++++++++++ .../MockBuilderDynamicReturnTypeExtension.php | 53 +++++++++--- src/Type/PHPUnit/MockBuilderType.php | 33 ++++++++ stubs/MockBuilder.stub | 29 ------- stubs/TestCase.stub | 80 ------------------- 7 files changed, 188 insertions(+), 121 deletions(-) create mode 100644 src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php create mode 100644 src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php create mode 100644 src/Type/PHPUnit/MockBuilderType.php delete mode 100644 stubs/MockBuilder.stub delete mode 100644 stubs/TestCase.stub diff --git a/extension.neon b/extension.neon index 3395ec1..b4794a7 100644 --- a/extension.neon +++ b/extension.neon @@ -5,9 +5,7 @@ parameters: - markTestIncomplete - markTestSkipped stubFiles: - - stubs/MockBuilder.stub - stubs/MockObject.stub - - stubs/TestCase.stub services: - @@ -26,6 +24,14 @@ services: class: PHPStan\Type\PHPUnit\Assert\AssertStaticMethodTypeSpecifyingExtension tags: - phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension + - + class: PHPStan\Type\PHPUnit\CreateMockDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: PHPStan\Type\PHPUnit\GetMockBuilderDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension - class: PHPStan\Type\PHPUnit\MockBuilderDynamicReturnTypeExtension tags: diff --git a/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php b/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php new file mode 100644 index 0000000..b68b010 --- /dev/null +++ b/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php @@ -0,0 +1,57 @@ + 0, + 'createConfiguredMock' => 0, + 'createPartialMock' => 0, + 'createTestProxy' => 0, + 'getMockForAbstractClass' => 0, + 'getMockFromWsdl' => 1, + ]; + + public function getClass(): string + { + return 'PHPUnit\Framework\TestCase'; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return array_key_exists($methodReflection->getName(), $this->methods); + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + $argumentIndex = $this->methods[$methodReflection->getName()]; + $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + if (!isset($methodCall->args[$argumentIndex])) { + return $parametersAcceptor->getReturnType(); + } + $argType = $scope->getType($methodCall->args[$argumentIndex]->value); + if (!$argType instanceof ConstantStringType) { + return $parametersAcceptor->getReturnType(); + } + + $class = $argType->getValue(); + + return TypeCombinator::intersect( + new ObjectType($class), + $parametersAcceptor->getReturnType() + ); + } + +} diff --git a/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php b/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php new file mode 100644 index 0000000..4c7dbe6 --- /dev/null +++ b/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php @@ -0,0 +1,47 @@ +getName() === 'getMockBuilder'; + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + $mockBuilderType = $parametersAcceptor->getReturnType(); + if (count($methodCall->args) === 0) { + return $mockBuilderType; + } + $argType = $scope->getType($methodCall->args[0]->value); + if (!$argType instanceof ConstantStringType) { + return $mockBuilderType; + } + + $class = $argType->getValue(); + + if (!$mockBuilderType instanceof TypeWithClassName) { + throw new \PHPStan\ShouldNotHappenException(); + } + + return new MockBuilderType($mockBuilderType, $class); + } + +} diff --git a/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php b/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php index d6ae40d..b75c69f 100644 --- a/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php @@ -4,34 +4,67 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; +use PHPStan\Broker\Broker; use PHPStan\Reflection\MethodReflection; +use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use PHPUnit\Framework\MockObject\MockBuilder; +use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeWithClassName; -class MockBuilderDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension +class MockBuilderDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension, \PHPStan\Reflection\BrokerAwareExtension { + /** @var \PHPStan\Broker\Broker */ + private $broker; + + public function setBroker(Broker $broker): void + { + $this->broker = $broker; + } + public function getClass(): string { - return MockBuilder::class; + $testCase = $this->broker->getClass('PHPUnit\Framework\TestCase'); + $mockBuilderType = ParametersAcceptorSelector::selectSingle( + $testCase->getNativeMethod('getMockBuilder')->getVariants() + )->getReturnType(); + if (!$mockBuilderType instanceof TypeWithClassName) { + throw new \PHPStan\ShouldNotHappenException(); + } + + return $mockBuilderType->getClassName(); } public function isMethodSupported(MethodReflection $methodReflection): bool { - return !in_array( + return true; + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + $calledOnType = $scope->getType($methodCall->var); + if (!in_array( $methodReflection->getName(), [ 'getMock', 'getMockForAbstractClass', - 'getMockForTrait', ], true - ); - } + )) { + return $calledOnType; + } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type - { - return $scope->getType($methodCall->var); + $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + + if (!$calledOnType instanceof MockBuilderType) { + return $parametersAcceptor->getReturnType(); + } + + return TypeCombinator::intersect( + new ObjectType($calledOnType->getMockedClass()), + $parametersAcceptor->getReturnType() + ); } } diff --git a/src/Type/PHPUnit/MockBuilderType.php b/src/Type/PHPUnit/MockBuilderType.php new file mode 100644 index 0000000..e47f16b --- /dev/null +++ b/src/Type/PHPUnit/MockBuilderType.php @@ -0,0 +1,33 @@ +getClassName()); + $this->mockedClass = $mockedClass; + } + + public function getMockedClass(): string + { + return $this->mockedClass; + } + + public function describe(VerbosityLevel $level): string + { + return sprintf('%s<%s>', parent::describe($level), $this->mockedClass); + } + +} diff --git a/stubs/MockBuilder.stub b/stubs/MockBuilder.stub deleted file mode 100644 index 1aaf138..0000000 --- a/stubs/MockBuilder.stub +++ /dev/null @@ -1,29 +0,0 @@ - $type - */ - public function __construct(TestCase $testCase, $type) {} - - /** - * @phpstan-return MockObject&TMockedClass - */ - public function getMock() {} - - /** - * @phpstan-return MockObject&TMockedClass - */ - public function getMockForAbstractClass() {} - -} diff --git a/stubs/TestCase.stub b/stubs/TestCase.stub deleted file mode 100644 index 6fe1e1f..0000000 --- a/stubs/TestCase.stub +++ /dev/null @@ -1,80 +0,0 @@ - $originalClassName - * @phpstan-return MockObject&T - */ - public function createStub($originalClassName) {} - - /** - * @template T - * @phpstan-param class-string $originalClassName - * @phpstan-return MockObject&T - */ - public function createMock($originalClassName) {} - - /** - * @template T - * @phpstan-param class-string $className - * @phpstan-return MockBuilder - */ - public function getMockBuilder(string $className) {} - - /** - * @template T - * @phpstan-param class-string $originalClassName - * @phpstan-return MockObject&T - */ - public function createConfiguredMock($originalClassName) {} - - /** - * @template T - * @phpstan-param class-string $originalClassName - * @phpstan-param string[] $methods - * @phpstan-return MockObject&T - */ - public function createPartialMock($originalClassName, array $methods) {} - - /** - * @template T - * @phpstan-param class-string $originalClassName - * @phpstan-return MockObject&T - */ - public function createTestProxy($originalClassName) {} - - /** - * @template T - * @phpstan-param class-string $originalClassName - * @phpstan-param mixed[] $arguments - * @phpstan-param string $mockClassName - * @phpstan-param bool $callOriginalConstructor - * @phpstan-param bool $callOriginalClone - * @phpstan-param bool $callAutoload - * @phpstan-param string[] $mockedMethods - * @phpstan-param bool $cloneArguments - * @phpstan-return MockObject&T - */ - protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false) {} - - /** - * @template T - * @phpstan-param string $wsdlFile - * @phpstan-param class-string $originalClassName - * @phpstan-param string $mockClassName - * @phpstan-param string[] $methods - * @phpstan-param bool $callOriginalConstructor - * @phpstan-param mixed[] $options - * @phpstan-return MockObject&T - */ - protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []) {} - -} From 29b543f8da16e488515c1b5dee5ff3a9b53b9154 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 7 Feb 2020 13:17:33 +0100 Subject: [PATCH 2/3] handle mocking multiple classes --- .../CreateMockDynamicReturnTypeExtension.php | 22 ++++-- ...tMockBuilderDynamicReturnTypeExtension.php | 21 +++-- .../MockBuilderDynamicReturnTypeExtension.php | 7 +- src/Type/PHPUnit/MockBuilderType.php | 17 ++-- .../Type/PHPUnit/CreateMockExtensionTest.php | 37 +++++++++ tests/Type/PHPUnit/ExtensionTestCase.php | 77 +++++++++++++++++++ .../PHPUnit/MockBuilderTypeExtensionTest.php | 37 +++++++++ tests/Type/PHPUnit/data/BarInterface.php | 9 +++ tests/Type/PHPUnit/data/FooInterface.php | 9 +++ tests/Type/PHPUnit/data/create-mock.php | 12 +++ tests/Type/PHPUnit/data/mock-builder.php | 10 +++ 11 files changed, 238 insertions(+), 20 deletions(-) create mode 100644 tests/Type/PHPUnit/CreateMockExtensionTest.php create mode 100644 tests/Type/PHPUnit/ExtensionTestCase.php create mode 100644 tests/Type/PHPUnit/MockBuilderTypeExtensionTest.php create mode 100644 tests/Type/PHPUnit/data/BarInterface.php create mode 100644 tests/Type/PHPUnit/data/FooInterface.php create mode 100644 tests/Type/PHPUnit/data/create-mock.php create mode 100644 tests/Type/PHPUnit/data/mock-builder.php diff --git a/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php b/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php index b68b010..295915e 100644 --- a/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; @@ -31,6 +32,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { + $name = $methodReflection->getName(); return array_key_exists($methodReflection->getName(), $this->methods); } @@ -42,15 +44,25 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $parametersAcceptor->getReturnType(); } $argType = $scope->getType($methodCall->args[$argumentIndex]->value); - if (!$argType instanceof ConstantStringType) { - return $parametersAcceptor->getReturnType(); + + $types = []; + if ($argType instanceof ConstantStringType) { + $types[] = new ObjectType($argType->getValue()); + } + + if ($argType instanceof ConstantArrayType) { + $types = array_map(function (Type $argType): ObjectType { + return new ObjectType($argType->getValue()); + }, $argType->getValueTypes()); } - $class = $argType->getValue(); + if (count($types) === 0) { + return $parametersAcceptor->getReturnType(); + } return TypeCombinator::intersect( - new ObjectType($class), - $parametersAcceptor->getReturnType() + $parametersAcceptor->getReturnType(), + ...$types ); } diff --git a/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php b/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php index 4c7dbe6..cd5e42f 100644 --- a/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Type; use PHPStan\Type\TypeWithClassName; @@ -30,18 +31,26 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method if (count($methodCall->args) === 0) { return $mockBuilderType; } + if (!$mockBuilderType instanceof TypeWithClassName) { + throw new \PHPStan\ShouldNotHappenException(); + } + $argType = $scope->getType($methodCall->args[0]->value); - if (!$argType instanceof ConstantStringType) { - return $mockBuilderType; + if ($argType instanceof ConstantStringType) { + $class = $argType->getValue(); + + return new MockBuilderType($mockBuilderType, $class); } - $class = $argType->getValue(); + if ($argType instanceof ConstantArrayType) { + $classes = array_map(function (Type $argType): string { + return $argType->getValue(); + }, $argType->getValueTypes()); - if (!$mockBuilderType instanceof TypeWithClassName) { - throw new \PHPStan\ShouldNotHappenException(); + return new MockBuilderType($mockBuilderType, ...$classes); } - return new MockBuilderType($mockBuilderType, $class); + return $mockBuilderType; } } diff --git a/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php b/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php index b75c69f..584ff4a 100644 --- a/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php @@ -60,10 +60,13 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method if (!$calledOnType instanceof MockBuilderType) { return $parametersAcceptor->getReturnType(); } + $types = array_map(function (string $type): ObjectType { + return new ObjectType($type); + }, $calledOnType->getMockedClasses()); return TypeCombinator::intersect( - new ObjectType($calledOnType->getMockedClass()), - $parametersAcceptor->getReturnType() + $parametersAcceptor->getReturnType(), + ...$types ); } diff --git a/src/Type/PHPUnit/MockBuilderType.php b/src/Type/PHPUnit/MockBuilderType.php index e47f16b..f8c39ad 100644 --- a/src/Type/PHPUnit/MockBuilderType.php +++ b/src/Type/PHPUnit/MockBuilderType.php @@ -8,26 +8,29 @@ class MockBuilderType extends \PHPStan\Type\ObjectType { - /** @var string */ - private $mockedClass; + /** @var array */ + private $mockedClasses; public function __construct( TypeWithClassName $mockBuilderType, - string $mockedClass + string ...$mockedClasses ) { parent::__construct($mockBuilderType->getClassName()); - $this->mockedClass = $mockedClass; + $this->mockedClasses = $mockedClasses; } - public function getMockedClass(): string + /** + * @return array + */ + public function getMockedClasses(): array { - return $this->mockedClass; + return $this->mockedClasses; } public function describe(VerbosityLevel $level): string { - return sprintf('%s<%s>', parent::describe($level), $this->mockedClass); + return sprintf('%s<%s>', parent::describe($level), implode('&', $this->mockedClasses)); } } diff --git a/tests/Type/PHPUnit/CreateMockExtensionTest.php b/tests/Type/PHPUnit/CreateMockExtensionTest.php new file mode 100644 index 0000000..e000361 --- /dev/null +++ b/tests/Type/PHPUnit/CreateMockExtensionTest.php @@ -0,0 +1,37 @@ +processFile( + __DIR__ . '/data/create-mock.php', + $expression, + $type, + [new CreateMockDynamicReturnTypeExtension(), new GetMockBuilderDynamicReturnTypeExtension()] + ); + } + + /** + * @return Iterator + */ + public function getProvider(): Iterator + { + yield ['$simpleInterface', implode('&', [FooInterface::class, MockObject::class])]; + yield ['$doubleInterface', implode('&', [BarInterface::class, FooInterface::class, MockObject::class])]; + } + +} diff --git a/tests/Type/PHPUnit/ExtensionTestCase.php b/tests/Type/PHPUnit/ExtensionTestCase.php new file mode 100644 index 0000000..e0f392f --- /dev/null +++ b/tests/Type/PHPUnit/ExtensionTestCase.php @@ -0,0 +1,77 @@ +createBroker($extensions); + $parser = $this->getParser(); + $currentWorkingDirectory = $this->getCurrentWorkingDirectory(); + $fileHelper = new FileHelper($currentWorkingDirectory); + $typeSpecifier = $this->createTypeSpecifier(new Standard(), $broker); + /** @var \PHPStan\PhpDoc\PhpDocStringResolver $phpDocStringResolver */ + $phpDocStringResolver = self::getContainer()->getByType(PhpDocStringResolver::class); + $resolver = new NodeScopeResolver( + $broker, + $parser, + new FileTypeMapper( + $parser, + $phpDocStringResolver, + self::getContainer()->getByType(PhpDocNodeResolver::class), + $this->createMock(Cache::class), + $this->createMock(AnonymousClassNameHelper::class) + ), + $fileHelper, + $typeSpecifier, + true, + true, + true, + [], + [] + ); + $resolver->setAnalysedFiles([$fileHelper->normalizePath($file)]); + + $run = false; + $resolver->processNodes( + $parser->parseFile($file), + $this->createScopeFactory($broker, $typeSpecifier)->create(ScopeContext::create($file)), + function (Node $node, Scope $scope) use ($expression, $type, &$run): void { + if ($node instanceof VirtualNode) { + return; + } + if ((new Standard())->prettyPrint([$node]) !== 'die') { + return; + } + /** @var \PhpParser\Node\Stmt\Expression $expNode */ + $expNode = $this->getParser()->parseString(sprintf('getType($expNode->expr)->describe(VerbosityLevel::typeOnly())); + $run = true; + } + ); + self::assertTrue($run); + } + +} diff --git a/tests/Type/PHPUnit/MockBuilderTypeExtensionTest.php b/tests/Type/PHPUnit/MockBuilderTypeExtensionTest.php new file mode 100644 index 0000000..896c766 --- /dev/null +++ b/tests/Type/PHPUnit/MockBuilderTypeExtensionTest.php @@ -0,0 +1,37 @@ +processFile( + __DIR__ . '/data/mock-builder.php', + $expression, + $type, + [new MockBuilderDynamicReturnTypeExtension(), new GetMockBuilderDynamicReturnTypeExtension()] + ); + } + + /** + * @return Iterator + */ + public function getProvider(): Iterator + { + yield ['$simpleInterface', implode('&', [FooInterface::class, MockObject::class])]; + yield ['$doubleInterface', implode('&', [BarInterface::class, FooInterface::class, MockObject::class])]; + } + +} diff --git a/tests/Type/PHPUnit/data/BarInterface.php b/tests/Type/PHPUnit/data/BarInterface.php new file mode 100644 index 0000000..4928f6c --- /dev/null +++ b/tests/Type/PHPUnit/data/BarInterface.php @@ -0,0 +1,9 @@ +getMethod('createMock')->setAccessible(true); +$simpleInterface = $test->createMock(\ExampleTestCase\FooInterface::class); +$doubleInterface = $test->createMock([\ExampleTestCase\FooInterface::class, \ExampleTestCase\BarInterface::class]); + +die; diff --git a/tests/Type/PHPUnit/data/mock-builder.php b/tests/Type/PHPUnit/data/mock-builder.php new file mode 100644 index 0000000..e28a80b --- /dev/null +++ b/tests/Type/PHPUnit/data/mock-builder.php @@ -0,0 +1,10 @@ +getMockBuilder(\ExampleTestCase\FooInterface::class)->getMock(); +$doubleInterface = $test->getMockBuilder([\ExampleTestCase\FooInterface::class, \ExampleTestCase\BarInterface::class])->getMock(); + +die; From 613e460c8064764083d8d01085d52aecdffce182 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 7 Feb 2020 14:38:12 +0100 Subject: [PATCH 3/3] fixes CI errors --- phpstan.neon | 1 + .../PHPUnit/CreateMockDynamicReturnTypeExtension.php | 5 +++-- .../GetMockBuilderDynamicReturnTypeExtension.php | 5 +++-- .../MockBuilderDynamicReturnTypeExtension.php | 4 +++- src/Type/PHPUnit/MockBuilderType.php | 3 ++- tests/Type/PHPUnit/CreateMockExtensionTest.php | 2 +- tests/Type/PHPUnit/ExtensionTestCase.php | 12 ++++++++++++ 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 9c4be9d..1138813 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,6 +7,7 @@ includes: parameters: excludes_analyse: - tests/*/data/* + treatPhpDocTypesAsCertain: false services: scopeIsInClass: diff --git a/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php b/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php index 295915e..3a03fea 100644 --- a/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php @@ -8,11 +8,12 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -class CreateMockDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension +class CreateMockDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { /** @var int[] */ @@ -51,7 +52,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } if ($argType instanceof ConstantArrayType) { - $types = array_map(function (Type $argType): ObjectType { + $types = array_map(function (ConstantStringType $argType): ObjectType { return new ObjectType($argType->getValue()); }, $argType->getValueTypes()); } diff --git a/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php b/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php index cd5e42f..5149767 100644 --- a/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/GetMockBuilderDynamicReturnTypeExtension.php @@ -8,10 +8,11 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; use PHPStan\Type\TypeWithClassName; -class GetMockBuilderDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension +class GetMockBuilderDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { public function getClass(): string @@ -43,7 +44,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } if ($argType instanceof ConstantArrayType) { - $classes = array_map(function (Type $argType): string { + $classes = array_map(function (ConstantStringType $argType): string { return $argType->getValue(); }, $argType->getValueTypes()); diff --git a/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php b/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php index 584ff4a..37a1919 100644 --- a/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php +++ b/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php @@ -5,14 +5,16 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Broker\Broker; +use PHPStan\Reflection\BrokerAwareExtension; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeWithClassName; -class MockBuilderDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension, \PHPStan\Reflection\BrokerAwareExtension +class MockBuilderDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension, BrokerAwareExtension { /** @var \PHPStan\Broker\Broker */ diff --git a/src/Type/PHPUnit/MockBuilderType.php b/src/Type/PHPUnit/MockBuilderType.php index f8c39ad..d77c3b6 100644 --- a/src/Type/PHPUnit/MockBuilderType.php +++ b/src/Type/PHPUnit/MockBuilderType.php @@ -2,10 +2,11 @@ namespace PHPStan\Type\PHPUnit; +use PHPStan\Type\ObjectType; use PHPStan\Type\TypeWithClassName; use PHPStan\Type\VerbosityLevel; -class MockBuilderType extends \PHPStan\Type\ObjectType +class MockBuilderType extends ObjectType { /** @var array */ diff --git a/tests/Type/PHPUnit/CreateMockExtensionTest.php b/tests/Type/PHPUnit/CreateMockExtensionTest.php index e000361..3994d15 100644 --- a/tests/Type/PHPUnit/CreateMockExtensionTest.php +++ b/tests/Type/PHPUnit/CreateMockExtensionTest.php @@ -21,7 +21,7 @@ public function testCreateMock(string $expression, string $type): void __DIR__ . '/data/create-mock.php', $expression, $type, - [new CreateMockDynamicReturnTypeExtension(), new GetMockBuilderDynamicReturnTypeExtension()] + [new CreateMockDynamicReturnTypeExtension()] ); } diff --git a/tests/Type/PHPUnit/ExtensionTestCase.php b/tests/Type/PHPUnit/ExtensionTestCase.php index e0f392f..c0bb043 100644 --- a/tests/Type/PHPUnit/ExtensionTestCase.php +++ b/tests/Type/PHPUnit/ExtensionTestCase.php @@ -14,12 +14,19 @@ use PHPStan\PhpDoc\PhpDocNodeResolver; use PHPStan\PhpDoc\PhpDocStringResolver; use PHPStan\Testing\TestCase; +use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\FileTypeMapper; use PHPStan\Type\VerbosityLevel; abstract class ExtensionTestCase extends TestCase { + /** + * @param string $file + * @param string $expression + * @param string $type + * @param array $extensions + */ protected function processFile( string $file, string $expression, @@ -27,6 +34,11 @@ protected function processFile( array $extensions ): void { + foreach ($extensions as $extension) { + if (!$extension instanceof DynamicMethodReturnTypeExtension) { + throw new \InvalidArgumentException(); + } + } $broker = $this->createBroker($extensions); $parser = $this->getParser(); $currentWorkingDirectory = $this->getCurrentWorkingDirectory();