Skip to content

Commit c34432b

Browse files
committed
Asymmetric visibility basics
1 parent 9b86df9 commit c34432b

File tree

45 files changed

+688
-32
lines changed

Some content is hidden

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

45 files changed

+688
-32
lines changed

conf/config.level0.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ rules:
9797
- PHPStan\Rules\Properties\MissingReadOnlyPropertyAssignRule
9898
- PHPStan\Rules\Properties\MissingReadOnlyByPhpDocPropertyAssignRule
9999
- PHPStan\Rules\Properties\PropertiesInInterfaceRule
100+
- PHPStan\Rules\Properties\PropertyAssignRefRule
100101
- PHPStan\Rules\Properties\PropertyAttributesRule
101102
- PHPStan\Rules\Properties\PropertyHookAttributesRule
102103
- PHPStan\Rules\Properties\PropertyInClassRule

src/Analyser/MutatingScope.php

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5381,12 +5381,67 @@ private function getBooleanExpressionDepth(Expr $expr, int $depth = 0): int
53815381
return $depth;
53825382
}
53835383

5384-
/** @api */
5384+
/**
5385+
* @api
5386+
* @deprecated Use canReadProperty() or canWriteProperty()
5387+
*/
53855388
public function canAccessProperty(PropertyReflection $propertyReflection): bool
53865389
{
53875390
return $this->canAccessClassMember($propertyReflection);
53885391
}
53895392

5393+
/** @api */
5394+
public function canReadProperty(ExtendedPropertyReflection $propertyReflection): bool
5395+
{
5396+
return $this->canAccessClassMember($propertyReflection);
5397+
}
5398+
5399+
/** @api */
5400+
public function canWriteProperty(ExtendedPropertyReflection $propertyReflection): bool
5401+
{
5402+
if (!$propertyReflection->isPrivateSet() && !$propertyReflection->isProtectedSet()) {
5403+
return $this->canAccessClassMember($propertyReflection);
5404+
}
5405+
5406+
if (!$this->phpVersion->supportsAsymmetricVisibility()) {
5407+
return $this->canAccessClassMember($propertyReflection);
5408+
}
5409+
5410+
$classReflectionName = $propertyReflection->getDeclaringClass()->getName();
5411+
$canAccessClassMember = static function (ClassReflection $classReflection) use ($propertyReflection, $classReflectionName) {
5412+
if ($propertyReflection->isPrivateSet()) {
5413+
return $classReflection->getName() === $classReflectionName;
5414+
}
5415+
5416+
// protected set
5417+
5418+
if (
5419+
$classReflection->getName() === $classReflectionName
5420+
|| $classReflection->isSubclassOf($classReflectionName)
5421+
) {
5422+
return true;
5423+
}
5424+
5425+
return $propertyReflection->getDeclaringClass()->isSubclassOf($classReflection->getName());
5426+
};
5427+
5428+
foreach ($this->inClosureBindScopeClasses as $inClosureBindScopeClass) {
5429+
if (!$this->reflectionProvider->hasClass($inClosureBindScopeClass)) {
5430+
continue;
5431+
}
5432+
5433+
if ($canAccessClassMember($this->reflectionProvider->getClass($inClosureBindScopeClass))) {
5434+
return true;
5435+
}
5436+
}
5437+
5438+
if ($this->isInClass()) {
5439+
return $canAccessClassMember($this->getClassReflection());
5440+
}
5441+
5442+
return false;
5443+
}
5444+
53905445
/** @api */
53915446
public function canCallMethod(MethodReflection $methodReflection): bool
53925447
{

src/Analyser/OutOfClassScope.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\Reflection\ClassConstantReflection;
66
use PHPStan\Reflection\ClassMemberAccessAnswerer;
77
use PHPStan\Reflection\ClassReflection;
8+
use PHPStan\Reflection\ExtendedPropertyReflection;
89
use PHPStan\Reflection\MethodReflection;
910
use PHPStan\Reflection\PropertyReflection;
1011

@@ -31,6 +32,18 @@ public function canAccessProperty(PropertyReflection $propertyReflection): bool
3132
return $propertyReflection->isPublic();
3233
}
3334

35+
public function canReadProperty(ExtendedPropertyReflection $propertyReflection): bool
36+
{
37+
return $propertyReflection->isPublic();
38+
}
39+
40+
public function canWriteProperty(ExtendedPropertyReflection $propertyReflection): bool
41+
{
42+
return $propertyReflection->isPublic()
43+
&& !$propertyReflection->isProtectedSet()
44+
&& !$propertyReflection->isPrivateSet();
45+
}
46+
3447
public function canCallMethod(MethodReflection $methodReflection): bool
3548
{
3649
return $methodReflection->isPublic();

src/Php/PhpVersion.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,11 @@ public function supportsPropertyHooks(): bool
357357
return $this->versionId >= 80400;
358358
}
359359

360+
public function supportsAsymmetricVisibility(): bool
361+
{
362+
return $this->versionId >= 80400;
363+
}
364+
360365
public function hasDateTimeExceptions(): bool
361366
{
362367
return $this->versionId >= 80300;

src/Reflection/Annotations/AnnotationPropertyReflection.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
112112
throw new ShouldNotHappenException();
113113
}
114114

115+
public function isProtectedSet(): bool
116+
{
117+
return false;
118+
}
119+
120+
public function isPrivateSet(): bool
121+
{
122+
return false;
123+
}
124+
115125
}

src/Reflection/ClassMemberAccessAnswerer.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@ public function isInClass(): bool;
1313

1414
public function getClassReflection(): ?ClassReflection;
1515

16+
/**
17+
* @deprecated Use canReadProperty() or canWriteProperty()
18+
*/
1619
public function canAccessProperty(PropertyReflection $propertyReflection): bool;
1720

21+
public function canReadProperty(ExtendedPropertyReflection $propertyReflection): bool;
22+
23+
public function canWriteProperty(ExtendedPropertyReflection $propertyReflection): bool;
24+
1825
public function canCallMethod(MethodReflection $methodReflection): bool;
1926

2027
public function canAccessConstant(ClassConstantReflection $constantReflection): bool;

src/Reflection/ClassReflection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
642642
}
643643

644644
$property = $this->wrapExtendedProperty($extension->getProperty($this, $propertyName));
645-
if ($scope->canAccessProperty($property)) {
645+
if ($scope->canReadProperty($property)) {
646646
return $this->properties[$key] = $property;
647647
}
648648
$this->properties[$key] = $property;

src/Reflection/Dummy/ChangedTypePropertyReflection.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
111111
return $this->reflection->getHook($hookType);
112112
}
113113

114+
public function isProtectedSet(): bool
115+
{
116+
return $this->reflection->isProtectedSet();
117+
}
118+
119+
public function isPrivateSet(): bool
120+
{
121+
return $this->reflection->isPrivateSet();
122+
}
123+
114124
}

src/Reflection/Dummy/DummyPropertyReflection.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
107107
throw new ShouldNotHappenException();
108108
}
109109

110+
public function isProtectedSet(): bool
111+
{
112+
return false;
113+
}
114+
115+
public function isPrivateSet(): bool
116+
{
117+
return false;
118+
}
119+
110120
}

src/Reflection/ExtendedPropertyReflection.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@ public function hasHook(string $hookType): bool;
4141
*/
4242
public function getHook(string $hookType): ExtendedMethodReflection;
4343

44+
public function isProtectedSet(): bool;
45+
46+
public function isPrivateSet(): bool;
47+
4448
}

0 commit comments

Comments
 (0)