1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Rules; |
4: | |
5: | use PHPStan\Reflection\ClassConstantReflection; |
6: | use PHPStan\Reflection\ExtendedMethodReflection; |
7: | use PHPStan\Reflection\ExtendedPropertyReflection; |
8: | use PHPStan\Reflection\FunctionReflection; |
9: | use function sprintf; |
10: | use function ucfirst; |
11: | |
12: | |
13: | |
14: | |
15: | final class ClassNameUsageLocation |
16: | { |
17: | |
18: | public const TRAIT_USE = 'traitUse'; |
19: | public const STATIC_PROPERTY_ACCESS = 'staticProperty'; |
20: | public const PHPDOC_TAG_ASSERT = 'assert'; |
21: | public const ATTRIBUTE = 'attribute'; |
22: | public const EXCEPTION_CATCH = 'catch'; |
23: | public const CLASS_CONSTANT_ACCESS = 'classConstant'; |
24: | public const CLASS_IMPLEMENTS = 'classImplements'; |
25: | public const ENUM_IMPLEMENTS = 'enumImplements'; |
26: | public const INTERFACE_EXTENDS = 'interfaceExtends'; |
27: | public const CLASS_EXTENDS = 'classExtends'; |
28: | public const INSTANCEOF = 'instanceof'; |
29: | public const PROPERTY_TYPE = 'property'; |
30: | public const PARAMETER_TYPE = 'parameter'; |
31: | public const RETURN_TYPE = 'return'; |
32: | public const PHPDOC_TAG_SELF_OUT = 'selfOut'; |
33: | public const PHPDOC_TAG_VAR = 'varTag'; |
34: | public const INSTANTIATION = 'new'; |
35: | public const TYPE_ALIAS = 'typeAlias'; |
36: | public const PHPDOC_TAG_METHOD = 'methodTag'; |
37: | public const PHPDOC_TAG_MIXIN = 'mixin'; |
38: | public const PHPDOC_TAG_PROPERTY = 'propertyTag'; |
39: | public const PHPDOC_TAG_REQUIRE_EXTENDS = 'requireExtends'; |
40: | public const PHPDOC_TAG_REQUIRE_IMPLEMENTS = 'requireImplements'; |
41: | public const PHPDOC_TAG_SEALED = 'sealed'; |
42: | public const STATIC_METHOD_CALL = 'staticMethod'; |
43: | public const PHPDOC_TAG_TEMPLATE_BOUND = 'templateBound'; |
44: | public const PHPDOC_TAG_TEMPLATE_DEFAULT = 'templateDefault'; |
45: | |
46: | |
47: | |
48: | |
49: | |
50: | private function __construct(public readonly string $value, public readonly array $data) |
51: | { |
52: | } |
53: | |
54: | |
55: | |
56: | |
57: | |
58: | public static function from(string $value, array $data = []): self |
59: | { |
60: | return new self($value, $data); |
61: | } |
62: | |
63: | public function getMethod(): ?ExtendedMethodReflection |
64: | { |
65: | return $this->data['method'] ?? null; |
66: | } |
67: | |
68: | public function getProperty(): ?ExtendedPropertyReflection |
69: | { |
70: | return $this->data['property'] ?? null; |
71: | } |
72: | |
73: | public function getFunction(): ?FunctionReflection |
74: | { |
75: | return $this->data['function'] ?? null; |
76: | } |
77: | |
78: | public function getPhpDocTagName(): ?string |
79: | { |
80: | return $this->data['phpDocTagName'] ?? null; |
81: | } |
82: | |
83: | public function getAssertedExprString(): ?string |
84: | { |
85: | return $this->data['assertedExprString'] ?? null; |
86: | } |
87: | |
88: | public function getClassConstant(): ?ClassConstantReflection |
89: | { |
90: | return $this->data['classConstant'] ?? null; |
91: | } |
92: | |
93: | public function getCurrentClassName(): ?string |
94: | { |
95: | return $this->data['currentClassName'] ?? null; |
96: | } |
97: | |
98: | public function getParameterName(): ?string |
99: | { |
100: | return $this->data['parameterName'] ?? null; |
101: | } |
102: | |
103: | public function getTypeAliasName(): ?string |
104: | { |
105: | return $this->data['typeAliasName'] ?? null; |
106: | } |
107: | |
108: | public function getMethodTagName(): ?string |
109: | { |
110: | return $this->data['methodTagName'] ?? null; |
111: | } |
112: | |
113: | public function getPropertyTagName(): ?string |
114: | { |
115: | return $this->data['propertyTagName'] ?? null; |
116: | } |
117: | |
118: | public function getTemplateTagName(): ?string |
119: | { |
120: | return $this->data['templateTagName'] ?? null; |
121: | } |
122: | |
123: | public function isInAnomyousFunction(): bool |
124: | { |
125: | return $this->data['isInAnonymousFunction'] ?? false; |
126: | } |
127: | |
128: | public function createMessage(string $part): string |
129: | { |
130: | switch ($this->value) { |
131: | case self::TRAIT_USE: |
132: | if ($this->getCurrentClassName() !== null) { |
133: | return sprintf('Usage of %s in class %s.', $part, $this->getCurrentClassName()); |
134: | } |
135: | return sprintf('Usage of %s.', $part); |
136: | case self::STATIC_PROPERTY_ACCESS: |
137: | $property = $this->getProperty(); |
138: | if ($property !== null) { |
139: | return sprintf('Access to static property $%s on %s.', $property->getName(), $part); |
140: | } |
141: | |
142: | return sprintf('Access to static property on %s.', $part); |
143: | case self::PHPDOC_TAG_ASSERT: |
144: | $phpDocTagName = $this->getPhpDocTagName(); |
145: | $assertExprString = $this->getAssertedExprString(); |
146: | if ($phpDocTagName !== null && $assertExprString !== null) { |
147: | return sprintf('PHPDoc tag %s for %s references %s.', $phpDocTagName, $assertExprString, $part); |
148: | } |
149: | |
150: | return sprintf('Assert tag references %s.', $part); |
151: | case self::ATTRIBUTE: |
152: | return sprintf('Attribute references %s.', $part); |
153: | case self::EXCEPTION_CATCH: |
154: | return sprintf('Catching %s.', $part); |
155: | case self::CLASS_CONSTANT_ACCESS: |
156: | if ($this->getClassConstant() !== null) { |
157: | return sprintf('Access to constant %s on %s.', $this->getClassConstant()->getName(), $part); |
158: | } |
159: | return sprintf('Access to constant on %s.', $part); |
160: | case self::CLASS_IMPLEMENTS: |
161: | if ($this->getCurrentClassName() !== null) { |
162: | return sprintf('Class %s implements %s.', $this->getCurrentClassName(), $part); |
163: | } |
164: | |
165: | return sprintf('Anonymous class implements %s.', $part); |
166: | case self::ENUM_IMPLEMENTS: |
167: | if ($this->getCurrentClassName() !== null) { |
168: | return sprintf('Enum %s implements %s.', $this->getCurrentClassName(), $part); |
169: | } |
170: | |
171: | return sprintf('Enum implements %s.', $part); |
172: | case self::INTERFACE_EXTENDS: |
173: | if ($this->getCurrentClassName() !== null) { |
174: | return sprintf('Interface %s extends %s.', $this->getCurrentClassName(), $part); |
175: | } |
176: | |
177: | return sprintf('Interface extends %s.', $part); |
178: | case self::CLASS_EXTENDS: |
179: | if ($this->getCurrentClassName() !== null) { |
180: | return sprintf('Class %s extends %s.', $this->getCurrentClassName(), $part); |
181: | } |
182: | |
183: | return sprintf('Anonymous class extends %s.', $part); |
184: | case self::INSTANCEOF: |
185: | return sprintf('Instanceof references %s.', $part); |
186: | case self::PROPERTY_TYPE: |
187: | $property = $this->getProperty(); |
188: | if ($property !== null) { |
189: | return sprintf('Property $%s references %s in its type.', $property->getName(), $part); |
190: | } |
191: | return sprintf('Property references %s in its type.', $part); |
192: | case self::PARAMETER_TYPE: |
193: | $parameterName = $this->getParameterName(); |
194: | if ($parameterName !== null) { |
195: | if ($this->isInAnomyousFunction()) { |
196: | return sprintf('Parameter $%s of anonymous function has typehint with %s.', $parameterName, $part); |
197: | } |
198: | if ($this->getMethod() !== null) { |
199: | if ($this->getCurrentClassName() !== null) { |
200: | return sprintf('Parameter $%s of method %s::%s() has typehint with %s.', $parameterName, $this->getCurrentClassName(), $this->getMethod()->getName(), $part); |
201: | } |
202: | |
203: | return sprintf('Parameter $%s of method %s() in anonymous class has typehint with %s.', $parameterName, $this->getMethod()->getName(), $part); |
204: | } |
205: | |
206: | if ($this->getFunction() !== null) { |
207: | return sprintf('Parameter $%s of function %s() has typehint with %s.', $parameterName, $this->getFunction()->getName(), $part); |
208: | } |
209: | |
210: | return sprintf('Parameter $%s has typehint with %s.', $parameterName, $part); |
211: | } |
212: | |
213: | return sprintf('Parameter has typehint with %s.', $part); |
214: | case self::RETURN_TYPE: |
215: | if ($this->isInAnomyousFunction()) { |
216: | return sprintf('Return type of anonymous function has typehint with %s.', $part); |
217: | } |
218: | if ($this->getMethod() !== null) { |
219: | if ($this->getCurrentClassName() !== null) { |
220: | return sprintf('Return type of method %s::%s() has typehint with %s.', $this->getCurrentClassName(), $this->getMethod()->getName(), $part); |
221: | } |
222: | |
223: | return sprintf('Return type of method %s() in anonymous class has typehint with %s.', $this->getMethod()->getName(), $part); |
224: | } |
225: | |
226: | if ($this->getFunction() !== null) { |
227: | return sprintf('Return type of function %s() has typehint with %s.', $this->getFunction()->getName(), $part); |
228: | } |
229: | |
230: | return sprintf('Return type has typehint with %s.', $part); |
231: | case self::PHPDOC_TAG_SELF_OUT: |
232: | return sprintf('PHPDoc tag @phpstan-self-out references %s.', $part); |
233: | case self::PHPDOC_TAG_VAR: |
234: | return sprintf('PHPDoc tag @var references %s.', $part); |
235: | case self::INSTANTIATION: |
236: | return sprintf('Instantiation of %s.', $part); |
237: | case self::TYPE_ALIAS: |
238: | if ($this->getTypeAliasName() !== null) { |
239: | return sprintf('Type alias %s references %s.', $this->getTypeAliasName(), $part); |
240: | } |
241: | |
242: | return sprintf('Type alias references %s.', $part); |
243: | case self::PHPDOC_TAG_METHOD: |
244: | if ($this->getMethodTagName() !== null) { |
245: | return sprintf('PHPDoc tag @method for %s() references %s.', $this->getMethodTagName(), $part); |
246: | } |
247: | return sprintf('PHPDoc tag @method references %s.', $part); |
248: | case self::PHPDOC_TAG_MIXIN: |
249: | return sprintf('PHPDoc tag @mixin references %s.', $part); |
250: | case self::PHPDOC_TAG_PROPERTY: |
251: | if ($this->getPropertyTagName() !== null) { |
252: | return sprintf('PHPDoc tag @property for $%s references %s.', $this->getPropertyTagName(), $part); |
253: | } |
254: | return sprintf('PHPDoc tag @property references %s.', $part); |
255: | case self::PHPDOC_TAG_REQUIRE_EXTENDS: |
256: | return sprintf('PHPDoc tag @phpstan-require-extends references %s.', $part); |
257: | case self::PHPDOC_TAG_REQUIRE_IMPLEMENTS: |
258: | return sprintf('PHPDoc tag @phpstan-require-implements references %s.', $part); |
259: | case self::PHPDOC_TAG_SEALED: |
260: | return sprintf('PHPDoc tag @phpstan-sealed references %s.', $part); |
261: | case self::STATIC_METHOD_CALL: |
262: | $method = $this->getMethod(); |
263: | if ($method !== null) { |
264: | return sprintf('Call to static method %s() on %s.', $method->getName(), $part); |
265: | } |
266: | |
267: | return sprintf('Call to static method on %s.', $part); |
268: | case self::PHPDOC_TAG_TEMPLATE_BOUND: |
269: | if ($this->getTemplateTagName() !== null) { |
270: | return sprintf('PHPDoc tag @template %s bound references %s.', $this->getTemplateTagName(), $part); |
271: | } |
272: | |
273: | return sprintf('PHPDoc tag @template bound references %s.', $part); |
274: | case self::PHPDOC_TAG_TEMPLATE_DEFAULT: |
275: | if ($this->getTemplateTagName() !== null) { |
276: | return sprintf('PHPDoc tag @template %s default references %s.', $this->getTemplateTagName(), $part); |
277: | } |
278: | |
279: | return sprintf('PHPDoc tag @template default references %s.', $part); |
280: | } |
281: | } |
282: | |
283: | public function createIdentifier(string $secondPart): string |
284: | { |
285: | if ($this->value === self::CLASS_IMPLEMENTS) { |
286: | return sprintf('class.implements%s', ucfirst($secondPart)); |
287: | } |
288: | if ($this->value === self::ENUM_IMPLEMENTS) { |
289: | return sprintf('enum.implements%s', ucfirst($secondPart)); |
290: | } |
291: | if ($this->value === self::INTERFACE_EXTENDS) { |
292: | return sprintf('interface.extends%s', ucfirst($secondPart)); |
293: | } |
294: | if ($this->value === self::CLASS_EXTENDS) { |
295: | return sprintf('class.extends%s', ucfirst($secondPart)); |
296: | } |
297: | if ($this->value === self::PHPDOC_TAG_TEMPLATE_BOUND) { |
298: | return sprintf('generics.%sBound', $secondPart); |
299: | } |
300: | if ($this->value === self::PHPDOC_TAG_TEMPLATE_DEFAULT) { |
301: | return sprintf('generics.%sDefault', $secondPart); |
302: | } |
303: | |
304: | return sprintf('%s.%s', $this->value, $secondPart); |
305: | } |
306: | |
307: | } |
308: | |