Skip to content

Commit 7004ae4

Browse files
authored
Merge branch 'phpstan:2.1.x' into mb_convert_encoding-can-return-false
2 parents 1d07b0e + 5bfe8f1 commit 7004ae4

File tree

53 files changed

+997
-196
lines changed

Some content is hidden

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

53 files changed

+997
-196
lines changed

Diff for: Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ lint:
9595
--exclude tests/PHPStan/Parser/data/cleaning-property-hooks-after.php \
9696
--exclude tests/PHPStan/Rules/Properties/data/existing-classes-property-hooks.php \
9797
--exclude tests/PHPStan/Rules/Properties/data/set-property-hook-parameter.php \
98+
--exclude tests/PHPStan/Rules/Properties/data/overriding-final-property.php \
9899
src tests
99100

100101
cs:

Diff for: conf/config.level0.neon

+1-3
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
@@ -179,9 +180,6 @@ services:
179180
class: PHPStan\Rules\Properties\AccessPropertiesRule
180181
tags:
181182
- phpstan.rules.rule
182-
arguments:
183-
reportMagicProperties: %reportMagicProperties%
184-
checkDynamicProperties: %checkDynamicProperties%
185183

186184
-
187185
class: PHPStan\Rules\Properties\AccessStaticPropertiesRule

Diff for: conf/config.neon

+6
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,12 @@ services:
10331033
-
10341034
class: PHPStan\Rules\Playground\NeverRuleHelper
10351035

1036+
-
1037+
class: PHPStan\Rules\Properties\AccessPropertiesCheck
1038+
arguments:
1039+
reportMagicProperties: %reportMagicProperties%
1040+
checkDynamicProperties: %checkDynamicProperties%
1041+
10361042
-
10371043
class: PHPStan\Rules\Properties\LazyReadWritePropertiesExtensionProvider
10381044

Diff for: src/Analyser/MutatingScope.php

+56-1
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
{

Diff for: src/Analyser/NodeScopeResolver.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -2993,7 +2993,7 @@ static function (): void {
29932993
$propertyName = $expr->name->toString();
29942994
$propertyHolderType = $scopeBeforeVar->getType($expr->var);
29952995
$propertyReflection = $scopeBeforeVar->getPropertyReflection($propertyHolderType, $propertyName);
2996-
if ($propertyReflection !== null) {
2996+
if ($propertyReflection !== null && $this->phpVersion->supportsPropertyHooks()) {
29972997
$propertyDeclaringClass = $propertyReflection->getDeclaringClass();
29982998
if ($propertyDeclaringClass->hasNativeProperty($propertyName)) {
29992999
$nativeProperty = $propertyDeclaringClass->getNativeProperty($propertyName);

Diff for: src/Analyser/OutOfClassScope.php

+13
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();

Diff for: src/Php/PhpVersion.php

+5
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;

Diff for: src/Reflection/Annotations/AnnotationPropertyReflection.php

+10
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
}

Diff for: src/Reflection/ClassMemberAccessAnswerer.php

+7
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;

Diff for: src/Reflection/ClassReflection.php

+1-1
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;

Diff for: src/Reflection/Dummy/ChangedTypePropertyReflection.php

+10
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
}

Diff for: src/Reflection/Dummy/DummyPropertyReflection.php

+10
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
}

Diff for: src/Reflection/ExtendedPropertyReflection.php

+4
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
}

Diff for: src/Reflection/Php/EnumPropertyReflection.php

+10
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
106106
throw new ShouldNotHappenException();
107107
}
108108

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

Diff for: src/Reflection/Php/PhpPropertyReflection.php

+38-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,14 @@ public function isAbstract(): TrinaryLogic
234234

235235
public function isFinal(): TrinaryLogic
236236
{
237-
return TrinaryLogic::createFromBoolean($this->reflection->isFinal());
237+
if ($this->reflection->isFinal()) {
238+
return TrinaryLogic::createYes();
239+
}
240+
if ($this->reflection->isPrivate()) {
241+
return TrinaryLogic::createNo();
242+
}
243+
244+
return TrinaryLogic::createFromBoolean($this->isPrivateSet());
238245
}
239246

240247
public function isVirtual(): TrinaryLogic
@@ -268,4 +275,34 @@ public function getHook(string $hookType): ExtendedMethodReflection
268275
return $this->setHook;
269276
}
270277

278+
public function isProtectedSet(): bool
279+
{
280+
if ($this->reflection->isProtectedSet()) {
281+
return true;
282+
}
283+
284+
if ($this->isReadOnly()) {
285+
return !$this->isPrivate() && !$this->reflection->isPrivateSet();
286+
}
287+
288+
return false;
289+
}
290+
291+
public function isPrivateSet(): bool
292+
{
293+
if ($this->reflection->isPrivateSet()) {
294+
return true;
295+
}
296+
297+
if ($this->reflection->isProtectedSet()) {
298+
return false;
299+
}
300+
301+
if ($this->isReadOnly()) {
302+
return $this->isPrivate();
303+
}
304+
305+
return false;
306+
}
307+
271308
}

Diff for: src/Reflection/Php/SimpleXMLElementProperty.php

+10
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
120120
throw new ShouldNotHappenException();
121121
}
122122

123+
public function isProtectedSet(): bool
124+
{
125+
return false;
126+
}
127+
128+
public function isPrivateSet(): bool
129+
{
130+
return false;
131+
}
132+
123133
}

Diff for: src/Reflection/Php/UniversalObjectCrateProperty.php

+10
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
110110
throw new ShouldNotHappenException();
111111
}
112112

113+
public function isProtectedSet(): bool
114+
{
115+
return false;
116+
}
117+
118+
public function isPrivateSet(): bool
119+
{
120+
return false;
121+
}
122+
113123
}

Diff for: src/Reflection/ResolvedPropertyReflection.php

+10
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
173173
);
174174
}
175175

176+
public function isProtectedSet(): bool
177+
{
178+
return $this->reflection->isProtectedSet();
179+
}
180+
181+
public function isPrivateSet(): bool
182+
{
183+
return $this->reflection->isPrivateSet();
184+
}
185+
176186
}

Diff for: src/Reflection/Type/IntersectionTypePropertyReflection.php

+10
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
160160
return new IntersectionTypeMethodReflection($hooks[0]->getName(), $hooks);
161161
}
162162

163+
public function isProtectedSet(): bool
164+
{
165+
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->isProtectedSet());
166+
}
167+
168+
public function isPrivateSet(): bool
169+
{
170+
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->isPrivateSet());
171+
}
172+
163173
}

Diff for: src/Reflection/Type/UnionTypePropertyReflection.php

+10
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
160160
return new UnionTypeMethodReflection($hooks[0]->getName(), $hooks);
161161
}
162162

163+
public function isProtectedSet(): bool
164+
{
165+
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->isProtectedSet());
166+
}
167+
168+
public function isPrivateSet(): bool
169+
{
170+
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->isPrivateSet());
171+
}
172+
163173
}

Diff for: src/Reflection/WrappedExtendedPropertyReflection.php

+10
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,14 @@ public function getHook(string $hookType): ExtendedMethodReflection
103103
throw new ShouldNotHappenException();
104104
}
105105

106+
public function isProtectedSet(): bool
107+
{
108+
return false;
109+
}
110+
111+
public function isPrivateSet(): bool
112+
{
113+
return false;
114+
}
115+
106116
}

0 commit comments

Comments
 (0)