Skip to content

Commit 2e7cb7d

Browse files
committed
ExtendedPropertyReflection - methods describing hooked properties
1 parent c91df5c commit 2e7cb7d

21 files changed

+940
-29
lines changed

src/Node/PropertyHookStatementNode.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,4 @@ public function getSubNodeNames(): array
4848
return [];
4949
}
5050

51-
5251
}

src/Reflection/Annotations/AnnotationPropertyReflection.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace PHPStan\Reflection\Annotations;
44

55
use PHPStan\Reflection\ClassReflection;
6+
use PHPStan\Reflection\ExtendedMethodReflection;
67
use PHPStan\Reflection\ExtendedPropertyReflection;
8+
use PHPStan\ShouldNotHappenException;
79
use PHPStan\TrinaryLogic;
810
use PHPStan\Type\Type;
911

@@ -85,4 +87,29 @@ public function getDocComment(): ?string
8587
return null;
8688
}
8789

90+
public function isAbstract(): TrinaryLogic
91+
{
92+
return TrinaryLogic::createNo();
93+
}
94+
95+
public function isFinal(): TrinaryLogic
96+
{
97+
return TrinaryLogic::createNo();
98+
}
99+
100+
public function isVirtual(): TrinaryLogic
101+
{
102+
return TrinaryLogic::createNo();
103+
}
104+
105+
public function hasHook(string $hookType): bool
106+
{
107+
return false;
108+
}
109+
110+
public function getHook(string $hookType): ExtendedMethodReflection
111+
{
112+
throw new ShouldNotHappenException();
113+
}
114+
88115
}

src/Reflection/Dummy/ChangedTypePropertyReflection.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Reflection\Dummy;
44

55
use PHPStan\Reflection\ClassReflection;
6+
use PHPStan\Reflection\ExtendedMethodReflection;
67
use PHPStan\Reflection\ExtendedPropertyReflection;
78
use PHPStan\Reflection\WrapperPropertyReflection;
89
use PHPStan\TrinaryLogic;
@@ -85,4 +86,29 @@ public function getOriginalReflection(): ExtendedPropertyReflection
8586
return $this->reflection;
8687
}
8788

89+
public function isAbstract(): TrinaryLogic
90+
{
91+
return $this->reflection->isAbstract();
92+
}
93+
94+
public function isFinal(): TrinaryLogic
95+
{
96+
return $this->reflection->isFinal();
97+
}
98+
99+
public function isVirtual(): TrinaryLogic
100+
{
101+
return $this->reflection->isVirtual();
102+
}
103+
104+
public function hasHook(string $hookType): bool
105+
{
106+
return $this->reflection->hasHook($hookType);
107+
}
108+
109+
public function getHook(string $hookType): ExtendedMethodReflection
110+
{
111+
return $this->reflection->getHook($hookType);
112+
}
113+
88114
}

src/Reflection/Dummy/DummyPropertyReflection.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace PHPStan\Reflection\Dummy;
44

55
use PHPStan\Reflection\ClassReflection;
6+
use PHPStan\Reflection\ExtendedMethodReflection;
67
use PHPStan\Reflection\ExtendedPropertyReflection;
78
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
9+
use PHPStan\ShouldNotHappenException;
810
use PHPStan\TrinaryLogic;
911
use PHPStan\Type\MixedType;
1012
use PHPStan\Type\Type;
@@ -80,4 +82,29 @@ public function getDocComment(): ?string
8082
return null;
8183
}
8284

85+
public function isAbstract(): TrinaryLogic
86+
{
87+
return TrinaryLogic::createNo();
88+
}
89+
90+
public function isFinal(): TrinaryLogic
91+
{
92+
return TrinaryLogic::createNo();
93+
}
94+
95+
public function isVirtual(): TrinaryLogic
96+
{
97+
return TrinaryLogic::createNo();
98+
}
99+
100+
public function hasHook(string $hookType): bool
101+
{
102+
return false;
103+
}
104+
105+
public function getHook(string $hookType): ExtendedMethodReflection
106+
{
107+
throw new ShouldNotHappenException();
108+
}
109+
83110
}

src/Reflection/ExtendedPropertyReflection.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PHPStan\Reflection;
44

5+
use PHPStan\TrinaryLogic;
6+
57
/**
68
* The purpose of this interface is to be able to
79
* answer more questions about properties
@@ -19,4 +21,24 @@
1921
interface ExtendedPropertyReflection extends PropertyReflection
2022
{
2123

24+
public const HOOK_GET = 'get';
25+
26+
public const HOOK_SET = 'set';
27+
28+
public function isAbstract(): TrinaryLogic;
29+
30+
public function isFinal(): TrinaryLogic;
31+
32+
public function isVirtual(): TrinaryLogic;
33+
34+
/**
35+
* @param self::HOOK_* $hookType
36+
*/
37+
public function hasHook(string $hookType): bool;
38+
39+
/**
40+
* @param self::HOOK_* $hookType
41+
*/
42+
public function getHook(string $hookType): ExtendedMethodReflection;
43+
2244
}

src/Reflection/Php/EnumPropertyReflection.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace PHPStan\Reflection\Php;
44

55
use PHPStan\Reflection\ClassReflection;
6+
use PHPStan\Reflection\ExtendedMethodReflection;
67
use PHPStan\Reflection\ExtendedPropertyReflection;
8+
use PHPStan\ShouldNotHappenException;
79
use PHPStan\TrinaryLogic;
810
use PHPStan\Type\Type;
911

@@ -79,4 +81,29 @@ public function isInternal(): TrinaryLogic
7981
return TrinaryLogic::createNo();
8082
}
8183

84+
public function isAbstract(): TrinaryLogic
85+
{
86+
return TrinaryLogic::createNo();
87+
}
88+
89+
public function isFinal(): TrinaryLogic
90+
{
91+
return TrinaryLogic::createNo();
92+
}
93+
94+
public function isVirtual(): TrinaryLogic
95+
{
96+
return TrinaryLogic::createNo();
97+
}
98+
99+
public function hasHook(string $hookType): bool
100+
{
101+
return false;
102+
}
103+
104+
public function getHook(string $hookType): ExtendedMethodReflection
105+
{
106+
throw new ShouldNotHappenException();
107+
}
108+
82109
}

src/Reflection/Php/PhpClassReflectionExtension.php

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
use PHPStan\Type\ErrorType;
4343
use PHPStan\Type\FileTypeMapper;
4444
use PHPStan\Type\GeneralizePrecision;
45+
use PHPStan\Type\Generic\TemplateMixedType;
4546
use PHPStan\Type\Generic\TemplateTypeHelper;
4647
use PHPStan\Type\Generic\TemplateTypeMap;
4748
use PHPStan\Type\Generic\TemplateTypeVariance;
@@ -212,7 +213,7 @@ private function createProperty(
212213
$types[] = $value;
213214
}
214215

215-
return new PhpPropertyReflection($declaringClassReflection, null, null, TypeCombinator::union(...$types), $classReflection->getNativeReflection()->getProperty($propertyName), null, false, false, false, false);
216+
return new PhpPropertyReflection($declaringClassReflection, null, null, TypeCombinator::union(...$types), $classReflection->getNativeReflection()->getProperty($propertyName), null, null, null, false, false, false, false);
216217
}
217218
}
218219

@@ -353,12 +354,72 @@ private function createProperty(
353354
$declaringTrait = $reflectionProvider->getClass($declaringTraitName);
354355
}
355356

357+
$getHook = null;
358+
$setHook = null;
359+
360+
$betterReflection = $propertyReflection->getBetterReflection();
361+
if ($betterReflection->hasHook('get')) {
362+
$betterReflectionGetHook = $betterReflection->getHook('get');
363+
if ($betterReflectionGetHook === null) {
364+
throw new ShouldNotHappenException();
365+
}
366+
$getHook = $this->createUserlandMethodReflection(
367+
$declaringClassReflection,
368+
$declaringClassReflection,
369+
new ReflectionMethod($betterReflectionGetHook),
370+
$declaringTraitName,
371+
);
372+
373+
if ($phpDocType !== null) {
374+
$getHookMethodReflectionVariant = $getHook->getOnlyVariant();
375+
$getHookMethodReflectionVariantPhpDocReturnType = $getHookMethodReflectionVariant->getPhpDocReturnType();
376+
if (
377+
$getHookMethodReflectionVariantPhpDocReturnType instanceof MixedType
378+
&& !$getHookMethodReflectionVariantPhpDocReturnType instanceof TemplateMixedType
379+
&& !$getHookMethodReflectionVariantPhpDocReturnType->isExplicitMixed()
380+
) {
381+
$getHook = $getHook->changePropertyGetHookPhpDocType($phpDocType);
382+
}
383+
}
384+
}
385+
386+
if ($betterReflection->hasHook('set')) {
387+
$betterReflectionSetHook = $betterReflection->getHook('set');
388+
if ($betterReflectionSetHook === null) {
389+
throw new ShouldNotHappenException();
390+
}
391+
$setHook = $this->createUserlandMethodReflection(
392+
$declaringClassReflection,
393+
$declaringClassReflection,
394+
new ReflectionMethod($betterReflectionSetHook),
395+
$declaringTraitName,
396+
);
397+
398+
if ($phpDocType !== null) {
399+
$setHookMethodReflectionVariant = $setHook->getOnlyVariant();
400+
$setHookMethodReflectionParameters = $setHookMethodReflectionVariant->getParameters();
401+
if (isset($setHookMethodReflectionParameters[0])) {
402+
$setHookMethodReflectionParameter = $setHookMethodReflectionParameters[0];
403+
$setHookMethodReflectionParameterPhpDocType = $setHookMethodReflectionParameter->getPhpDocType();
404+
if (
405+
$setHookMethodReflectionParameterPhpDocType instanceof MixedType
406+
&& !$setHookMethodReflectionParameterPhpDocType instanceof TemplateMixedType
407+
&& !$setHookMethodReflectionParameterPhpDocType->isExplicitMixed()
408+
) {
409+
$setHook = $setHook->changePropertySetHookPhpDocType($setHookMethodReflectionParameter->getName(), $phpDocType);
410+
}
411+
}
412+
}
413+
}
414+
356415
return new PhpPropertyReflection(
357416
$declaringClassReflection,
358417
$declaringTrait,
359418
$nativeType,
360419
$phpDocType,
361420
$propertyReflection,
421+
$getHook,
422+
$setHook,
362423
$deprecatedDescription,
363424
$isDeprecated,
364425
$isInternal,

src/Reflection/Php/PhpMethodReflection.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,63 @@ public function isPure(): TrinaryLogic
450450
return TrinaryLogic::createFromBoolean($this->isPure);
451451
}
452452

453+
public function changePropertyGetHookPhpDocType(Type $phpDocType): self
454+
{
455+
return new self(
456+
$this->initializerExprTypeResolver,
457+
$this->declaringClass,
458+
$this->declaringTrait,
459+
$this->reflection,
460+
$this->reflectionProvider,
461+
$this->parser,
462+
$this->templateTypeMap,
463+
$this->phpDocParameterTypes,
464+
$phpDocType,
465+
$this->phpDocThrowType,
466+
$this->deprecatedDescription,
467+
$this->isDeprecated,
468+
$this->isInternal,
469+
$this->isFinal,
470+
$this->isPure,
471+
$this->asserts,
472+
$this->acceptsNamedArguments,
473+
$this->selfOutType,
474+
$this->phpDocComment,
475+
$this->phpDocParameterOutTypes,
476+
$this->immediatelyInvokedCallableParameters,
477+
$this->phpDocClosureThisTypeParameters,
478+
);
479+
}
480+
481+
public function changePropertySetHookPhpDocType(string $parameterName, Type $phpDocType): self
482+
{
483+
$phpDocParameterTypes = $this->phpDocParameterTypes;
484+
$phpDocParameterTypes[$parameterName] = $phpDocType;
485+
486+
return new self(
487+
$this->initializerExprTypeResolver,
488+
$this->declaringClass,
489+
$this->declaringTrait,
490+
$this->reflection,
491+
$this->reflectionProvider,
492+
$this->parser,
493+
$this->templateTypeMap,
494+
$phpDocParameterTypes,
495+
$this->phpDocReturnType,
496+
$this->phpDocThrowType,
497+
$this->deprecatedDescription,
498+
$this->isDeprecated,
499+
$this->isInternal,
500+
$this->isFinal,
501+
$this->isPure,
502+
$this->asserts,
503+
$this->acceptsNamedArguments,
504+
$this->selfOutType,
505+
$this->phpDocComment,
506+
$this->phpDocParameterOutTypes,
507+
$this->immediatelyInvokedCallableParameters,
508+
$this->phpDocClosureThisTypeParameters,
509+
);
510+
}
511+
453512
}

0 commit comments

Comments
 (0)