DependencyResolver   F
last analyzed

Complexity

Total Complexity 118

Size/Duplication

Total Lines 569
Duplicated Lines 0 %

Importance

Changes 22
Bugs 3 Features 3
Metric Value
eloc 233
c 22
b 3
f 3
dl 0
loc 569
rs 2
wmc 118

27 Methods

Rating   Name   Duplication   Size   Complexity  
A resolveForeach() 0 11 4
A resolveTraitUse() 0 5 3
A getDependerReflection() 0 16 3
A resolveMethodCall() 0 19 5
A resolveFuncCall() 0 22 2
A resolveInstanceOf() 0 4 2
A resolveClassReflection() 0 6 2
A resolveStaticPropertyFetch() 0 17 6
B resolveStaticCall() 0 19 7
A resolveInterfaceNode() 0 6 4
A resolvePropertyFetch() 0 19 5
A resolveClassConstFetch() 0 22 6
A addDependencyWhenResolveClassReflectionIsSucceeded() 0 4 2
A resolveNew() 0 5 3
A getFunctionReflection() 0 8 2
D resolveDependencies() 0 104 22
A extractFromParametersAcceptor() 0 24 4
A resolvePropertyProperty() 0 10 4
A resolveArray() 0 8 4
A resolveClosure() 0 14 4
A resolveFunction() 0 16 3
A resolveArrayDimFetch() 0 8 3
A resolveClassMethod() 0 32 6
A resolveCatch() 0 4 2
A __construct() 0 5 1
B resolveClassNode() 0 20 7
A resolveClassReflectionOrAddUnkownDependency() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like DependencyResolver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DependencyResolver, and based on these observations, apply Extract Interface, too.

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
Documentation Bug introduced by
It seems like $this->getDependerReflection($node, $scope) can also be of type PHPStan\Reflection\ClassReflection. However, the property $depender is declared as type ReflectionClass. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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
Bug introduced by
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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
introduced by
The condition $node->getDocComment() !== null is always false.
Loading history...
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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
introduced by
$node is never a sub-type of PhpParser\Node\Identifier.
Loading history...
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
Bug introduced by
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 ignore-call  annotation

474
                    $node->name->/** @scrutinizer ignore-call */ 
475
                                 toString(),

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.

Loading history...
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
Bug introduced by
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 ignore-call  annotation

496
                $this->dependencyGraphBuilder->addPropertyFetch($this->depender, $dependee->getNativeReflection(), $node->name->/** @scrutinizer ignore-call */ toString(), $scope->getFunctionName());

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.

Loading history...
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
Bug introduced by
$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 ignore-type  annotation

500
                if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName(/** @scrutinizer ignore-type */ $node->class))) {
Loading history...
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
introduced by
$nativeProperty is always a sub-type of PHPStan\Reflection\Php\PhpPropertyReflection.
Loading history...
595
            $type = $nativeProperty->getType();
596
            if ($type instanceof TypeWithClassName) {
597
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($type->getClassName());
598
            }
599
        }
600
    }
601
}
602