Skip to content

Commit dff492e

Browse files
committed
Invalidate result cache properly when property hook changes
1 parent 795cc66 commit dff492e

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

src/Dependency/ExportedNode/ExportedPropertiesNode.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ final class ExportedPropertiesNode implements JsonSerializable, ExportedNode
1515
/**
1616
* @param string[] $names
1717
* @param ExportedAttributeNode[] $attributes
18+
* @param ExportedPropertyHookNode[] $hooks
1819
*/
1920
public function __construct(
2021
private array $names,
@@ -25,6 +26,7 @@ public function __construct(
2526
private bool $static,
2627
private bool $readonly,
2728
private array $attributes,
29+
private array $hooks,
2830
)
2931
{
3032
}
@@ -67,6 +69,16 @@ public function equals(ExportedNode $node): bool
6769
}
6870
}
6971

72+
if (count($this->hooks) !== count($node->hooks)) {
73+
return false;
74+
}
75+
76+
foreach ($this->hooks as $i => $hook) {
77+
if (!$hook->equals($node->hooks[$i])) {
78+
return false;
79+
}
80+
}
81+
7082
return $this->type === $node->type
7183
&& $this->public === $node->public
7284
&& $this->private === $node->private
@@ -88,6 +100,7 @@ public static function __set_state(array $properties): self
88100
$properties['static'],
89101
$properties['readonly'],
90102
$properties['attributes'],
103+
$properties['hooks'],
91104
);
92105
}
93106

@@ -110,6 +123,12 @@ public static function decode(array $data): self
110123
}
111124
return ExportedAttributeNode::decode($attributeData['data']);
112125
}, $data['attributes']),
126+
array_map(static function (array $attributeData): ExportedPropertyHookNode {
127+
if ($attributeData['type'] !== ExportedPropertyHookNode::class) {
128+
throw new ShouldNotHappenException();
129+
}
130+
return ExportedPropertyHookNode::decode($attributeData['data']);
131+
}, $data['hooks']),
113132
);
114133
}
115134

@@ -130,6 +149,7 @@ public function jsonSerialize()
130149
'static' => $this->static,
131150
'readonly' => $this->readonly,
132151
'attributes' => $this->attributes,
152+
'hooks' => $this->hooks,
133153
],
134154
];
135155
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Dependency\ExportedNode;
4+
5+
use JsonSerializable;
6+
use PHPStan\Dependency\ExportedNode;
7+
use PHPStan\ShouldNotHappenException;
8+
use ReturnTypeWillChange;
9+
use function array_map;
10+
use function count;
11+
12+
final class ExportedPropertyHookNode implements ExportedNode, JsonSerializable
13+
{
14+
15+
/**
16+
* @param ExportedParameterNode[] $parameters
17+
* @param ExportedAttributeNode[] $attributes
18+
*/
19+
public function __construct(
20+
private string $name,
21+
private ?ExportedPhpDocNode $phpDoc,
22+
private bool $byRef,
23+
private bool $abstract,
24+
private bool $final,
25+
private bool $short,
26+
private array $parameters,
27+
private array $attributes,
28+
)
29+
{
30+
}
31+
32+
public function equals(ExportedNode $node): bool
33+
{
34+
if (!$node instanceof self) {
35+
return false;
36+
}
37+
38+
if (count($this->parameters) !== count($node->parameters)) {
39+
return false;
40+
}
41+
42+
foreach ($this->parameters as $i => $ourParameter) {
43+
$theirParameter = $node->parameters[$i];
44+
if (!$ourParameter->equals($theirParameter)) {
45+
return false;
46+
}
47+
}
48+
49+
if ($this->phpDoc === null) {
50+
if ($node->phpDoc !== null) {
51+
return false;
52+
}
53+
} elseif ($node->phpDoc !== null) {
54+
if (!$this->phpDoc->equals($node->phpDoc)) {
55+
return false;
56+
}
57+
} else {
58+
return false;
59+
}
60+
61+
if (count($this->attributes) !== count($node->attributes)) {
62+
return false;
63+
}
64+
65+
foreach ($this->attributes as $i => $attribute) {
66+
if (!$attribute->equals($node->attributes[$i])) {
67+
return false;
68+
}
69+
}
70+
71+
return $this->name === $node->name
72+
&& $this->byRef === $node->byRef
73+
&& $this->abstract === $node->abstract
74+
&& $this->final === $node->final
75+
&& $this->short === $node->short;
76+
}
77+
78+
/**
79+
* @param mixed[] $properties
80+
*/
81+
public static function __set_state(array $properties): self
82+
{
83+
return new self(
84+
$properties['name'],
85+
$properties['phpDoc'],
86+
$properties['byRef'],
87+
$properties['abstract'],
88+
$properties['final'],
89+
$properties['short'],
90+
$properties['parameters'],
91+
$properties['attributes'],
92+
);
93+
}
94+
95+
/**
96+
* @return mixed
97+
*/
98+
#[ReturnTypeWillChange]
99+
public function jsonSerialize()
100+
{
101+
return [
102+
'type' => self::class,
103+
'data' => [
104+
'name' => $this->name,
105+
'phpDoc' => $this->phpDoc,
106+
'byRef' => $this->byRef,
107+
'abstract' => $this->abstract,
108+
'final' => $this->final,
109+
'short' => $this->short,
110+
'parameters' => $this->parameters,
111+
'attributes' => $this->attributes,
112+
],
113+
];
114+
}
115+
116+
/**
117+
* @param mixed[] $data
118+
*/
119+
public static function decode(array $data): self
120+
{
121+
return new self(
122+
$data['name'],
123+
$data['phpDoc'] !== null ? ExportedPhpDocNode::decode($data['phpDoc']['data']) : null,
124+
$data['byRef'],
125+
$data['abstract'],
126+
$data['final'],
127+
$data['short'],
128+
array_map(static function (array $parameterData): ExportedParameterNode {
129+
if ($parameterData['type'] !== ExportedParameterNode::class) {
130+
throw new ShouldNotHappenException();
131+
}
132+
return ExportedParameterNode::decode($parameterData['data']);
133+
}, $data['parameters']),
134+
array_map(static function (array $attributeData): ExportedAttributeNode {
135+
if ($attributeData['type'] !== ExportedAttributeNode::class) {
136+
throw new ShouldNotHappenException();
137+
}
138+
return ExportedAttributeNode::decode($attributeData['data']);
139+
}, $data['attributes']),
140+
);
141+
}
142+
143+
}

src/Dependency/ExportedNodeResolver.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Dependency;
44

55
use PhpParser\Node;
6+
use PhpParser\Node\Expr;
67
use PhpParser\Node\Name;
78
use PhpParser\Node\Stmt\Class_;
89
use PhpParser\Node\Stmt\ClassMethod;
@@ -19,6 +20,7 @@
1920
use PHPStan\Dependency\ExportedNode\ExportedParameterNode;
2021
use PHPStan\Dependency\ExportedNode\ExportedPhpDocNode;
2122
use PHPStan\Dependency\ExportedNode\ExportedPropertiesNode;
23+
use PHPStan\Dependency\ExportedNode\ExportedPropertyHookNode;
2224
use PHPStan\Dependency\ExportedNode\ExportedTraitNode;
2325
use PHPStan\Dependency\ExportedNode\ExportedTraitUseAdaptation;
2426
use PHPStan\Node\Printer\ExprPrinter;
@@ -27,6 +29,7 @@
2729
use PHPStan\Type\FileTypeMapper;
2830
use function array_map;
2931
use function is_string;
32+
use function sprintf;
3033

3134
final class ExportedNodeResolver
3235
{
@@ -307,6 +310,7 @@ private function exportClassStatement(Node\Stmt $node, string $fileName, string
307310
$node->isStatic(),
308311
$node->isReadonly(),
309312
$this->exportAttributeNodes($node->attrGroups),
313+
$this->exportPropertyHooks($node->hooks, $fileName, $namespacedName),
310314
);
311315
}
312316

@@ -382,4 +386,41 @@ private function exportAttributeNodes(array $attributeGroups): array
382386
return $nodes;
383387
}
384388

389+
/**
390+
* @param Node\PropertyHook[] $hooks
391+
* @return ExportedPropertyHookNode[]
392+
*/
393+
private function exportPropertyHooks(
394+
array $hooks,
395+
string $fileName,
396+
string $namespacedName,
397+
): array
398+
{
399+
$nodes = [];
400+
foreach ($hooks as $hook) {
401+
$docComment = $hook->getDocComment();
402+
$propertyName = $hook->getAttribute('propertyName');
403+
if ($propertyName === null) {
404+
continue;
405+
}
406+
$nodes[] = new ExportedPropertyHookNode(
407+
$hook->name->toString(),
408+
$this->exportPhpDocNode(
409+
$fileName,
410+
$namespacedName,
411+
sprintf('$%s::%s', $propertyName, $hook->name->toString()),
412+
$docComment !== null ? $docComment->getText() : null,
413+
),
414+
$hook->byRef,
415+
$hook->body === null,
416+
$hook->isFinal(),
417+
$hook->body instanceof Expr,
418+
$this->exportParameterNodes($hook->params),
419+
$this->exportAttributeNodes($hook->attrGroups),
420+
);
421+
}
422+
423+
return $nodes;
424+
}
425+
385426
}

0 commit comments

Comments
 (0)