Skip to content

Commit 55c1791

Browse files
committed
Handle DurringCall magic methods
1 parent c38dc02 commit 55c1791

7 files changed

+186
-2
lines changed

extension.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ services:
22
-
33
class: Proget\PHPStan\PhpSpec\Reflection\ObjectBehaviorMethodsClassReflectionExtension
44
tags: [phpstan.broker.methodsClassReflectionExtension]
5+
-
6+
class: Proget\PHPStan\PhpSpec\Reflection\DuringCallMethodsClassReflectionExtension
7+
tags: [phpstan.broker.methodsClassReflectionExtension]
58
-
69
class: Proget\PHPStan\PhpSpec\Reflection\ObjectBehaviorPropertiesClassReflectionExtension
710
tags: [phpstan.broker.propertiesClassReflectionExtension]

spec/PhpSpec/CodeAnalysis/MagicAwareAccessInspectorSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function it_should_detect_a_magic_getter_if_no_value_is_given()
2626

2727
public function it_should_detect_a_magic_setter_if_a_value_is_given()
2828
{
29-
$this->isPropertyWritable(new ObjectWithMagicSet, 'property', true)->shouldReturn(true);
29+
$this->isPropertyWritable(new ObjectWithMagicSet, 'property')->shouldReturn(true);
3030
}
3131

3232
public function it_should_detect_a_magic_call_method()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Proget\PHPStan\PhpSpec\Reflection;
6+
7+
use PHPStan\Analyser\OutOfClassScope;
8+
use PHPStan\Broker\Broker;
9+
use PHPStan\Reflection\BrokerAwareExtension;
10+
use PHPStan\Reflection\ClassReflection;
11+
use PHPStan\Reflection\MethodReflection;
12+
use PHPStan\Reflection\MethodsClassReflectionExtension;
13+
use PhpSpec\Wrapper\Subject\Expectation\DuringCall;
14+
15+
final class DuringCallMethodsClassReflectionExtension implements MethodsClassReflectionExtension, BrokerAwareExtension
16+
{
17+
/**
18+
* @var Broker
19+
*/
20+
private $broker;
21+
22+
public function setBroker(Broker $broker): void
23+
{
24+
$this->broker = $broker;
25+
}
26+
27+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
28+
{
29+
return $classReflection->getName() === DuringCall::class && preg_match('/^during(.+)$/', $methodName) !== false;
30+
}
31+
32+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
33+
{
34+
return new DuringMethodReflection($classReflection->getMethod('during', new OutOfClassScope()));
35+
}
36+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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\FunctionVariantWithPhpDocs;
10+
use PHPStan\Reflection\MethodReflection;
11+
12+
final class DuringMethodReflection implements MethodReflection
13+
{
14+
/**
15+
* @var MethodReflection
16+
*/
17+
private $during;
18+
19+
public function __construct(MethodReflection $during)
20+
{
21+
$this->during = $during;
22+
}
23+
24+
public function getDeclaringClass(): ClassReflection
25+
{
26+
return $this->during->getDeclaringClass();
27+
}
28+
29+
public function isStatic(): bool
30+
{
31+
return $this->during->isStatic();
32+
}
33+
34+
public function isPrivate(): bool
35+
{
36+
return $this->during->isPrivate();
37+
}
38+
39+
public function isPublic(): bool
40+
{
41+
return $this->during->isPublic();
42+
}
43+
44+
public function getName(): string
45+
{
46+
return $this->during->getName();
47+
}
48+
49+
public function getPrototype(): ClassMemberReflection
50+
{
51+
return $this->during->getPrototype();
52+
}
53+
54+
public function getVariants(): array
55+
{
56+
$variants = $this->during->getVariants();
57+
if (count($variants) === 1 && $variants[0] instanceof FunctionVariantWithPhpDocs) {
58+
/** @var FunctionVariantWithPhpDocs $variant */
59+
$variant = $variants[0];
60+
61+
return [new FunctionVariantWithPhpDocs(
62+
[$variant->getParameters()[1]], //remove first param (see DuringCall::__call)
63+
$variant->isVariadic(),
64+
$variant->getReturnType(),
65+
$variant->getPhpDocReturnType(),
66+
$variant->getNativeReturnType()
67+
)];
68+
}
69+
70+
return $variants;
71+
}
72+
}

src/Reflection/ObjectBehaviorMethodsClassReflectionExtension.php

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

60+
return $this->getWrappedClassMethodReflection($classReflection, $methodName);
61+
}
62+
63+
private function getWrappedClassMethodReflection(ClassReflection $classReflection, $methodName): MethodReflection
64+
{
6065
/** @var PSR0Resource[] $resources */
6166
$resources = $this->locator->findResources((string) $classReflection->getFileName());
6267

src/Reflection/ObjectBehaviorPropertiesClassReflectionExtension.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
use PHPStan\Reflection\ClassReflection;
1515
use PHPStan\Reflection\PropertiesClassReflectionExtension;
1616
use PHPStan\Reflection\PropertyReflection;
17+
use PHPStan\Type\MixedType;
1718
use Proget\PHPStan\PhpSpec\Exception\SpecSourceClassNotFound;
19+
use Proget\PHPStan\PhpSpec\Type\SubjectConstantPropertyReflection;
1820

1921
final class ObjectBehaviorPropertiesClassReflectionExtension implements PropertiesClassReflectionExtension, BrokerAwareExtension
2022
{
@@ -64,7 +66,9 @@ public function getProperty(ClassReflection $classReflection, string $propertyNa
6466

6567
//special case to handle magic proxy call in object behavior
6668
if ($srcClassReflection->hasConstant($propertyName)) {
67-
throw new \RuntimeException('Need to implement this');
69+
$constant = $srcClassReflection->getConstant($propertyName);
70+
71+
return new SubjectConstantPropertyReflection($constant->getDeclaringClass(), new MixedType());
6872
}
6973
}
7074
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Proget\PHPStan\PhpSpec\Type;
6+
7+
use PHPStan\Reflection\ClassReflection;
8+
use PHPStan\Reflection\PropertyReflection;
9+
use PHPStan\Type\Type;
10+
11+
final class SubjectConstantPropertyReflection implements PropertyReflection
12+
{
13+
/**
14+
* @var ClassReflection
15+
*/
16+
private $declaringClass;
17+
18+
/**
19+
* @var Type
20+
*/
21+
private $type;
22+
23+
public function __construct(ClassReflection $declaringClass, Type $type)
24+
{
25+
$this->declaringClass = $declaringClass;
26+
$this->type = $type;
27+
}
28+
29+
public function getDeclaringClass(): ClassReflection
30+
{
31+
return $this->declaringClass;
32+
}
33+
34+
public function isStatic(): bool
35+
{
36+
// although it could be argued, however, from the point of view of the spec, it is not static
37+
return false;
38+
}
39+
40+
public function isPrivate(): bool
41+
{
42+
return false;
43+
}
44+
45+
public function isPublic(): bool
46+
{
47+
return true;
48+
}
49+
50+
public function getType(): Type
51+
{
52+
return $this->type;
53+
}
54+
55+
public function isReadable(): bool
56+
{
57+
return true;
58+
}
59+
60+
public function isWritable(): bool
61+
{
62+
return false;
63+
}
64+
}

0 commit comments

Comments
 (0)