-
Notifications
You must be signed in to change notification settings - Fork 432
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement require-extends reflection plumbing
- Loading branch information
1 parent
8900a43
commit 1740286
Showing
18 changed files
with
516 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
src/Reflection/RequireExtension/RequireExtendsMethodsClassReflectionExtension.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Reflection\RequireExtension; | ||
|
||
use PHPStan\Analyser\OutOfClassScope; | ||
use PHPStan\Reflection\ClassReflection; | ||
use PHPStan\Reflection\ExtendedMethodReflection; | ||
use PHPStan\Reflection\MethodReflection; | ||
use PHPStan\Reflection\MethodsClassReflectionExtension; | ||
use PHPStan\ShouldNotHappenException; | ||
|
||
class RequireExtendsMethodsClassReflectionExtension implements MethodsClassReflectionExtension | ||
{ | ||
|
||
public function hasMethod(ClassReflection $classReflection, string $methodName): bool | ||
{ | ||
return $this->findMethod($classReflection, $methodName) !== null; | ||
} | ||
|
||
/** | ||
* @return ExtendedMethodReflection | ||
*/ | ||
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection | ||
{ | ||
$method = $this->findMethod($classReflection, $methodName); | ||
if ($method === null) { | ||
throw new ShouldNotHappenException(); | ||
} | ||
|
||
return $method; | ||
} | ||
|
||
/** | ||
* @return ExtendedMethodReflection|null | ||
*/ | ||
private function findMethod(ClassReflection $classReflection, string $methodName): ?MethodReflection | ||
{ | ||
if (!$classReflection->isInterface()) { | ||
return null; | ||
} | ||
|
||
$extendsTags = $classReflection->getRequireExtendsTags(); | ||
foreach ($extendsTags as $extendsTag) { | ||
$type = $extendsTag->getType(); | ||
|
||
if (!$type->hasMethod($methodName)->yes()) { | ||
continue; | ||
} | ||
|
||
return $type->getMethod($methodName, new OutOfClassScope()); | ||
} | ||
|
||
$interfaces = $classReflection->getInterfaces(); | ||
foreach ($interfaces as $interface) { | ||
$method = $this->findMethod($interface, $methodName); | ||
if ($method !== null) { | ||
return $method; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
} |
57 changes: 57 additions & 0 deletions
57
src/Reflection/RequireExtension/RequireExtendsPropertiesClassReflectionExtension.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Reflection\RequireExtension; | ||
|
||
use PHPStan\Analyser\OutOfClassScope; | ||
use PHPStan\Reflection\ClassReflection; | ||
use PHPStan\Reflection\PropertiesClassReflectionExtension; | ||
use PHPStan\Reflection\PropertyReflection; | ||
use PHPStan\ShouldNotHappenException; | ||
|
||
class RequireExtendsPropertiesClassReflectionExtension implements PropertiesClassReflectionExtension | ||
{ | ||
|
||
public function hasProperty(ClassReflection $classReflection, string $propertyName): bool | ||
{ | ||
return $this->findProperty($classReflection, $propertyName) !== null; | ||
} | ||
|
||
public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection | ||
{ | ||
$property = $this->findProperty($classReflection, $propertyName); | ||
if ($property === null) { | ||
throw new ShouldNotHappenException(); | ||
} | ||
|
||
return $property; | ||
} | ||
|
||
private function findProperty(ClassReflection $classReflection, string $propertyName): ?PropertyReflection | ||
{ | ||
if (!$classReflection->isInterface()) { | ||
return null; | ||
} | ||
|
||
$requireExtendsTags = $classReflection->getRequireExtendsTags(); | ||
foreach ($requireExtendsTags as $requireExtendsTag) { | ||
$type = $requireExtendsTag->getType(); | ||
|
||
if (!$type->hasProperty($propertyName)->yes()) { | ||
continue; | ||
} | ||
|
||
return $type->getProperty($propertyName, new OutOfClassScope()); | ||
} | ||
|
||
$interfaces = $classReflection->getInterfaces(); | ||
foreach ($interfaces as $interface) { | ||
$property = $this->findProperty($interface, $propertyName); | ||
if ($property !== null) { | ||
return $property; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
tests/PHPStan/Analyser/data/bug-10302-interface-extends.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
namespace Bug10302InterfaceExtends; | ||
|
||
use function PHPStan\Testing\assertType; | ||
|
||
/** | ||
* @phpstan-require-extends SomeClass | ||
*/ | ||
interface SampleInterface | ||
{ | ||
} | ||
|
||
class SomeClass { | ||
public int $x; | ||
protected string $y; | ||
private array $z = []; | ||
|
||
public function doFoo():int | ||
{ | ||
return 1; | ||
} | ||
} | ||
|
||
function test(SampleInterface $test): void | ||
{ | ||
assertType('int', $test->x); | ||
assertType('string', $test->y); | ||
assertType('array', $test->z); | ||
|
||
assertType('int', $test->doFoo()); | ||
} | ||
|
||
function testExtendedInterface(AnotherInterface $test): void | ||
{ | ||
assertType('int', $test->x); | ||
assertType('string', $test->y); | ||
assertType('array', $test->z); | ||
|
||
assertType('int', $test->doFoo()); | ||
} | ||
|
||
interface AnotherInterface extends SampleInterface | ||
{ | ||
} | ||
|
||
class SomeSubClass extends SomeClass {} | ||
|
||
class ValidClass extends SomeClass implements SampleInterface {} | ||
|
||
class ValidSubClass extends SomeSubClass implements SampleInterface {} | ||
|
||
class InvalidClass implements SampleInterface {} |
Oops, something went wrong.