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