Skip to content

Commit 99380da

Browse files
committed
Allow write access to offsets of readonly ArrayAccess properties
In case a readonly property is an object which implements ArrayAccess, offet accesses to said property are turned into method calls on PHP's side and are allowed even on read-only properties. this fixes phpstan/phpstan#8929
1 parent f436584 commit 99380da

File tree

3 files changed

+34
-0
lines changed

3 files changed

+34
-0
lines changed

src/Rules/Properties/ReadOnlyPropertyAssignRule.php

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\Properties;
44

5+
use ArrayAccess;
56
use PhpParser\Node;
67
use PHPStan\Analyser\Scope;
78
use PHPStan\Node\PropertyAssignNode;
@@ -10,6 +11,7 @@
1011
use PHPStan\Rules\Rule;
1112
use PHPStan\Rules\RuleErrorBuilder;
1213
use PHPStan\ShouldNotHappenException;
14+
use PHPStan\Type\ObjectType;
1315
use PHPStan\Type\TypeUtils;
1416
use function in_array;
1517
use function sprintf;
@@ -89,6 +91,10 @@ public function processNode(Node $node, Scope $scope): array
8991
continue;
9092
}
9193

94+
if ((new ObjectType(ArrayAccess::class))->isSuperTypeOf($propertyReflection->getNativeType())->yes()) {
95+
continue;
96+
}
97+
9298
$errors[] = RuleErrorBuilder::message(sprintf('Readonly property %s::$%s is assigned outside of the constructor.', $declaringClass->getDisplayName(), $propertyReflection->getName()))
9399
->identifier('property.readOnlyAssignNotInConstructor')
94100
->build();

tests/PHPStan/Rules/Properties/ReadOnlyPropertyAssignRuleTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,13 @@ public function testBug6773(): void
168168
]);
169169
}
170170

171+
public function testBug8929(): void
172+
{
173+
if (PHP_VERSION_ID < 80100) {
174+
self::markTestSkipped('Test requires PHP 8.1');
175+
}
176+
177+
$this->analyse([__DIR__ . '/data/bug-8929.php'], []);
178+
}
179+
171180
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php declare(strict_types = 1); // lint >= 8.1
2+
3+
namespace Bug8929;
4+
5+
class Test
6+
{
7+
/** @var \WeakMap<object, mixed> */
8+
protected readonly \WeakMap $cache;
9+
10+
public function __construct()
11+
{
12+
$this->cache = new \WeakMap();
13+
}
14+
15+
public function add(object $key, mixed $value): void
16+
{
17+
$this->cache[$key] = $value;
18+
}
19+
}

0 commit comments

Comments
 (0)