1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Analyser;
4:
5: use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
6: use function serialize;
7:
8: final class IntermediaryNameScope
9: {
10:
11: /**
12: * @api
13: * @param non-empty-string|null $namespace
14: * @param array<string, string> $uses alias(string) => fullName(string)
15: * @param array<string, array{string, TemplateTagValueNode}> $templatePhpDocNodes
16: * @param array<string, string> $constUses alias(string) => fullName(string)
17: * @param array<string, true> $typeAliasesMap
18: * @param array{string, string, string, string|null, string|null}|null $traitData
19: */
20: public function __construct(
21: private ?string $namespace,
22: private array $uses,
23: private ?string $className = null,
24: private ?string $functionName = null,
25: private array $templatePhpDocNodes = [],
26: private ?self $parent = null,
27: private array $typeAliasesMap = [],
28: private bool $bypassTypeAliases = false,
29: private array $constUses = [],
30: private ?string $typeAliasClassName = null,
31: private ?array $traitData = null,
32: )
33: {
34: }
35:
36: /**
37: * @return non-empty-string|null
38: */
39: public function getNamespace(): ?string
40: {
41: return $this->namespace;
42: }
43:
44: /**
45: * @return array<string, string>
46: */
47: public function getUses(): array
48: {
49: return $this->uses;
50: }
51:
52: /**
53: * @return array<string, string>
54: */
55: public function getConstUses(): array
56: {
57: return $this->constUses;
58: }
59:
60: public function getClassName(): ?string
61: {
62: return $this->className;
63: }
64:
65: public function getFunctionName(): ?string
66: {
67: return $this->functionName;
68: }
69:
70: /**
71: * @return array<string, array{string, TemplateTagValueNode}>
72: */
73: public function getTemplatePhpDocNodes(): array
74: {
75: return $this->templatePhpDocNodes;
76: }
77:
78: public function withTraitData(string $fileName, string $className, string $traitName, ?string $lookForTraitName, ?string $docComment): self
79: {
80: return new self(
81: $this->namespace,
82: $this->uses,
83: $this->className,
84: $this->functionName,
85: $this->templatePhpDocNodes,
86: $this->parent,
87: $this->typeAliasesMap,
88: $this->bypassTypeAliases,
89: $this->constUses,
90: $this->typeAliasClassName,
91: [$fileName, $className, $traitName, $lookForTraitName, $docComment],
92: );
93: }
94:
95: /**
96: * @param string[] $namesToUnset
97: */
98: public function unsetTemplatePhpDocNodes(array $namesToUnset): self
99: {
100: $templatePhpDocNodes = $this->templatePhpDocNodes;
101: foreach ($namesToUnset as $name) {
102: unset($templatePhpDocNodes[$name]);
103: }
104: return new self(
105: $this->namespace,
106: $this->uses,
107: $this->className,
108: $this->functionName,
109: $templatePhpDocNodes,
110: $this->parent,
111: $this->typeAliasesMap,
112: $this->bypassTypeAliases,
113: $this->constUses,
114: $this->typeAliasClassName,
115: $this->traitData,
116: );
117: }
118:
119: /**
120: * Restores sharing of identical property values and parent scopes after hydration from the file cache.
121: *
122: * var_export() used by the cache cannot represent shared references, so every hydrated
123: * scope carries its own copy of the same uses maps and of the whole parent chain.
124: * Without interning, the name scope map of a file with many members takes up
125: * many times more memory when loaded from the cache than when freshly created.
126: *
127: * @param array<string, self|array<mixed>> $pool
128: */
129: public function intern(array &$pool): self
130: {
131: $key = serialize($this);
132: if (isset($pool[$key])) {
133: /** @var self */
134: return $pool[$key];
135: }
136:
137: $this->uses = self::internArray($pool, $this->uses);
138: $this->templatePhpDocNodes = self::internArray($pool, $this->templatePhpDocNodes);
139: $this->typeAliasesMap = self::internArray($pool, $this->typeAliasesMap);
140: $this->constUses = self::internArray($pool, $this->constUses);
141: if ($this->parent !== null) {
142: $this->parent = $this->parent->intern($pool);
143: }
144:
145: return $pool[$key] = $this;
146: }
147:
148: /**
149: * @template T of array<mixed>
150: * @param array<string, self|array<mixed>> $pool
151: * @param T $value
152: * @return T
153: */
154: private static function internArray(array &$pool, array $value): array
155: {
156: $key = 'a:' . serialize($value);
157: if (isset($pool[$key])) {
158: /** @var T */
159: return $pool[$key];
160: }
161:
162: $pool[$key] = $value;
163:
164: return $value;
165: }
166:
167: /**
168: * @return array{string, string, string, string|null, string|null}|null
169: */
170: public function getTraitData(): ?array
171: {
172: return $this->traitData;
173: }
174:
175: public function getParent(): ?self
176: {
177: return $this->parent;
178: }
179:
180: /**
181: * @return array<string, true>
182: */
183: public function getTypeAliasesMap(): array
184: {
185: return $this->typeAliasesMap;
186: }
187:
188: public function shouldBypassTypeAliases(): bool
189: {
190: return $this->bypassTypeAliases;
191: }
192:
193: public function getClassNameForTypeAlias(): ?string
194: {
195: return $this->typeAliasClassName;
196: }
197:
198: /**
199: * @param array<string, mixed> $properties
200: */
201: public static function __set_state(array $properties): self
202: {
203: return new self(
204: $properties['namespace'],
205: $properties['uses'],
206: $properties['className'],
207: $properties['functionName'],
208: $properties['templatePhpDocNodes'],
209: $properties['parent'],
210: $properties['typeAliasesMap'],
211: $properties['bypassTypeAliases'],
212: $properties['constUses'],
213: $properties['typeAliasClassName'],
214: $properties['traitData'],
215: );
216: }
217:
218: }
219: