Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 19, 2022
1 parent 22561e7 commit 5511155
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 72 deletions.
11 changes: 11 additions & 0 deletions src/Reflection/ClassReflection.php
Expand Up @@ -339,6 +339,17 @@ private function collectTraits(ReflectionClass|ReflectionEnum $class): array
return $traits;
}

public function allowsDynamicProperties(): bool
{
if (!$this->phpVersion->deprecatesDynamicProperties()) {
return true;
}

$attributes = $this->reflection->getAttributes('AllowDynamicProperties');

return count($attributes) > 0;
}

public function hasProperty(string $propertyName): bool
{
if ($this->isEnum()) {
Expand Down
12 changes: 1 addition & 11 deletions src/Rules/Properties/AccessPropertiesRule.php
Expand Up @@ -8,7 +8,6 @@
use PHPStan\Analyser\NullsafeOperatorHelper;
use PHPStan\Analyser\Scope;
use PHPStan\Internal\SprintfHelper;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
Expand All @@ -33,7 +32,6 @@ class AccessPropertiesRule implements Rule
public function __construct(
private ReflectionProvider $reflectionProvider,
private RuleLevelHelper $ruleLevelHelper,
private PhpVersion $phpVersion,
private bool $reportMagicProperties,
private bool $checkDynamicProperties,
)
Expand Down Expand Up @@ -167,15 +165,7 @@ private function processSingleProperty(Scope $scope, PropertyFetch $node, string

private function canAccessUndefinedProperties(Scope $scope, Node\Expr $node): bool
{
if (!$scope->isUndefinedExpressionAllowed($node)) {
return false;
}

if ($this->checkDynamicProperties) {
return false;
}

return !$this->phpVersion->deprecatesDynamicProperties();
return $scope->isUndefinedExpressionAllowed($node) && !$this->checkDynamicProperties;
}

}
6 changes: 5 additions & 1 deletion src/Type/ObjectType.php
Expand Up @@ -129,7 +129,11 @@ public function hasProperty(string $propertyName): TrinaryLogic
return TrinaryLogic::createYes();
}

return TrinaryLogic::createMaybe();
if ($classReflection->allowsDynamicProperties()) {
return TrinaryLogic::createMaybe();
}

return TrinaryLogic::createNo();
}

public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): PropertyReflection
Expand Down
Expand Up @@ -2,7 +2,6 @@

namespace PHPStan\Rules\Properties;

use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
Expand All @@ -18,7 +17,7 @@ protected function getRule(): Rule
{
$reflectionProvider = $this->createReflectionProvider();
return new AccessPropertiesInAssignRule(
new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, false, true, false), new PhpVersion(PHP_VERSION_ID), true, true),
new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, false, true, false), true, true),
);
}

Expand Down
86 changes: 28 additions & 58 deletions tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php
Expand Up @@ -2,7 +2,6 @@

namespace PHPStan\Rules\Properties;

use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
Expand All @@ -20,20 +19,17 @@ class AccessPropertiesRuleTest extends RuleTestCase

private bool $checkDynamicProperties;

private int $phpVersionId;

protected function getRule(): Rule
{
$reflectionProvider = $this->createReflectionProvider();
return new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, $this->checkUnionTypes, false), new PhpVersion($this->phpVersionId), true, $this->checkDynamicProperties);
return new AccessPropertiesRule($reflectionProvider, new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, $this->checkUnionTypes, false), true, $this->checkDynamicProperties);
}

public function testAccessProperties(): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse(
[__DIR__ . '/data/access-properties.php'],
[
Expand Down Expand Up @@ -165,7 +161,6 @@ public function testAccessPropertiesWithoutUnionTypes(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = false;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse(
[__DIR__ . '/data/access-properties.php'],
[
Expand Down Expand Up @@ -280,7 +275,6 @@ public function testRuleAssignOp(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/access-properties-assign-op.php'], [
[
'Access to an undefined property TestAccessProperties\AssignOpNonexistentProperty::$flags.',
Expand All @@ -294,7 +288,6 @@ public function testAccessPropertiesOnThisOnly(): void
$this->checkThisOnly = true;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse(
[__DIR__ . '/data/access-properties.php'],
[
Expand All @@ -315,7 +308,6 @@ public function testAccessPropertiesAfterIsNullInBooleanOr(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/access-properties-after-isnull.php'], [
[
'Cannot access property $fooProperty on null.',
Expand Down Expand Up @@ -357,7 +349,6 @@ public function testDateIntervalChildProperties(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/date-interval-child-properties.php'], [
[
'Access to an undefined property AccessPropertiesDateIntervalChild\DateIntervalChild::$nonexistent.',
Expand All @@ -371,7 +362,6 @@ public function testClassExists(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;

$this->analyse([__DIR__ . '/data/access-properties-class-exists.php'], [
[
Expand Down Expand Up @@ -402,7 +392,6 @@ public function testMixin(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/mixin.php'], [
[
'Access to an undefined property MixinProperties\GenericFoo<ReflectionClass>::$namee.',
Expand All @@ -416,7 +405,6 @@ public function testBug3947(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/bug-3947.php'], []);
}

Expand All @@ -425,7 +413,6 @@ public function testNullSafe(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;

$this->analyse([__DIR__ . '/data/nullsafe-property-fetch.php'], [
[
Expand Down Expand Up @@ -456,7 +443,6 @@ public function testBug3371(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/bug-3371.php'], []);
}

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

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

Expand All @@ -486,7 +470,6 @@ public function testBug5868(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/bug-5868.php'], [
[
'Cannot access property $child on Bug5868PropertyFetch\Foo|null.',
Expand Down Expand Up @@ -516,7 +499,6 @@ public function testBug6385(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/bug-6385.php'], [
[
'Access to an undefined property UnitEnum::$value.',
Expand All @@ -537,7 +519,6 @@ public function testBug6566(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$this->analyse([__DIR__ . '/data/bug-6566.php'], []);
}

Expand All @@ -546,7 +527,6 @@ public function testBug6899(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$errors = [
[
'Cannot access property $prop on string.',
Expand All @@ -561,20 +541,6 @@ public function testBug6899(): void
15,
],
];
if (PHP_VERSION_ID >= 80200) {
$errors[] = [
'Access to an undefined property object|string::$prop.',
24,
];
$errors[] = [
'Access to an undefined property object|string::$prop.',
25,
];
$errors[] = [
'Access to an undefined property object|string::$prop.',
26,
];
}
$this->analyse([__DIR__ . '/data/bug-6899.php'], $errors);
}

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

Expand All @@ -592,14 +557,7 @@ public function testBug3659(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$errors = [];
if (PHP_VERSION_ID >= 80200) {
$errors[] = [
'Access to an undefined property object::$someProperty.',
9,
];
}
$this->analyse([__DIR__ . '/data/bug-3659.php'], $errors);
}

Expand Down Expand Up @@ -630,26 +588,47 @@ public function dataDynamicProperties(): array
'Access to an undefined property DynamicProperties\Bar::$dynamicProperty.',
16,
],
[
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
23,
],
];

if (PHP_VERSION_ID < 80200) {
$errors[] = [
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
26,
];
$errors[] = [
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
27,
];
$errors[] = [
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
28,
];
}

return [
[false, 80000, []],
[true, 80000, $errors],
[false, 80200, $errors],
[true, 80200, $errors],
[false, PHP_VERSION_ID < 80200 ? [
[
'Access to an undefined property DynamicProperties\Baz::$dynamicProperty.',
23,
],
] : $errors],
[true, $errors],
];
}

/**
* @dataProvider dataDynamicProperties
* @param mixed[] $errors
*/
public function testDynamicProperties(bool $checkDynamicProperties, int $phpVersionId, array $errors): void
public function testDynamicProperties(bool $checkDynamicProperties, array $errors): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = $checkDynamicProperties;
$this->phpVersionId = $phpVersionId;
$this->analyse([__DIR__ . '/data/dynamic-properties.php'], $errors);
}

Expand All @@ -658,14 +637,7 @@ public function testBug4559(): void
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->checkDynamicProperties = false;
$this->phpVersionId = PHP_VERSION_ID;
$errors = [];
if (PHP_VERSION_ID >= 80200) {
$errors[] = [
'Access to an undefined property object::$message.',
11,
];
}
$this->analyse([__DIR__ . '/data/bug-4559.php'], $errors);
}

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

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

Expand Down
12 changes: 12 additions & 0 deletions tests/PHPStan/Rules/Properties/data/dynamic-properties.php
Expand Up @@ -17,3 +17,15 @@ public function doBar() {
}
}

#[\AllowDynamicProperties]
class Baz {
public function doBaz() {
echo $this->dynamicProperty;
}
public function doBar() {
isset($this->dynamicProperty);
empty($this->dynamicProperty);
$this->dynamicProperty ?? 'test';
}
}

0 comments on commit 5511155

Please sign in to comment.