Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5511155

Browse files
Fix
1 parent 22561e7 commit 5511155

File tree

6 files changed

+58
-72
lines changed

6 files changed

+58
-72
lines changed

‎src/Reflection/ClassReflection.php‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,17 @@ private function collectTraits(ReflectionClass|ReflectionEnum $class): array
339339
return $traits;
340340
}
341341

342+
public function allowsDynamicProperties(): bool
343+
{
344+
if (!$this->phpVersion->deprecatesDynamicProperties()) {
345+
return true;
346+
}
347+
348+
$attributes = $this->reflection->getAttributes('AllowDynamicProperties');
349+
350+
return count($attributes) > 0;
351+
}
352+
342353
public function hasProperty(string $propertyName): bool
343354
{
344355
if ($this->isEnum()) {

‎src/Rules/Properties/AccessPropertiesRule.php‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PHPStan\Analyser\NullsafeOperatorHelper;
99
use PHPStan\Analyser\Scope;
1010
use PHPStan\Internal\SprintfHelper;
11-
use PHPStan\Php\PhpVersion;
1211
use PHPStan\Reflection\ReflectionProvider;
1312
use PHPStan\Rules\Rule;
1413
use PHPStan\Rules\RuleError;
@@ -33,7 +32,6 @@ class AccessPropertiesRule implements Rule
3332
public function __construct(
3433
private ReflectionProvider $reflectionProvider,
3534
private RuleLevelHelper $ruleLevelHelper,
36-
private PhpVersion $phpVersion,
3735
private bool $reportMagicProperties,
3836
private bool $checkDynamicProperties,
3937
)
@@ -167,15 +165,7 @@ private function processSingleProperty(Scope $scope, PropertyFetch $node, string
167165

168166
private function canAccessUndefinedProperties(Scope $scope, Node\Expr $node): bool
169167
{
170-
if (!$scope->isUndefinedExpressionAllowed($node)) {
171-
return false;
172-
}
173-
174-
if ($this->checkDynamicProperties) {
175-
return false;
176-
}
177-
178-
return !$this->phpVersion->deprecatesDynamicProperties();
168+
return $scope->isUndefinedExpressionAllowed($node) && !$this->checkDynamicProperties;
179169
}
180170

181171
}

‎src/Type/ObjectType.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,11 @@ public function hasProperty(string $propertyName): TrinaryLogic
129129
return TrinaryLogic::createYes();
130130
}
131131

132-
return TrinaryLogic::createMaybe();
132+
if ($classReflection->allowsDynamicProperties()) {
133+
return TrinaryLogic::createMaybe();
134+
}
135+
136+
return TrinaryLogic::createNo();
133137
}
134138

135139
public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): PropertyReflection

‎tests/PHPStan/Rules/Properties/AccessPropertiesInAssignRuleTest.php‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace PHPStan\Rules\Properties;
44

5-
use PHPStan\Php\PhpVersion;
65
use PHPStan\Rules\Rule;
76
use PHPStan\Rules\RuleLevelHelper;
87
use PHPStan\Testing\RuleTestCase;
@@ -18,7 +17,7 @@ protected function getRule(): Rule
1817
{
1918
$reflectionProvider = $this->createReflectionProvider();
2019
return new AccessPropertiesInAssignRule(
21-
new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, false, true, false), newPhpVersion(PHP_VERSION_ID), true, true),
20+
new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, false, true, false), true, true),
2221
);
2322
}
2423

‎tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php‎

Lines changed: 28 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace PHPStan\Rules\Properties;
44

5-
use PHPStan\Php\PhpVersion;
65
use PHPStan\Rules\Rule;
76
use PHPStan\Rules\RuleLevelHelper;
87
use PHPStan\Testing\RuleTestCase;
@@ -20,20 +19,17 @@ class AccessPropertiesRuleTest extends RuleTestCase
2019

2120
private bool $checkDynamicProperties;
2221

23-
private int $phpVersionId;
24-
2522
protected function getRule(): Rule
2623
{
2724
$reflectionProvider = $this->createReflectionProvider();
28-
return new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, $this->checkUnionTypes, false), newPhpVersion($this->phpVersionId), true, $this->checkDynamicProperties);
25+
return new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, $this->checkUnionTypes, false), true, $this->checkDynamicProperties);
2926
}
3027

3128
public function testAccessProperties(): void
3229
{
3330
$this->checkThisOnly = false;
3431
$this->checkUnionTypes = true;
3532
$this->checkDynamicProperties = false;
36-
$this->phpVersionId = PHP_VERSION_ID;
3733
$this->analyse(
3834
[__DIR__ . '/data/access-properties.php'],
3935
[
@@ -165,7 +161,6 @@ public function testAccessPropertiesWithoutUnionTypes(): void
165161
$this->checkThisOnly = false;
166162
$this->checkUnionTypes = false;
167163
$this->checkDynamicProperties = false;
168-
$this->phpVersionId = PHP_VERSION_ID;
169164
$this->analyse(
170165
[__DIR__ . '/data/access-properties.php'],
171166
[
@@ -280,7 +275,6 @@ public function testRuleAssignOp(): void
280275
$this->checkThisOnly = false;
281276
$this->checkUnionTypes = true;
282277
$this->checkDynamicProperties = false;
283-
$this->phpVersionId = PHP_VERSION_ID;
284278
$this->analyse([__DIR__ . '/data/access-properties-assign-op.php'], [
285279
[
286280
'Access to an undefined property TestAccessProperties\AssignOpNonexistentProperty::$flags.',
@@ -294,7 +288,6 @@ public function testAccessPropertiesOnThisOnly(): void
294288
$this->checkThisOnly = true;
295289
$this->checkUnionTypes = true;
296290
$this->checkDynamicProperties = false;
297-
$this->phpVersionId = PHP_VERSION_ID;
298291
$this->analyse(
299292
[__DIR__ . '/data/access-properties.php'],
300293
[
@@ -315,7 +308,6 @@ public function testAccessPropertiesAfterIsNullInBooleanOr(): void
315308
$this->checkThisOnly = false;
316309
$this->checkUnionTypes = true;
317310
$this->checkDynamicProperties = false;
318-
$this->phpVersionId = PHP_VERSION_ID;
319311
$this->analyse([__DIR__ . '/data/access-properties-after-isnull.php'], [
320312
[
321313
'Cannot access property $fooProperty on null.',
@@ -357,7 +349,6 @@ public function testDateIntervalChildProperties(): void
357349
$this->checkThisOnly = false;
358350
$this->checkUnionTypes = true;
359351
$this->checkDynamicProperties = false;
360-
$this->phpVersionId = PHP_VERSION_ID;
361352
$this->analyse([__DIR__ . '/data/date-interval-child-properties.php'], [
362353
[
363354
'Access to an undefined property AccessPropertiesDateIntervalChild\DateIntervalChild::$nonexistent.',
@@ -371,7 +362,6 @@ public function testClassExists(): void
371362
$this->checkThisOnly = false;
372363
$this->checkUnionTypes = true;
373364
$this->checkDynamicProperties = false;
374-
$this->phpVersionId = PHP_VERSION_ID;
375365

376366
$this->analyse([__DIR__ . '/data/access-properties-class-exists.php'], [
377367
[
@@ -402,7 +392,6 @@ public function testMixin(): void
402392
$this->checkThisOnly = false;
403393
$this->checkUnionTypes = true;
404394
$this->checkDynamicProperties = false;
405-
$this->phpVersionId = PHP_VERSION_ID;
406395
$this->analyse([__DIR__ . '/data/mixin.php'], [
407396
[
408397
'Access to an undefined property MixinProperties\GenericFoo<ReflectionClass>::$namee.',
@@ -416,7 +405,6 @@ public function testBug3947(): void
416405
$this->checkThisOnly = false;
417406
$this->checkUnionTypes = true;
418407
$this->checkDynamicProperties = false;
419-
$this->phpVersionId = PHP_VERSION_ID;
420408
$this->analyse([__DIR__ . '/data/bug-3947.php'], []);
421409
}
422410

@@ -425,7 +413,6 @@ public function testNullSafe(): void
425413
$this->checkThisOnly = false;
426414
$this->checkUnionTypes = true;
427415
$this->checkDynamicProperties = false;
428-
$this->phpVersionId = PHP_VERSION_ID;
429416

430417
$this->analyse([__DIR__ . '/data/nullsafe-property-fetch.php'], [
431418
[
@@ -456,7 +443,6 @@ public function testBug3371(): void
456443
$this->checkThisOnly = false;
457444
$this->checkUnionTypes = true;
458445
$this->checkDynamicProperties = false;
459-
$this->phpVersionId = PHP_VERSION_ID;
460446
$this->analyse([__DIR__ . '/data/bug-3371.php'], []);
461447
}
462448

@@ -465,7 +451,6 @@ public function testBug4527(): void
465451
$this->checkThisOnly = false;
466452
$this->checkUnionTypes = true;
467453
$this->checkDynamicProperties = false;
468-
$this->phpVersionId = PHP_VERSION_ID;
469454
$this->analyse([__DIR__ . '/data/bug-4527.php'], []);
470455
}
471456

@@ -474,7 +459,6 @@ public function testBug4808(): void
474459
$this->checkThisOnly = false;
475460
$this->checkUnionTypes = true;
476461
$this->checkDynamicProperties = false;
477-
$this->phpVersionId = PHP_VERSION_ID;
478462
$this->analyse([__DIR__ . '/data/bug-4808.php'], []);
479463
}
480464

@@ -486,7 +470,6 @@ public function testBug5868(): void
486470
$this->checkThisOnly = false;
487471
$this->checkUnionTypes = true;
488472
$this->checkDynamicProperties = false;
489-
$this->phpVersionId = PHP_VERSION_ID;
490473
$this->analyse([__DIR__ . '/data/bug-5868.php'], [
491474
[
492475
'Cannot access property $child on Bug5868PropertyFetch\Foo|null.',
@@ -516,7 +499,6 @@ public function testBug6385(): void
516499
$this->checkThisOnly = false;
517500
$this->checkUnionTypes = true;
518501
$this->checkDynamicProperties = false;
519-
$this->phpVersionId = PHP_VERSION_ID;
520502
$this->analyse([__DIR__ . '/data/bug-6385.php'], [
521503
[
522504
'Access to an undefined property UnitEnum::$value.',
@@ -537,7 +519,6 @@ public function testBug6566(): void
537519
$this->checkThisOnly = false;
538520
$this->checkUnionTypes = true;
539521
$this->checkDynamicProperties = false;
540-
$this->phpVersionId = PHP_VERSION_ID;
541522
$this->analyse([__DIR__ . '/data/bug-6566.php'], []);
542523
}
543524

@@ -546,7 +527,6 @@ public function testBug6899(): void
546527
$this->checkThisOnly = false;
547528
$this->checkUnionTypes = true;
548529
$this->checkDynamicProperties = false;
549-
$this->phpVersionId = PHP_VERSION_ID;
550530
$errors = [
551531
[
552532
'Cannot access property $prop on string.',
@@ -561,20 +541,6 @@ public function testBug6899(): void
561541
15,
562542
],
563543
];
564-
if (PHP_VERSION_ID >= 80200) {
565-
$errors[] = [
566-
'Access to an undefined property object|string::$prop.',
567-
24,
568-
];
569-
$errors[] = [
570-
'Access to an undefined property object|string::$prop.',
571-
25,
572-
];
573-
$errors[] = [
574-
'Access to an undefined property object|string::$prop.',
575-
26,
576-
];
577-
}
578544
$this->analyse([__DIR__ . '/data/bug-6899.php'], $errors);
579545
}
580546

@@ -583,7 +549,6 @@ public function testBug6026(): void
583549
$this->checkThisOnly = false;
584550
$this->checkUnionTypes = true;
585551
$this->checkDynamicProperties = false;
586-
$this->phpVersionId = PHP_VERSION_ID;
587552
$this->analyse([__DIR__ . '/data/bug-6026.php'], []);
588553
}
589554

@@ -592,14 +557,7 @@ public function testBug3659(): void
592557
$this->checkThisOnly = false;
593558
$this->checkUnionTypes = true;
594559
$this->checkDynamicProperties = false;
595-
$this->phpVersionId = PHP_VERSION_ID;
596560
$errors = [];
597-
if (PHP_VERSION_ID >= 80200) {
598-
$errors[] = [
599-
'Access to an undefined property object::$someProperty.',
600-
9,
601-
];
602-
}
603561
$this->analyse([__DIR__ . '/data/bug-3659.php'], $errors);
604562
}
605563

@@ -630,26 +588,47 @@ public function dataDynamicProperties(): array
630588
'Access to an undefined property DynamicProperties\Bar::$dynamicProperty.',
631589
16,
632590
],
591+
[
592+
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
593+
23,
594+
],
633595
];
634596

597+
if (PHP_VERSION_ID < 80200) {
598+
$errors[] = [
599+
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
600+
26,
601+
];
602+
$errors[] = [
603+
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
604+
27,
605+
];
606+
$errors[] = [
607+
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
608+
28,
609+
];
610+
}
611+
635612
return [
636-
[false, 80000, []],
637-
[true, 80000, $errors],
638-
[false, 80200, $errors],
639-
[true, 80200, $errors],
613+
[false, PHP_VERSION_ID < 80200 ? [
614+
[
615+
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
616+
23,
617+
],
618+
] : $errors],
619+
[true, $errors],
640620
];
641621
}
642622

643623
/**
644624
* @dataProvider dataDynamicProperties
645625
* @param mixed[] $errors
646626
*/
647-
public function testDynamicProperties(bool $checkDynamicProperties, int$phpVersionId, array $errors): void
627+
public function testDynamicProperties(bool $checkDynamicProperties, array $errors): void
648628
{
649629
$this->checkThisOnly = false;
650630
$this->checkUnionTypes = true;
651631
$this->checkDynamicProperties = $checkDynamicProperties;
652-
$this->phpVersionId = $phpVersionId;
653632
$this->analyse([__DIR__ . '/data/dynamic-properties.php'], $errors);
654633
}
655634

@@ -658,14 +637,7 @@ public function testBug4559(): void
658637
$this->checkThisOnly = false;
659638
$this->checkUnionTypes = true;
660639
$this->checkDynamicProperties = false;
661-
$this->phpVersionId = PHP_VERSION_ID;
662640
$errors = [];
663-
if (PHP_VERSION_ID >= 80200) {
664-
$errors[] = [
665-
'Access to an undefined property object::$message.',
666-
11,
667-
];
668-
}
669641
$this->analyse([__DIR__ . '/data/bug-4559.php'], $errors);
670642
}
671643

@@ -674,7 +646,6 @@ public function testBug3171(): void
674646
$this->checkThisOnly = false;
675647
$this->checkUnionTypes = true;
676648
$this->checkDynamicProperties = false;
677-
$this->phpVersionId = PHP_VERSION_ID;
678649
$this->analyse([__DIR__ . '/data/bug-3171.php'], []);
679650
}
680651

@@ -683,7 +654,6 @@ public function testBug3171OnDynamicProperties(): void
683654
$this->checkThisOnly = false;
684655
$this->checkUnionTypes = true;
685656
$this->checkDynamicProperties = true;
686-
$this->phpVersionId = PHP_VERSION_ID;
687657
$this->analyse([__DIR__ . '/data/bug-3171.php'], []);
688658
}
689659

‎tests/PHPStan/Rules/Properties/data/dynamic-properties.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,15 @@ public function doBar() {
1717
}
1818
}
1919

20+
#[\AllowDynamicProperties]
21+
class Baz {
22+
public function doBaz() {
23+
echo $this->dynamicProperty;
24+
}
25+
public function doBar() {
26+
isset($this->dynamicProperty);
27+
empty($this->dynamicProperty);
28+
$this->dynamicProperty ?? 'test';
29+
}
30+
}
31+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /