Skip to content

Commit d14128a

Browse files
committed
Provides correct class for method
1 parent 16ca57b commit d14128a

6 files changed

+135
-9
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
* Provides correct attributes for `ObjectBehavior`:
2626
* public attributes
2727
* static properties (with `$this->CONSTANT_NAME`)
28+
* Provides correct class for `getWrappedObject` method
29+
2830

2931
## Compatibility
3032

spec/Proget/Tests/FooSpec.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public function it_should_return_int_from_baz(Bar $bar, Baz $baz): void
2121
$bar->baz = $baz;
2222
$baz->someInt()->willReturn(99);
2323

24-
$this->getIntFromBaz($bar)->shouldBe(99);
24+
// intentionally to test support for correct return type
25+
$int = $baz->getWrappedObject()->someInt();
26+
$this->getWrappedObject()->property;
27+
28+
$this->getIntFromBaz($bar)->shouldBe($int);
2529
}
2630
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Proget\PHPStan\PhpSpec\Reflection;
6+
7+
use PHPStan\Reflection\ClassMemberReflection;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Reflection\FunctionVariant;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Type\ObjectType;
12+
13+
final class GetWrappedObjectMethodReflection implements MethodReflection
14+
{
15+
/**
16+
* @var MethodReflection
17+
*/
18+
private $methodReflection;
19+
20+
/**
21+
* @var string
22+
*/
23+
private $wrappedClass;
24+
25+
public function __construct(MethodReflection $methodReflection, string $wrappedClass)
26+
{
27+
$this->methodReflection = $methodReflection;
28+
$this->wrappedClass = $wrappedClass;
29+
}
30+
31+
public function getDeclaringClass(): ClassReflection
32+
{
33+
return $this->methodReflection->getDeclaringClass();
34+
}
35+
36+
public function isStatic(): bool
37+
{
38+
return $this->methodReflection->isStatic();
39+
}
40+
41+
public function isPrivate(): bool
42+
{
43+
return $this->methodReflection->isPrivate();
44+
}
45+
46+
public function isPublic(): bool
47+
{
48+
return $this->methodReflection->isPublic();
49+
}
50+
51+
public function getName(): string
52+
{
53+
return $this->methodReflection->getName();
54+
}
55+
56+
public function getPrototype(): ClassMemberReflection
57+
{
58+
return $this->methodReflection->getPrototype();
59+
}
60+
61+
public function getVariants(): array
62+
{
63+
$variant = $this->methodReflection->getVariants()[0];
64+
65+
return [
66+
new FunctionVariant(
67+
$variant->getParameters(),
68+
$variant->isVariadic(),
69+
new ObjectType($this->wrappedClass)
70+
)
71+
];
72+
}
73+
}

src/Reflection/ObjectBehaviorMethodsClassReflectionExtension.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,14 @@ public function getMethod(ClassReflection $classReflection, string $methodName):
5757
return $subjectReflection->getMethod($methodName, new OutOfClassScope());
5858
}
5959

60-
return $this->getWrappedClassMethodReflection($classReflection, $methodName);
60+
return new ObjectBehaviorMethodReflection(
61+
$this->broker->getClass(
62+
$this->getSourceClassName($classReflection)
63+
)->getMethod($methodName, new OutOfClassScope())
64+
);
6165
}
6266

63-
private function getWrappedClassMethodReflection(ClassReflection $classReflection, $methodName): MethodReflection
67+
private function getSourceClassName(ClassReflection $classReflection): string
6468
{
6569
/** @var PSR0Resource[] $resources */
6670
$resources = $this->locator->findResources((string) $classReflection->getFileName());
@@ -74,8 +78,6 @@ private function getWrappedClassMethodReflection(ClassReflection $classReflectio
7478
throw new SpecSourceClassNotFound(sprintf('Spec source class %s not found', $className));
7579
}
7680

77-
$srcClassReflection = $this->broker->getClass($className);
78-
79-
return new ObjectBehaviorMethodReflection($srcClassReflection->getMethod($methodName, new OutOfClassScope()));
81+
return $className;
8082
}
8183
}

src/Reflection/SpoofedCollaboratorMethodsClassReflectionExtension.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@ public function hasMethod(ClassReflection $classReflection, string $methodName):
3535

3636
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
3737
{
38+
$collaboratorClassName = (string) preg_replace('/Collaborator$/', '', SpoofedCollaboratorRegistry::getAlias($classReflection->getName()));
3839
$collaboratorReflection = $this->broker->getClass(Collaborator::class);
40+
41+
if ($methodName === 'getWrappedObject') {
42+
return new GetWrappedObjectMethodReflection($collaboratorReflection->getMethod($methodName, new OutOfClassScope()), $collaboratorClassName);
43+
}
44+
3945
if ($collaboratorReflection->hasMethod($methodName)) {
4046
return $collaboratorReflection->getMethod($methodName, new OutOfClassScope());
4147
}
4248

43-
$collaboratorClassName = (string) preg_replace('/Collaborator$/', '', SpoofedCollaboratorRegistry::getAlias($classReflection->getName()));
44-
4549
return new CollaboratorMethodReflection($this->broker->getClass($collaboratorClassName)->getMethod($methodName, new OutOfClassScope()));
4650
}
4751
}

src/Type/ObjectBehaviorDynamicMethodReturnTypeExtension.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,51 @@
66

77
use PhpParser\Node\Expr\MethodCall;
88
use PhpSpec\Locator\PSR0\PSR0Locator;
9+
use PhpSpec\Locator\PSR0\PSR0Resource;
910
use PhpSpec\Locator\Resource;
11+
use PhpSpec\Locator\ResourceLocator;
1012
use PhpSpec\ObjectBehavior;
13+
use PhpSpec\Util\Filesystem;
1114
use PHPStan\Analyser\Scope;
15+
use PHPStan\Reflection\ClassReflection;
1216
use PHPStan\Reflection\MethodReflection;
1317
use PHPStan\ShouldNotHappenException;
1418
use PHPStan\Type\ArrayType;
1519
use PHPStan\Type\DynamicMethodReturnTypeExtension;
1620
use PHPStan\Type\ObjectType;
1721
use PHPStan\Type\ResourceType;
1822
use PHPStan\Type\Type;
23+
use Proget\PHPStan\PhpSpec\Exception\SpecSourceClassNotFound;
1924
use Proget\PHPStan\PhpSpec\Reflection\ObjectBehaviorMethodReflection;
2025

2126
final class ObjectBehaviorDynamicMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension
2227
{
28+
/**
29+
* @var ResourceLocator
30+
*/
31+
private $locator;
32+
33+
public function __construct()
34+
{
35+
$this->locator = new PSR0Locator(new Filesystem());
36+
}
37+
2338
public function getClass(): string
2439
{
2540
return ObjectBehavior::class;
2641
}
2742

2843
public function isMethodSupported(MethodReflection $methodReflection): bool
2944
{
30-
return $methodReflection instanceof ObjectBehaviorMethodReflection;
45+
return $methodReflection instanceof ObjectBehaviorMethodReflection || $methodReflection->getName() === 'getWrappedObject';
3146
}
3247

3348
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
3449
{
50+
if ($methodReflection->getName() === 'getWrappedObject') {
51+
return new ObjectType($this->getSourceClassName($scope->getClassReflection()));
52+
}
53+
3554
if (!$methodReflection instanceof ObjectBehaviorMethodReflection) {
3655
throw new ShouldNotHappenException();
3756
}
@@ -50,4 +69,26 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
5069

5170
return new SubjectType($returnType);
5271
}
72+
73+
// todo: looks like duplication from ObjectBehaviorMethodsClassReflectionExtension
74+
private function getSourceClassName(?ClassReflection $classReflection): string
75+
{
76+
if ($classReflection === null) {
77+
throw new ShouldNotHappenException();
78+
}
79+
80+
/** @var PSR0Resource[] $resources */
81+
$resources = $this->locator->findResources((string) $classReflection->getFileName());
82+
83+
if (count($resources) === 0) {
84+
throw new SpecSourceClassNotFound(sprintf('Source class from %s not found', $classReflection->getName()));
85+
}
86+
87+
$className = $resources[0]->getSrcClassname();
88+
if (!class_exists($className)) {
89+
throw new SpecSourceClassNotFound(sprintf('Spec source class %s not found', $className));
90+
}
91+
92+
return $className;
93+
}
5394
}

0 commit comments

Comments
 (0)