Skip to content

Commit 8f7e973

Browse files
Difference between Instance and Static properties
1 parent a389320 commit 8f7e973

File tree

49 files changed

+1362
-152
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1362
-152
lines changed

src/Analyser/MutatingScope.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4370,7 +4370,7 @@ public function assignInitializedProperty(Type $fetchedOnType, string $propertyN
43704370
return $this;
43714371
}
43724372

4373-
$propertyReflection = $this->getPropertyReflection($fetchedOnType, $propertyName);
4373+
$propertyReflection = $this->getInstancePropertyReflection($fetchedOnType, $propertyName);
43744374
if ($propertyReflection === null) {
43754375
return $this;
43764376
}
@@ -6204,7 +6204,10 @@ private function methodCallReturnType(Type $typeWithMethod, string $methodName,
62046204
return $this->transformVoidToNull($parametersAcceptor->getReturnType(), $methodCall);
62056205
}
62066206

6207-
/** @api */
6207+
/**
6208+
* @api
6209+
* @deprecated Use getInstancePropertyReflection or getStaticPropertyReflection instead
6210+
*/
62086211
public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection
62096212
{
62106213
if ($typeWithProperty instanceof UnionType) {
@@ -6217,12 +6220,43 @@ public function getPropertyReflection(Type $typeWithProperty, string $propertyNa
62176220
return $typeWithProperty->getProperty($propertyName, $this);
62186221
}
62196222

6223+
/** @api */
6224+
public function getInstancePropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection
6225+
{
6226+
if ($typeWithProperty instanceof UnionType) {
6227+
$typeWithProperty = $typeWithProperty->filterTypes(static fn (Type $innerType) => $innerType->hasInstanceProperty($propertyName)->yes());
6228+
}
6229+
if (!$typeWithProperty->hasInstanceProperty($propertyName)->yes()) {
6230+
return null;
6231+
}
6232+
6233+
return $typeWithProperty->getInstanceProperty($propertyName, $this);
6234+
}
6235+
6236+
/** @api */
6237+
public function getStaticPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection
6238+
{
6239+
if ($typeWithProperty instanceof UnionType) {
6240+
$typeWithProperty = $typeWithProperty->filterTypes(static fn (Type $innerType) => $innerType->hasStaticProperty($propertyName)->yes());
6241+
}
6242+
if (!$typeWithProperty->hasStaticProperty($propertyName)->yes()) {
6243+
return null;
6244+
}
6245+
6246+
return $typeWithProperty->getStaticProperty($propertyName, $this);
6247+
}
6248+
62206249
/**
62216250
* @param PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch
62226251
*/
62236252
private function propertyFetchType(Type $fetchedOnType, string $propertyName, Expr $propertyFetch): ?Type
62246253
{
6225-
$propertyReflection = $this->getPropertyReflection($fetchedOnType, $propertyName);
6254+
if ($propertyFetch instanceof PropertyFetch) {
6255+
$propertyReflection = $this->getInstancePropertyReflection($fetchedOnType, $propertyName);
6256+
} else {
6257+
$propertyReflection = $this->getStaticPropertyReflection($fetchedOnType, $propertyName);
6258+
}
6259+
62266260
if ($propertyReflection === null) {
62276261
return null;
62286262
}

src/Analyser/NodeScopeResolver.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3216,7 +3216,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
32163216
} else {
32173217
$propertyName = $expr->name->toString();
32183218
$propertyHolderType = $scopeBeforeVar->getType($expr->var);
3219-
$propertyReflection = $scopeBeforeVar->getPropertyReflection($propertyHolderType, $propertyName);
3219+
$propertyReflection = $scopeBeforeVar->getInstancePropertyReflection($propertyHolderType, $propertyName);
32203220
if ($propertyReflection !== null && $this->phpVersion->supportsPropertyHooks()) {
32213221
$propertyDeclaringClass = $propertyReflection->getDeclaringClass();
32223222
if ($propertyDeclaringClass->hasNativeProperty($propertyName)) {
@@ -5898,8 +5898,8 @@ static function (): void {
58985898
}
58995899

59005900
$propertyHolderType = $scope->getType($var->var);
5901-
if ($propertyName !== null && $propertyHolderType->hasProperty($propertyName)->yes()) {
5902-
$propertyReflection = $propertyHolderType->getProperty($propertyName, $scope);
5901+
if ($propertyName !== null && $propertyHolderType->hasInstanceProperty($propertyName)->yes()) {
5902+
$propertyReflection = $propertyHolderType->getInstanceProperty($propertyName, $scope);
59035903
$assignedExprType = $scope->getType($assignedExpr);
59045904
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scopeBeforeAssignEval);
59055905
if ($propertyReflection->canChangeTypeAfterAssignment()) {
@@ -5991,7 +5991,7 @@ static function (): void {
59915991
$scope = $result->getScope();
59925992

59935993
if ($propertyName !== null) {
5994-
$propertyReflection = $scope->getPropertyReflection($propertyHolderType, $propertyName);
5994+
$propertyReflection = $scope->getStaticPropertyReflection($propertyHolderType, $propertyName);
59955995
$assignedExprType = $scope->getType($assignedExpr);
59965996
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scopeBeforeAssignEval);
59975997
if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) {

src/Analyser/Scope.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,13 @@ public function getMaybeDefinedVariables(): array;
7575

7676
public function hasConstant(Name $name): bool;
7777

78+
/** @deprecated Use getInstancePropertyReflection or getStaticPropertyReflection instead */
7879
public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection;
7980

81+
public function getInstancePropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection;
82+
83+
public function getStaticPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection;
84+
8085
public function getMethodReflection(Type $typeWithMethod, string $methodName): ?ExtendedMethodReflection;
8186

8287
public function getConstantReflection(Type $typeWithConstant, string $constantName): ?ClassConstantReflection;

src/Dependency/DependencyResolver.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public function resolveDependencies(Node $node, Scope $scope): NodeDependencies
261261
}
262262

263263
if ($node->name instanceof Node\Identifier) {
264-
$propertyReflection = $scope->getPropertyReflection($fetchedOnType, $node->name->toString());
264+
$propertyReflection = $scope->getInstancePropertyReflection($fetchedOnType, $node->name->toString());
265265
if ($propertyReflection !== null) {
266266
$this->addClassToDependencies($propertyReflection->getDeclaringClass()->getName(), $dependenciesReflections);
267267
}
@@ -377,13 +377,13 @@ public function resolveDependencies(Node $node, Scope $scope): NodeDependencies
377377
$className = $scope->resolveName($node->class);
378378
if ($this->reflectionProvider->hasClass($className)) {
379379
$propertyClassReflection = $this->reflectionProvider->getClass($className);
380-
if ($propertyClassReflection->hasProperty($node->name->toString())) {
381-
$propertyReflection = $propertyClassReflection->getProperty($node->name->toString(), $scope);
380+
if ($propertyClassReflection->hasStaticProperty($node->name->toString())) {
381+
$propertyReflection = $propertyClassReflection->getStaticProperty($node->name->toString());
382382
$this->addClassToDependencies($propertyReflection->getDeclaringClass()->getName(), $dependenciesReflections);
383383
}
384384
}
385385
} else {
386-
$propertyReflection = $scope->getPropertyReflection($scope->getType($node->class), $node->name->toString());
386+
$propertyReflection = $scope->getStaticPropertyReflection($scope->getType($node->class), $node->name->toString());
387387
if ($propertyReflection !== null) {
388388
$this->addClassToDependencies($propertyReflection->getDeclaringClass()->getName(), $dependenciesReflections);
389389
}

src/Node/ClassPropertiesNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public function getUninitializedProperties(
201201
continue;
202202
}
203203

204-
$propertyReflection = $usageScope->getPropertyReflection($fetchedOnType, $propertyName);
204+
$propertyReflection = $usageScope->getInstancePropertyReflection($fetchedOnType, $propertyName);
205205
if ($propertyReflection === null) {
206206
continue;
207207
}

src/Reflection/ClassReflection.php

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ final class ClassReflection
8383
/** @var ExtendedPropertyReflection[] */
8484
private array $properties = [];
8585

86+
/** @var ExtendedPropertyReflection[] */
87+
private array $instanceProperties = [];
88+
89+
/** @var ExtendedPropertyReflection[] */
90+
private array $staticProperties = [];
91+
8692
/** @var RealClassClassConstantReflection[] */
8793
private array $constants = [];
8894

@@ -149,6 +155,12 @@ final class ClassReflection
149155
/** @var array<string, bool> */
150156
private array $hasPropertyCache = [];
151157

158+
/** @var array<string, bool> */
159+
private array $hasInstancePropertyCache = [];
160+
161+
/** @var array<string, bool> */
162+
private array $hasStaticPropertyCache = [];
163+
152164
/**
153165
* @param PropertiesClassReflectionExtension[] $propertiesClassReflectionExtensions
154166
* @param MethodsClassReflectionExtension[] $methodsClassReflectionExtensions
@@ -449,6 +461,9 @@ public function allowsDynamicProperties(): bool
449461
return $attributes !== [];
450462
}
451463

464+
/**
465+
* @deprecated Use hasInstanceProperty or hasStaticProperty instead
466+
*/
452467
public function hasProperty(string $propertyName): bool
453468
{
454469
if (array_key_exists($propertyName, $this->hasPropertyCache)) {
@@ -468,13 +483,68 @@ public function hasProperty(string $propertyName): bool
468483
}
469484
}
470485

486+
// For BC purpose
487+
if ($this->getPhpExtension()->hasProperty($this, $propertyName)) {
488+
return $this->hasPropertyCache[$propertyName] = true;
489+
}
490+
471491
if ($this->requireExtendsPropertiesClassReflectionExtension->hasProperty($this, $propertyName)) {
472492
return $this->hasPropertyCache[$propertyName] = true;
473493
}
474494

475495
return $this->hasPropertyCache[$propertyName] = false;
476496
}
477497

498+
public function hasInstanceProperty(string $propertyName): bool
499+
{
500+
if (array_key_exists($propertyName, $this->hasInstancePropertyCache)) {
501+
return $this->hasInstancePropertyCache[$propertyName];
502+
}
503+
504+
if ($this->isEnum()) {
505+
return $this->hasInstancePropertyCache[$propertyName] = $this->hasNativeProperty($propertyName);
506+
}
507+
508+
foreach ($this->propertiesClassReflectionExtensions as $i => $extension) {
509+
if ($i > 0 && !$this->allowsDynamicProperties()) {
510+
break;
511+
}
512+
if ($extension->hasProperty($this, $propertyName)) {
513+
$property = $extension->getProperty($this, $propertyName);
514+
if ($property->isStatic()) {
515+
continue;
516+
}
517+
return $this->hasInstancePropertyCache[$propertyName] = true;
518+
}
519+
}
520+
521+
if ($this->requireExtendsPropertiesClassReflectionExtension->hasInstanceProperty($this, $propertyName)) {
522+
return $this->hasPropertyCache[$propertyName] = true;
523+
}
524+
525+
return $this->hasPropertyCache[$propertyName] = false;
526+
}
527+
528+
public function hasStaticProperty(string $propertyName): bool
529+
{
530+
if (array_key_exists($propertyName, $this->hasStaticPropertyCache)) {
531+
return $this->hasStaticPropertyCache[$propertyName];
532+
}
533+
534+
if ($this->getPhpExtension()->hasProperty($this, $propertyName)) {
535+
$property = $this->getPhpExtension()->getProperty($this, $propertyName);
536+
if ($property->isStatic()) {
537+
return $this->hasStaticPropertyCache[$propertyName] = true;
538+
}
539+
}
540+
541+
if ($this->requireExtendsPropertiesClassReflectionExtension->hasStaticProperty($this, $propertyName)) {
542+
return $this->hasStaticPropertyCache[$propertyName] = true;
543+
}
544+
545+
return $this->hasStaticPropertyCache[$propertyName] = false;
546+
}
547+
478548
public function hasMethod(string $methodName): bool
479549
{
480550
if (array_key_exists($methodName, $this->hasMethodCache)) {
@@ -619,6 +689,20 @@ public function evictPrivateSymbols(): void
619689

620690
unset($this->properties[$name]);
621691
}
692+
foreach ($this->instanceProperties as $name => $property) {
693+
if (!$property->isPrivate()) {
694+
continue;
695+
}
696+
697+
unset($this->instanceProperties[$name]);
698+
}
699+
foreach ($this->staticProperties as $name => $property) {
700+
if (!$property->isPrivate()) {
701+
continue;
702+
}
703+
704+
unset($this->staticProperties[$name]);
705+
}
622706
foreach ($this->methods as $name => $method) {
623707
if (!$method->isPrivate()) {
624708
continue;
@@ -629,6 +713,7 @@ public function evictPrivateSymbols(): void
629713
$this->getPhpExtension()->evictPrivateSymbols($this->getCacheKey());
630714
}
631715

716+
/** @deprecated Use getInstanceProperty or getStaticProperty */
632717
public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
633718
{
634719
if ($this->isEnum()) {
@@ -658,6 +743,13 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
658743
}
659744
}
660745

746+
// For BC purpose
747+
if ($this->getPhpExtension()->hasProperty($this, $propertyName)) {
748+
$property = $this->getPhpExtension()->getProperty($this, $propertyName);
749+
750+
return $this->properties[$key] = $property;
751+
}
752+
661753
if (!isset($this->properties[$key])) {
662754
if ($this->requireExtendsPropertiesClassReflectionExtension->hasProperty($this, $propertyName)) {
663755
$property = $this->requireExtendsPropertiesClassReflectionExtension->getProperty($this, $propertyName);
@@ -672,6 +764,79 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
672764
return $this->properties[$key];
673765
}
674766

767+
public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
768+
{
769+
if ($this->isEnum()) {
770+
return $this->getNativeProperty($propertyName);
771+
}
772+
773+
$key = $propertyName;
774+
if ($scope->isInClass()) {
775+
$key = sprintf('%s-%s', $key, $scope->getClassReflection()->getCacheKey());
776+
}
777+
778+
if (!isset($this->instanceProperties[$key])) {
779+
foreach ($this->propertiesClassReflectionExtensions as $i => $extension) {
780+
if ($i > 0 && !$this->allowsDynamicProperties()) {
781+
break;
782+
}
783+
784+
if (!$extension->hasProperty($this, $propertyName)) {
785+
continue;
786+
}
787+
788+
$nakedProperty = $extension->getProperty($this, $propertyName);
789+
if ($nakedProperty->isStatic()) {
790+
continue;
791+
}
792+
793+
$property = $this->wrapExtendedProperty($propertyName, $nakedProperty);
794+
if ($scope->canReadProperty($property)) {
795+
return $this->instanceProperties[$key] = $property;
796+
}
797+
$this->instanceProperties[$key] = $property;
798+
}
799+
}
800+
801+
if (!isset($this->instanceProperties[$key])) {
802+
if ($this->requireExtendsPropertiesClassReflectionExtension->hasInstanceProperty($this, $propertyName)) {
803+
$property = $this->requireExtendsPropertiesClassReflectionExtension->getInstanceProperty($this, $propertyName);
804+
$this->instanceProperties[$key] = $property;
805+
}
806+
}
807+
808+
if (!isset($this->instanceProperties[$key])) {
809+
throw new MissingPropertyFromReflectionException($this->getName(), $propertyName);
810+
}
811+
812+
return $this->instanceProperties[$key];
813+
}
814+
815+
public function getStaticProperty(string $propertyName): ExtendedPropertyReflection
816+
{
817+
$key = $propertyName;
818+
if (isset($this->staticProperties[$key])) {
819+
return $this->staticProperties[$key];
820+
}
821+
822+
if ($this->getPhpExtension()->hasProperty($this, $propertyName)) {
823+
$nakedProperty = $this->getPhpExtension()->getProperty($this, $propertyName);
824+
if ($nakedProperty->isStatic()) {
825+
$property = $this->wrapExtendedProperty($propertyName, $nakedProperty);
826+
if ($property->isStatic()) {
827+
return $this->staticProperties[$key] = $property;
828+
}
829+
}
830+
}
831+
832+
if ($this->requireExtendsPropertiesClassReflectionExtension->hasStaticProperty($this, $propertyName)) {
833+
$property = $this->requireExtendsPropertiesClassReflectionExtension->getStaticProperty($this, $propertyName);
834+
return $this->staticProperties[$key] = $property;
835+
}
836+
837+
throw new MissingPropertyFromReflectionException($this->getName(), $propertyName);
838+
}
839+
675840
public function hasNativeProperty(string $propertyName): bool
676841
{
677842
return $this->getPhpExtension()->hasProperty($this, $propertyName);

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,11 @@ public function getType(Expr $expr, InitializerExprContext $context): Type
408408

409409
if ($expr instanceof PropertyFetch && $expr->name instanceof Identifier) {
410410
$fetchedOnType = $this->getType($expr->var, $context);
411-
if (!$fetchedOnType->hasProperty($expr->name->name)->yes()) {
411+
if (!$fetchedOnType->hasInstanceProperty($expr->name->name)->yes()) {
412412
return new ErrorType();
413413
}
414414

415-
return $fetchedOnType->getProperty($expr->name->name, new OutOfClassScope())->getReadableType();
415+
return $fetchedOnType->getInstanceProperty($expr->name->name, new OutOfClassScope())->getReadableType();
416416
}
417417

418418
return new MixedType();

0 commit comments

Comments
 (0)