1 | <?php |
||||||
2 | declare(strict_types = 1); |
||||||
3 | |||||||
4 | namespace DependencyAnalyzer\DependencyDumper; |
||||||
5 | |||||||
6 | use DependencyAnalyzer\DependencyGraphBuilder; |
||||||
7 | use DependencyAnalyzer\Exceptions\ResolveDependencyException; |
||||||
8 | use PhpParser\Node\Identifier; |
||||||
9 | use PHPStan\AnalysedCodeException; |
||||||
10 | use PHPStan\Analyser\Scope; |
||||||
11 | use PHPStan\Broker\Broker; |
||||||
12 | use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; |
||||||
13 | use PHPStan\PhpDocParser\Lexer\Lexer; |
||||||
14 | use PHPStan\PhpDocParser\Parser\PhpDocParser; |
||||||
15 | use PHPStan\PhpDocParser\Parser\TokenIterator; |
||||||
16 | use PHPStan\Reflection\ClassReflection; |
||||||
17 | use PHPStan\Reflection\ParametersAcceptorSelector; |
||||||
18 | use PHPStan\Reflection\ParametersAcceptorWithPhpDocs; |
||||||
19 | use PHPStan\Reflection\Php\PhpMethodReflection; |
||||||
20 | use PHPStan\Reflection\Php\PhpPropertyReflection; |
||||||
21 | use PHPStan\Reflection\ReflectionWithFilename; |
||||||
22 | use PHPStan\ShouldNotHappenException; |
||||||
23 | use PHPStan\Type\ClosureType; |
||||||
24 | use PHPStan\Type\TypeWithClassName; |
||||||
25 | |||||||
26 | /** |
||||||
27 | * @da-internal \DependencyAnalyzer\DependencyDumper\ |
||||||
28 | */ |
||||||
29 | class DependencyResolver |
||||||
30 | { |
||||||
31 | /** |
||||||
32 | * @var Broker |
||||||
33 | */ |
||||||
34 | protected $broker; |
||||||
35 | |||||||
36 | /** |
||||||
37 | * @var Lexer |
||||||
38 | */ |
||||||
39 | protected $phpDocLexer; |
||||||
40 | |||||||
41 | /** |
||||||
42 | * @var PhpDocParser |
||||||
43 | */ |
||||||
44 | protected $phpDocParser; |
||||||
45 | |||||||
46 | /** |
||||||
47 | * @var DependencyGraphBuilder |
||||||
48 | */ |
||||||
49 | protected $dependencyGraphBuilder; |
||||||
50 | |||||||
51 | /** |
||||||
52 | * @var \ReflectionClass |
||||||
53 | */ |
||||||
54 | protected $depender = null; |
||||||
55 | |||||||
56 | public function __construct(Broker $broker, Lexer $phpDocLexer, PhpDocParser $phpDocParser) |
||||||
57 | { |
||||||
58 | $this->broker = $broker; |
||||||
59 | $this->phpDocLexer = $phpDocLexer; |
||||||
60 | $this->phpDocParser = $phpDocParser; |
||||||
61 | } |
||||||
62 | |||||||
63 | protected function getDependerReflection(\PhpParser\Node $node, Scope $scope): ?ClassReflection |
||||||
64 | { |
||||||
65 | if ($scope->isInClass()) { |
||||||
66 | return $scope->getClassReflection(); |
||||||
67 | } else { |
||||||
68 | // Maybe, class declare statement |
||||||
69 | // ex: |
||||||
70 | // class Hoge {} |
||||||
71 | // abstract class Hoge {} |
||||||
72 | // interface Hoge {} |
||||||
73 | if ($node instanceof \PhpParser\Node\Stmt\ClassLike) { |
||||||
74 | return $this->resolveClassReflection($node->namespacedName->toString()); |
||||||
75 | } |
||||||
76 | } |
||||||
77 | |||||||
78 | return null; |
||||||
79 | } |
||||||
80 | |||||||
81 | /** |
||||||
82 | * @param \PhpParser\Node $node |
||||||
83 | * @param Scope $scope |
||||||
84 | * @param DependencyGraphBuilder $dependencyGraphBuilder |
||||||
85 | * @return ReflectionWithFilename[] |
||||||
86 | */ |
||||||
87 | public function resolveDependencies(\PhpParser\Node $node, Scope $scope, DependencyGraphBuilder $dependencyGraphBuilder): array |
||||||
88 | { |
||||||
89 | try { |
||||||
90 | if (is_null($this->depender = $this->getDependerReflection($node, $scope))) { |
||||||
0 ignored issues
–
show
|
|||||||
91 | return []; |
||||||
92 | } |
||||||
93 | $this->depender = $this->depender->getNativeReflection(); |
||||||
94 | $this->dependencyGraphBuilder = $dependencyGraphBuilder; |
||||||
95 | |||||||
96 | if ($node instanceof \PhpParser\Node\Stmt\Class_) { |
||||||
97 | // define class statement |
||||||
98 | // ex: class SomeClass {} |
||||||
99 | $this->resolveClassNode($node); |
||||||
100 | } elseif ($node instanceof \PhpParser\Node\Stmt\Interface_) { |
||||||
101 | // define interface statement |
||||||
102 | // ex: interface SomeInterface {} |
||||||
103 | $this->resolveInterfaceNode($node); |
||||||
104 | } elseif ($node instanceof \PhpParser\Node\Stmt\ClassMethod) { |
||||||
105 | // define class method statement |
||||||
106 | // ex: |
||||||
107 | // class SomeClass { |
||||||
108 | // function ClassMethod() {} |
||||||
109 | // } |
||||||
110 | $this->resolveClassMethod($node, $scope); |
||||||
111 | // } elseif ($node instanceof \PhpParser\Node\Stmt\Function_) { |
||||||
112 | // // define function statement |
||||||
113 | // // ex: function SomeFunction() {} |
||||||
114 | // return $this->resolveFunction($node); |
||||||
115 | } elseif ($node instanceof \PhpParser\Node\Expr\Closure) { |
||||||
116 | // closure expression |
||||||
117 | // ex: |
||||||
118 | // function (SomeClass1 $someClass1): SomeClass2 { |
||||||
119 | // // some logic. |
||||||
120 | // } |
||||||
121 | $this->resolveClosure($node, $scope); |
||||||
122 | } elseif ($node instanceof \PhpParser\Node\Expr\FuncCall) { |
||||||
123 | // function call expression |
||||||
124 | // ex: someFunction(); |
||||||
125 | $this->resolveFuncCall($node, $scope); |
||||||
126 | } elseif ($node instanceof \PhpParser\Node\Expr\MethodCall) { |
||||||
127 | // method call expression |
||||||
128 | // ex: $someObject->someMethod(); |
||||||
129 | $this->resolveMethodCall($node, $scope); |
||||||
130 | } elseif ($node instanceof \PhpParser\Node\Expr\PropertyFetch) { |
||||||
131 | // property fetch expression |
||||||
132 | // ex: $someObject->somePublicProperty; |
||||||
133 | $this->resolvePropertyFetch($node, $scope); |
||||||
134 | } elseif ($node instanceof \PhpParser\Node\Expr\StaticCall) { |
||||||
135 | // static method call expression |
||||||
136 | // ex: SomeClass::someStaticMethod() |
||||||
137 | $this->resolveStaticCall($node, $scope); |
||||||
138 | } elseif ($node instanceof \PhpParser\Node\Expr\ClassConstFetch) { |
||||||
139 | // class const fetch expression |
||||||
140 | // ex: SomeClass::SOME_CONST |
||||||
141 | $this->resolveClassConstFetch($node, $scope); |
||||||
142 | } elseif ($node instanceof \PhpParser\Node\Expr\StaticPropertyFetch) { |
||||||
143 | // class static property fetch expression |
||||||
144 | // ex: SomeClass::$someStaticProperty |
||||||
145 | $this->resolveStaticPropertyFetch($node, $scope); |
||||||
146 | } elseif ($node instanceof \PhpParser\Node\Expr\New_) { |
||||||
147 | // new object expression |
||||||
148 | // ex: new SomeClass(); |
||||||
149 | $this->resolveNew($node, $scope); |
||||||
150 | } elseif ($node instanceof \PhpParser\Node\Stmt\TraitUse) { |
||||||
151 | // use trait expression |
||||||
152 | // ex: |
||||||
153 | // class SomeClass { |
||||||
154 | // use SomeTrait; |
||||||
155 | // } |
||||||
156 | $this->resolveTraitUse($node); |
||||||
157 | } elseif ($node instanceof \PhpParser\Node\Expr\Instanceof_) { |
||||||
158 | // instanceof expression |
||||||
159 | // ex: if ($someObject instanceof SomeClass) {} |
||||||
160 | $this->resolveInstanceOf($node, $scope); |
||||||
161 | } elseif ($node instanceof \PhpParser\Node\Stmt\Catch_) { |
||||||
162 | // catch expression |
||||||
163 | // ex: try {} catch (SomeException $e) {} |
||||||
164 | $this->resolveCatch($node, $scope); |
||||||
165 | } elseif ($node instanceof \PhpParser\Node\Expr\ArrayDimFetch) { |
||||||
166 | // array dim fetch expression |
||||||
167 | // ex: $someArray[2] |
||||||
168 | $this->resolveArrayDimFetch($node, $scope); |
||||||
169 | } elseif ($node instanceof \PhpParser\Node\Stmt\Foreach_) { |
||||||
170 | // foreach access expression |
||||||
171 | // foreach ($someArray as $key => $someObject) {} |
||||||
172 | $this->resolveForeach($node, $scope); |
||||||
173 | } elseif ($node instanceof \PhpParser\Node\Expr\Array_) { |
||||||
174 | $this->resolveArray($node, $scope); |
||||||
175 | } elseif ($node instanceof \PhpParser\Node\Stmt\PropertyProperty) { |
||||||
176 | // property with phpdoc |
||||||
177 | // ex: |
||||||
178 | // class SomeClass { |
||||||
179 | // /** @var SomeClass2 $someProperty */ |
||||||
180 | // private $someProperty; |
||||||
181 | // } |
||||||
182 | $this->resolvePropertyProperty($node, $scope); |
||||||
183 | } |
||||||
184 | } catch (AnalyzedCodeException $e) { |
||||||
0 ignored issues
–
show
The type
DependencyAnalyzer\Depen...r\AnalyzedCodeException was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
185 | throw new ResolveDependencyException($node, 'resolving node dependency is failed.', 0, $e); |
||||||
186 | } catch (ShouldNotHappenException $e) { |
||||||
187 | throw new ResolveDependencyException($node, 'resolving node dependency is failed.', 0, $e); |
||||||
188 | } |
||||||
189 | |||||||
190 | return []; |
||||||
191 | } |
||||||
192 | |||||||
193 | public function resolveClassReflection(string $className): ?ClassReflection |
||||||
194 | { |
||||||
195 | try { |
||||||
196 | return $this->broker->getClass($className); |
||||||
197 | } catch (\PHPStan\Broker\ClassNotFoundException $e) { |
||||||
198 | return null; |
||||||
199 | // return new UnknownClassReflection($className); |
||||||
200 | } |
||||||
201 | } |
||||||
202 | |||||||
203 | protected function getFunctionReflection(\PhpParser\Node\Name $nameNode, ?Scope $scope): ReflectionWithFilename |
||||||
204 | { |
||||||
205 | $reflection = $this->broker->getFunction($nameNode, $scope); |
||||||
206 | if (!$reflection instanceof ReflectionWithFilename) { |
||||||
207 | throw new \PHPStan\Broker\FunctionNotFoundException((string) $nameNode); |
||||||
208 | } |
||||||
209 | |||||||
210 | return $reflection; |
||||||
211 | } |
||||||
212 | |||||||
213 | /** |
||||||
214 | * @param ParametersAcceptorWithPhpDocs $parametersAcceptor |
||||||
215 | * @return ClassReflection[] |
||||||
216 | */ |
||||||
217 | protected function extractFromParametersAcceptor(ParametersAcceptorWithPhpDocs $parametersAcceptor): array |
||||||
218 | { |
||||||
219 | $dependenciesReflections = []; |
||||||
220 | |||||||
221 | foreach ($parametersAcceptor->getParameters() as $parameter) { |
||||||
222 | $referencedClasses = array_merge( |
||||||
223 | $parameter->getNativeType()->getReferencedClasses(), |
||||||
224 | $parameter->getPhpDocType()->getReferencedClasses() |
||||||
225 | ); |
||||||
226 | |||||||
227 | foreach ($referencedClasses as $referencedClass) { |
||||||
228 | $dependenciesReflections[] = $this->resolveClassReflection($referencedClass); |
||||||
229 | } |
||||||
230 | } |
||||||
231 | |||||||
232 | $returnTypeReferencedClasses = array_merge( |
||||||
233 | $parametersAcceptor->getNativeReturnType()->getReferencedClasses(), |
||||||
234 | $parametersAcceptor->getPhpDocReturnType()->getReferencedClasses() |
||||||
235 | ); |
||||||
236 | foreach ($returnTypeReferencedClasses as $referencedClass) { |
||||||
237 | $dependenciesReflections[] = $this->resolveClassReflection($referencedClass); |
||||||
238 | } |
||||||
239 | |||||||
240 | return $dependenciesReflections; |
||||||
241 | } |
||||||
242 | |||||||
243 | protected function resolveClassReflectionOrAddUnkownDependency(string $className): ?ClassReflection |
||||||
244 | { |
||||||
245 | if (!is_null($classReflection = $this->resolveClassReflection($className))) { |
||||||
246 | return $classReflection; |
||||||
247 | } |
||||||
248 | |||||||
249 | $this->dependencyGraphBuilder->addUnknownDependency($this->depender, $className); |
||||||
250 | return null; |
||||||
251 | } |
||||||
252 | |||||||
253 | protected function addDependencyWhenResolveClassReflectionIsSucceeded(string $className): void |
||||||
254 | { |
||||||
255 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className)) { |
||||||
256 | $this->dependencyGraphBuilder->addDependency($this->depender, $dependee->getNativeReflection()); |
||||||
257 | } |
||||||
258 | } |
||||||
259 | |||||||
260 | protected function resolveClassNode(\PhpParser\Node\Stmt\Class_ $node): void |
||||||
261 | { |
||||||
262 | if ($node->extends !== null) { |
||||||
263 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($node->extends->toString())) { |
||||||
264 | $this->dependencyGraphBuilder->addExtends($this->depender, $dependee->getNativeReflection()); |
||||||
265 | } |
||||||
266 | } |
||||||
267 | foreach ($node->implements as $className) { |
||||||
268 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className->toString())) { |
||||||
269 | $this->dependencyGraphBuilder->addImplements($this->depender, $dependee->getNativeReflection()); |
||||||
270 | } |
||||||
271 | } |
||||||
272 | if ($node->getDocComment() !== null) { |
||||||
0 ignored issues
–
show
Are you sure the usage of
$node->getDocComment() targeting PhpParser\NodeAbstract::getDocComment() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||
273 | $tokens = new TokenIterator($this->phpDocLexer->tokenize($node->getDocComment()->getText())); |
||||||
274 | $phpDocNode = $this->phpDocParser->parse($tokens); |
||||||
275 | foreach ($phpDocNode->getTagsByName('@dependOn') as $phpDocTagNode) { |
||||||
276 | /** @var PhpDocTagNode $phpDocTagNode */ |
||||||
277 | preg_match('/^@dependOn\s+(.+)$/', $phpDocTagNode->__toString(), $matches); |
||||||
278 | |||||||
279 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($matches[1]); |
||||||
280 | }; |
||||||
281 | } |
||||||
282 | } |
||||||
283 | |||||||
284 | protected function resolveInterfaceNode(\PhpParser\Node\Stmt\Interface_ $node): void |
||||||
285 | { |
||||||
286 | if ($node->extends !== null) { |
||||||
287 | foreach ($node->extends as $className) { |
||||||
288 | if ($dependee = $this->resolveClassReflection($className->toString())) { |
||||||
289 | $this->dependencyGraphBuilder->addExtends($this->depender, $dependee->getNativeReflection()); |
||||||
290 | } |
||||||
291 | } |
||||||
292 | } |
||||||
293 | } |
||||||
294 | |||||||
295 | /** |
||||||
296 | * @param \PhpParser\Node\Stmt\ClassMethod $node |
||||||
297 | * @param Scope $scope |
||||||
298 | * @throws \PHPStan\Reflection\MissingMethodFromReflectionException |
||||||
299 | */ |
||||||
300 | protected function resolveClassMethod(\PhpParser\Node\Stmt\ClassMethod $node, Scope $scope) |
||||||
301 | { |
||||||
302 | if (!$scope->isInClass()) { |
||||||
303 | throw new \PHPStan\ShouldNotHappenException(); |
||||||
304 | } |
||||||
305 | |||||||
306 | $nativeMethod = $scope->getClassReflection()->getNativeMethod($node->name->name); |
||||||
307 | if ($nativeMethod instanceof PhpMethodReflection) { |
||||||
308 | /** @var \PHPStan\Reflection\ParametersAcceptorWithPhpDocs $parametersAcceptor */ |
||||||
309 | $parametersAcceptor = ParametersAcceptorSelector::selectSingle($nativeMethod->getVariants()); |
||||||
310 | |||||||
311 | // foreach ($this->extractFromParametersAcceptor($parametersAcceptor) as $classReflection) { |
||||||
312 | // $this->dependencyGraphBuilder->addDependency($this->depender, $classReflection->getNativeReflection()); |
||||||
313 | // } |
||||||
314 | |||||||
315 | foreach ($parametersAcceptor->getParameters() as $parameter) { |
||||||
316 | $referencedClasses = array_merge( |
||||||
317 | $parameter->getNativeType()->getReferencedClasses(), |
||||||
318 | $parameter->getPhpDocType()->getReferencedClasses() |
||||||
319 | ); |
||||||
320 | |||||||
321 | foreach ($referencedClasses as $referencedClass) { |
||||||
322 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
323 | } |
||||||
324 | } |
||||||
325 | |||||||
326 | $returnTypeReferencedClasses = array_merge( |
||||||
327 | $parametersAcceptor->getNativeReturnType()->getReferencedClasses(), |
||||||
328 | $parametersAcceptor->getPhpDocReturnType()->getReferencedClasses() |
||||||
329 | ); |
||||||
330 | foreach ($returnTypeReferencedClasses as $referencedClass) { |
||||||
331 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
332 | } |
||||||
333 | } |
||||||
334 | } |
||||||
335 | |||||||
336 | /** |
||||||
337 | * @param \PhpParser\Node\Stmt\Function_ $node |
||||||
338 | * @return ReflectionWithFilename[] |
||||||
339 | * @throws \PHPStan\Broker\FunctionNotFoundException |
||||||
340 | */ |
||||||
341 | protected function resolveFunction(\PhpParser\Node\Stmt\Function_ $node): array |
||||||
342 | { |
||||||
343 | $functionName = $node->name->name; |
||||||
344 | if (isset($node->namespacedName)) { |
||||||
345 | $functionName = (string)$node->namespacedName; |
||||||
346 | } |
||||||
347 | $functionNameName = new \PhpParser\Node\Name($functionName); |
||||||
348 | if ($this->broker->hasCustomFunction($functionNameName, null)) { |
||||||
349 | $functionReflection = $this->broker->getCustomFunction($functionNameName, null); |
||||||
350 | |||||||
351 | /** @var \PHPStan\Reflection\ParametersAcceptorWithPhpDocs $parametersAcceptor */ |
||||||
352 | $parametersAcceptor = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants()); |
||||||
353 | return $this->extractFromParametersAcceptor($parametersAcceptor); |
||||||
354 | } |
||||||
355 | |||||||
356 | return []; |
||||||
357 | } |
||||||
358 | |||||||
359 | protected function resolveClosure(\PhpParser\Node\Expr\Closure $node, Scope $scope) |
||||||
360 | { |
||||||
361 | /** @var ClosureType $closureType */ |
||||||
362 | $closureType = $scope->getType($node); |
||||||
363 | foreach ($closureType->getParameters() as $parameter) { |
||||||
364 | $referencedClasses = $parameter->getType()->getReferencedClasses(); |
||||||
365 | foreach ($referencedClasses as $referencedClass) { |
||||||
366 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
367 | } |
||||||
368 | } |
||||||
369 | |||||||
370 | $returnTypeReferencedClasses = $closureType->getReturnType()->getReferencedClasses(); |
||||||
371 | foreach ($returnTypeReferencedClasses as $referencedClass) { |
||||||
372 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
373 | } |
||||||
374 | } |
||||||
375 | |||||||
376 | protected function resolveFuncCall(\PhpParser\Node\Expr\FuncCall $node, Scope $scope) |
||||||
377 | { |
||||||
378 | // $functionName = $node->name; |
||||||
379 | // if ($functionName instanceof \PhpParser\Node\Name) { |
||||||
380 | // try { |
||||||
381 | // $dependenciesReflections[] = $this->getFunctionReflection($functionName, $scope); |
||||||
382 | // } catch (\PHPStan\Broker\FunctionNotFoundException $e) { |
||||||
383 | // // pass |
||||||
384 | // } |
||||||
385 | // } else { |
||||||
386 | // $variants = $scope->getType($functionName)->getCallableParametersAcceptors($scope); |
||||||
387 | // foreach ($variants as $variant) { |
||||||
388 | // $referencedClasses = $variant->getReturnType()->getReferencedClasses(); |
||||||
389 | // foreach ($referencedClasses as $referencedClass) { |
||||||
390 | // $dependenciesReflections[] = $this->resolveClassReflection($referencedClass); |
||||||
391 | // } |
||||||
392 | // } |
||||||
393 | // } |
||||||
394 | |||||||
395 | $returnType = $scope->getType($node); |
||||||
396 | foreach ($returnType->getReferencedClasses() as $referencedClass) { |
||||||
397 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
398 | } |
||||||
399 | } |
||||||
400 | |||||||
401 | protected function resolveMethodCall(\PhpParser\Node\Expr\MethodCall $node, Scope $scope) |
||||||
402 | { |
||||||
403 | if ($node instanceof Identifier) { |
||||||
0 ignored issues
–
show
|
|||||||
404 | $classNames = $scope->getType($node->var)->getReferencedClasses(); |
||||||
405 | foreach ($classNames as $className) { |
||||||
406 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className)) { |
||||||
407 | $this->dependencyGraphBuilder->addMethodCall( |
||||||
408 | $this->depender, |
||||||
409 | $dependee->getNativeReflection(), |
||||||
410 | $node->name->toString(), |
||||||
411 | $scope->getFunctionName() |
||||||
412 | ); |
||||||
413 | } |
||||||
414 | } |
||||||
415 | } |
||||||
416 | |||||||
417 | $returnType = $scope->getType($node); |
||||||
418 | foreach ($returnType->getReferencedClasses() as $referencedClass) { |
||||||
419 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
420 | } |
||||||
421 | } |
||||||
422 | |||||||
423 | protected function resolvePropertyFetch(\PhpParser\Node\Expr\PropertyFetch $node, Scope $scope) |
||||||
424 | { |
||||||
425 | if ($node->name instanceof Identifier) { |
||||||
426 | $classNames = $scope->getType($node->var)->getReferencedClasses(); |
||||||
427 | foreach ($classNames as $className) { |
||||||
428 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className)) { |
||||||
429 | $this->dependencyGraphBuilder->addPropertyFetch( |
||||||
430 | $this->depender, |
||||||
431 | $dependee->getNativeReflection(), |
||||||
432 | $node->name->toString(), |
||||||
433 | $scope->getFunctionName() |
||||||
434 | ); |
||||||
435 | } |
||||||
436 | } |
||||||
437 | } |
||||||
438 | |||||||
439 | $returnType = $scope->getType($node); |
||||||
440 | foreach ($returnType->getReferencedClasses() as $referencedClass) { |
||||||
441 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
442 | } |
||||||
443 | } |
||||||
444 | |||||||
445 | protected function resolveStaticCall(\PhpParser\Node\Expr\StaticCall $node, Scope $scope) |
||||||
446 | { |
||||||
447 | if ($node->name instanceof Identifier) { |
||||||
448 | if ($node->class instanceof \PhpParser\Node\Name) { |
||||||
449 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) { |
||||||
450 | $this->dependencyGraphBuilder->addMethodCall($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName()); |
||||||
451 | } |
||||||
452 | } else { |
||||||
453 | foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) { |
||||||
454 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($referencedClass)) { |
||||||
455 | $this->dependencyGraphBuilder->addMethodCall($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName()); |
||||||
456 | } |
||||||
457 | } |
||||||
458 | } |
||||||
459 | } |
||||||
460 | |||||||
461 | $returnType = $scope->getType($node); |
||||||
462 | foreach ($returnType->getReferencedClasses() as $referencedClass) { |
||||||
463 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
464 | } |
||||||
465 | } |
||||||
466 | |||||||
467 | protected function resolveClassConstFetch(\PhpParser\Node\Expr\ClassConstFetch $node, Scope $scope) |
||||||
468 | { |
||||||
469 | if ($node->class instanceof \PhpParser\Node\Name) { |
||||||
470 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) { |
||||||
471 | $this->dependencyGraphBuilder->addConstFetch( |
||||||
472 | $this->depender, |
||||||
473 | $dependee->getNativeReflection(), |
||||||
474 | $node->name->toString(), |
||||||
0 ignored issues
–
show
The method
toString() does not exist on PhpParser\Node\Expr\Error .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
475 | $scope->getFunctionName() |
||||||
476 | ); |
||||||
477 | } |
||||||
478 | } else { |
||||||
479 | foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) { |
||||||
480 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($referencedClass)) { |
||||||
481 | $this->dependencyGraphBuilder->addConstFetch($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName()); |
||||||
482 | } |
||||||
483 | } |
||||||
484 | } |
||||||
485 | |||||||
486 | $returnType = $scope->getType($node); |
||||||
487 | foreach ($returnType->getReferencedClasses() as $referencedClass) { |
||||||
488 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
489 | } |
||||||
490 | } |
||||||
491 | |||||||
492 | protected function resolveStaticPropertyFetch(\PhpParser\Node\Expr\StaticPropertyFetch $node, Scope $scope) |
||||||
493 | { |
||||||
494 | if ($node->class instanceof \PhpParser\Node\Name) { |
||||||
495 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) { |
||||||
496 | $this->dependencyGraphBuilder->addPropertyFetch($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName()); |
||||||
0 ignored issues
–
show
The method
toString() does not exist on PhpParser\Node\Expr .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
497 | } |
||||||
498 | } else { |
||||||
499 | foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) { |
||||||
500 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) { |
||||||
0 ignored issues
–
show
$node->class of type PhpParser\Node\Expr is incompatible with the type PhpParser\Node\Name expected by parameter $name of PHPStan\Analyser\Scope::resolveName() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
501 | $this->dependencyGraphBuilder->addPropertyFetch($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName()); |
||||||
502 | } |
||||||
503 | } |
||||||
504 | } |
||||||
505 | |||||||
506 | $returnType = $scope->getType($node); |
||||||
507 | foreach ($returnType->getReferencedClasses() as $referencedClass) { |
||||||
508 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
509 | } |
||||||
510 | } |
||||||
511 | |||||||
512 | protected function resolveNew(\PhpParser\Node\Expr\New_ $node, Scope $scope) |
||||||
513 | { |
||||||
514 | if ($node->class instanceof \PhpParser\Node\Name) { |
||||||
515 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) { |
||||||
516 | $this->dependencyGraphBuilder->addNew($this->depender, $dependee->getNativeReflection(), $scope->getFunctionName()); |
||||||
517 | } |
||||||
518 | } |
||||||
519 | } |
||||||
520 | |||||||
521 | protected function resolveTraitUse(\PhpParser\Node\Stmt\TraitUse $node) |
||||||
522 | { |
||||||
523 | foreach ($node->traits as $traitName) { |
||||||
524 | if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($traitName->toString())) { |
||||||
525 | $this->dependencyGraphBuilder->addUseTrait($this->depender, $dependee->getNativeReflection()); |
||||||
526 | } |
||||||
527 | } |
||||||
528 | } |
||||||
529 | |||||||
530 | protected function resolveInstanceOf(\PhpParser\Node\Expr\Instanceof_ $node, Scope $scope) |
||||||
531 | { |
||||||
532 | if ($node->class instanceof \PhpParser\Node\Name) { |
||||||
533 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($scope->resolveName($node->class)); |
||||||
534 | } |
||||||
535 | } |
||||||
536 | |||||||
537 | protected function resolveCatch(\PhpParser\Node\Stmt\Catch_ $node, Scope $scope) |
||||||
538 | { |
||||||
539 | foreach ($node->types as $type) { |
||||||
540 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($scope->resolveName($type)); |
||||||
541 | } |
||||||
542 | } |
||||||
543 | |||||||
544 | protected function resolveArrayDimFetch(\PhpParser\Node\Expr\ArrayDimFetch $node, Scope $scope) |
||||||
545 | { |
||||||
546 | if ($node->dim !== null) { |
||||||
547 | $varType = $scope->getType($node->var); |
||||||
548 | $dimType = $scope->getType($node->dim); |
||||||
549 | |||||||
550 | foreach ($varType->getOffsetValueType($dimType)->getReferencedClasses() as $referencedClass) { |
||||||
551 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
552 | } |
||||||
553 | } |
||||||
554 | } |
||||||
555 | |||||||
556 | protected function resolveForeach(\PhpParser\Node\Stmt\Foreach_ $node, Scope $scope) |
||||||
557 | { |
||||||
558 | $exprType = $scope->getType($node->expr); |
||||||
559 | if ($node->keyVar !== null) { |
||||||
560 | foreach ($exprType->getIterableKeyType()->getReferencedClasses() as $referencedClass) { |
||||||
561 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
562 | } |
||||||
563 | } |
||||||
564 | |||||||
565 | foreach ($exprType->getIterableValueType()->getReferencedClasses() as $referencedClass) { |
||||||
566 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
567 | } |
||||||
568 | } |
||||||
569 | |||||||
570 | protected function resolveArray(\PhpParser\Node\Expr\Array_ $node, Scope $scope) |
||||||
571 | { |
||||||
572 | $arrayType = $scope->getType($node); |
||||||
573 | if (!$arrayType->isCallable()->no()) { |
||||||
574 | foreach ($arrayType->getCallableParametersAcceptors($scope) as $variant) { |
||||||
575 | $referencedClasses = $variant->getReturnType()->getReferencedClasses(); |
||||||
576 | foreach ($referencedClasses as $referencedClass) { |
||||||
577 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass); |
||||||
578 | } |
||||||
579 | } |
||||||
580 | } |
||||||
581 | } |
||||||
582 | |||||||
583 | /** |
||||||
584 | * @param \PhpParser\Node\Stmt\PropertyProperty $node |
||||||
585 | * @param Scope $scope |
||||||
586 | * @throws \PHPStan\Reflection\MissingPropertyFromReflectionException |
||||||
587 | */ |
||||||
588 | protected function resolvePropertyProperty(\PhpParser\Node\Stmt\PropertyProperty $node, Scope $scope) |
||||||
589 | { |
||||||
590 | if (!$scope->isInClass()) { |
||||||
591 | throw new \PHPStan\ShouldNotHappenException(); |
||||||
592 | } |
||||||
593 | $nativeProperty = $scope->getClassReflection()->getNativeProperty($node->name->name); |
||||||
594 | if ($nativeProperty instanceof PhpPropertyReflection) { |
||||||
0 ignored issues
–
show
|
|||||||
595 | $type = $nativeProperty->getType(); |
||||||
596 | if ($type instanceof TypeWithClassName) { |
||||||
597 | $this->addDependencyWhenResolveClassReflectionIsSucceeded($type->getClassName()); |
||||||
598 | } |
||||||
599 | } |
||||||
600 | } |
||||||
601 | } |
||||||
602 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.