diff --git a/extension.neon b/extension.neon index f3a1e35..d26555d 100644 --- a/extension.neon +++ b/extension.neon @@ -18,3 +18,11 @@ services: class: PHPStan\Type\PHPUnit\MockBuilderDynamicReturnTypeExtension tags: - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: PHPStan\Type\Prophecy\ProphetDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: PHPStan\Type\Prophecy\ObjectProphecyDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension diff --git a/src/Type/Prophecy/ObjectProphecyDynamicReturnTypeExtension.php b/src/Type/Prophecy/ObjectProphecyDynamicReturnTypeExtension.php new file mode 100644 index 0000000..afd663d --- /dev/null +++ b/src/Type/Prophecy/ObjectProphecyDynamicReturnTypeExtension.php @@ -0,0 +1,38 @@ +getName() === 'reveal') { + $calledOnType = $scope->getType($methodCall->var); + + if ($calledOnType instanceof ObjectProphecyType) { + return new ObjectType($calledOnType->getMockedClass()); + } + } + + return $methodReflection->getReturnType(); + } + +} diff --git a/src/Type/Prophecy/ObjectProphecyType.php b/src/Type/Prophecy/ObjectProphecyType.php new file mode 100644 index 0000000..f607747 --- /dev/null +++ b/src/Type/Prophecy/ObjectProphecyType.php @@ -0,0 +1,31 @@ +mockedClass = $mockedClass; + } + + public function getMockedClass(): string + { + return $this->mockedClass; + } + + public function describe(): string + { + return sprintf('%s<%s>', parent::describe(), $this->mockedClass); + } + +} diff --git a/src/Type/Prophecy/ProphetDynamicReturnTypeExtension.php b/src/Type/Prophecy/ProphetDynamicReturnTypeExtension.php new file mode 100644 index 0000000..0a58022 --- /dev/null +++ b/src/Type/Prophecy/ProphetDynamicReturnTypeExtension.php @@ -0,0 +1,51 @@ +getName() === 'prophesize'; + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + if (count($methodCall->args) === 0) { + return $methodReflection->getReturnType(); + } + $arg = $methodCall->args[0]->value; + if (!($arg instanceof \PhpParser\Node\Expr\ClassConstFetch)) { + return $methodReflection->getReturnType(); + } + + $class = $arg->class; + if (!($class instanceof \PhpParser\Node\Name)) { + return $methodReflection->getReturnType(); + } + + $class = (string) $class; + if ($class === 'static') { + return $methodReflection->getReturnType(); + } + + if ($class === 'self') { + $class = $scope->getClassReflection()->getName(); + } + + return new ObjectProphecyType($class); + } + +}