Passed
Push — master ( 056350...65484c )
by Satoshi
05:02
created

DependencyResolver::resolveStaticCall()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 6
nop 2
dl 0
loc 17
rs 9.2222
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace DependencyAnalyzer\DependencyDumper;
5
6
use DependencyAnalyzer\DependencyGraphBuilder;
7
use DependencyAnalyzer\Exceptions\ResolveDependencyException;
8
use PHPStan\AnalysedCodeException;
9
use PHPStan\Analyser\Scope;
10
use PHPStan\Broker\Broker;
11
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
12
use PHPStan\PhpDocParser\Lexer\Lexer;
13
use PHPStan\PhpDocParser\Parser\PhpDocParser;
14
use PHPStan\PhpDocParser\Parser\TokenIterator;
15
use PHPStan\Reflection\ClassReflection;
16
use PHPStan\Reflection\ParametersAcceptorSelector;
17
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
18
use PHPStan\Reflection\Php\PhpMethodReflection;
19
use PHPStan\Reflection\Php\PhpPropertyReflection;
20
use PHPStan\Reflection\ReflectionWithFilename;
21
use PHPStan\ShouldNotHappenException;
22
use PHPStan\Type\ClosureType;
23
use PHPStan\Type\TypeWithClassName;
24
25
/**
26
 * @canOnlyUsedBy \DependencyAnalyzer\DependencyDumper\
27
 */
28
class DependencyResolver
29
{
30
    /**
31
     * @var Broker
32
     */
33
    protected $broker;
34
35
    /**
36
     * @var Lexer
37
     */
38
    protected $phpDocLexer;
39
40
    /**
41
     * @var PhpDocParser
42
     */
43
    protected $phpDocParser;
44
45
    /**
46
     * @var DependencyGraphBuilder
47
     */
48
    protected $dependencyGraphBuilder;
49
50
    /**
51
     * @var \ReflectionClass
52
     */
53
    protected $depender = null;
54
55
    public function __construct(Broker $broker, Lexer $phpDocLexer, PhpDocParser $phpDocParser)
56
    {
57
        $this->broker = $broker;
58
        $this->phpDocLexer = $phpDocLexer;
59
        $this->phpDocParser = $phpDocParser;
60
    }
61
62
    protected function getDependerReflection(\PhpParser\Node $node, Scope $scope): ?ClassReflection
63
    {
64
        if ($scope->isInClass()) {
65
            return $scope->getClassReflection();
66
        } else {
67
            // Maybe, class declare statement
68
            // ex:
69
            //   class Hoge {}
70
            //   abstract class Hoge {}
71
            //   interface Hoge {}
72
            if ($node instanceof \PhpParser\Node\Stmt\ClassLike) {
73
                return $this->resolveClassReflection($node->namespacedName->toString());
74
            }
75
        }
76
77
        return null;
78
    }
79
80
    /**
81
     * @param \PhpParser\Node $node
82
     * @param Scope $scope
83
     * @param DependencyGraphBuilder $dependencyGraphBuilder
84
     * @return ReflectionWithFilename[]
85
     */
86
    public function resolveDependencies(\PhpParser\Node $node, Scope $scope, DependencyGraphBuilder $dependencyGraphBuilder): array
87
    {
88
        try {
89
            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...
90
                return [];
91
            }
92
            $this->depender = $this->depender->getNativeReflection();
93
            $this->dependencyGraphBuilder = $dependencyGraphBuilder;
94
95
            if ($node instanceof \PhpParser\Node\Stmt\Class_) {
96
                // define class statement
97
                // ex: class SomeClass {}
98
                $this->resolveClassNode($node);
99
            } elseif ($node instanceof \PhpParser\Node\Stmt\Interface_) {
100
                // define interface statement
101
                // ex: interface SomeInterface {}
102
                $this->resolveInterfaceNode($node);
103
            } elseif ($node instanceof \PhpParser\Node\Stmt\ClassMethod) {
104
                // define class method statement
105
                // ex:
106
                //   class SomeClass {
107
                //       function ClassMethod() {}
108
                //   }
109
                $this->resolveClassMethod($node, $scope);
110
//            } elseif ($node instanceof \PhpParser\Node\Stmt\Function_) {
111
//                // define function statement
112
//                // ex: function SomeFunction() {}
113
//                return $this->resolveFunction($node);
114
            } elseif ($node instanceof \PhpParser\Node\Expr\Closure) {
115
                // closure expression
116
                // ex:
117
                //   function (SomeClass1 $someClass1): SomeClass2 {
118
                //       // some logic.
119
                //   }
120
                $this->resolveClosure($node, $scope);
121
            } elseif ($node instanceof \PhpParser\Node\Expr\FuncCall) {
122
                // function call expression
123
                // ex: someFunction();
124
                $this->resolveFuncCall($node, $scope);
125
            } elseif ($node instanceof \PhpParser\Node\Expr\MethodCall) {
126
                // method call expression
127
                // ex: $someObject->someMethod();
128
                $this->resolveMethodCall($node, $scope);
129
            } elseif ($node instanceof \PhpParser\Node\Expr\PropertyFetch) {
130
                // property fetch expression
131
                // ex: $someObject->somePublicProperty;
132
                $this->resolvePropertyFetch($node, $scope);
133
            } elseif ($node instanceof \PhpParser\Node\Expr\StaticCall) {
134
                // static method call expression
135
                // ex: SomeClass::someStaticMethod()
136
                $this->resolveStaticCall($node, $scope);
137
            } elseif ($node instanceof \PhpParser\Node\Expr\ClassConstFetch) {
138
                // class const fetch expression
139
                // ex: SomeClass::SOME_CONST
140
                $this->resolveClassConstFetch($node, $scope);
141
            } elseif ($node instanceof \PhpParser\Node\Expr\StaticPropertyFetch) {
142
                // class static property fetch expression
143
                // ex: SomeClass::$someStaticProperty
144
                $this->resolveStaticPropertyFetch($node, $scope);
145
            } elseif ($node instanceof \PhpParser\Node\Expr\New_) {
146
                // new object expression
147
                // ex: new SomeClass();
148
                $this->resolveNew($node, $scope);
149
            } elseif ($node instanceof \PhpParser\Node\Stmt\TraitUse) {
150
                // use trait expression
151
                // ex:
152
                //   class SomeClass {
153
                //     use SomeTrait;
154
                //   }
155
                $this->resolveTraitUse($node);
156
            } elseif ($node instanceof \PhpParser\Node\Expr\Instanceof_) {
157
                // instanceof expression
158
                // ex: if ($someObject instanceof SomeClass) {}
159
                $this->resolveInstanceOf($node, $scope);
160
            } elseif ($node instanceof \PhpParser\Node\Stmt\Catch_) {
161
                // catch expression
162
                // ex: try {} catch (SomeException $e) {}
163
                $this->resolveCatch($node, $scope);
164
            } elseif ($node instanceof \PhpParser\Node\Expr\ArrayDimFetch) {
165
                // array dim fetch expression
166
                // ex: $someArray[2]
167
                $this->resolveArrayDimFetch($node, $scope);
168
            } elseif ($node instanceof \PhpParser\Node\Stmt\Foreach_) {
169
                // foreach access expression
170
                // foreach ($someArray as $key => $someObject) {}
171
                $this->resolveForeach($node, $scope);
172
            } elseif ($node instanceof \PhpParser\Node\Expr\Array_) {
173
                $this->resolveArray($node, $scope);
174
            } elseif ($node instanceof \PhpParser\Node\Stmt\PropertyProperty) {
175
                // property with phpdoc
176
                // ex:
177
                //   class SomeClass {
178
                //     /** @var SomeClass2 $someProperty */
179
                //     private $someProperty;
180
                //   }
181
                $this->resolvePropertyProperty($node, $scope);
182
            }
183
        } catch (AnalysedCodeException $e) {
184
            throw new ResolveDependencyException($node, 'resolving node dependency is failed.', 0, $e);
185
        } catch (ShouldNotHappenException $e) {
186
            throw new ResolveDependencyException($node, 'resolving node dependency is failed.', 0, $e);
187
        }
188
189
        return [];
190
    }
191
192
    public function resolveClassReflection(string $className): ?ClassReflection
193
    {
194
        try {
195
            return $this->broker->getClass($className);
196
        } catch (\PHPStan\Broker\ClassNotFoundException $e) {
197
            return null;
198
//            return new UnknownClassReflection($className);
199
        }
200
    }
201
202
    protected function getFunctionReflection(\PhpParser\Node\Name $nameNode, ?Scope $scope): ReflectionWithFilename
203
    {
204
        $reflection = $this->broker->getFunction($nameNode, $scope);
205
        if (!$reflection instanceof ReflectionWithFilename) {
206
            throw new \PHPStan\Broker\FunctionNotFoundException((string) $nameNode);
207
        }
208
209
        return $reflection;
210
    }
211
212
    /**
213
     * @param ParametersAcceptorWithPhpDocs $parametersAcceptor
214
     * @return ClassReflection[]
215
     */
216
    protected function extractFromParametersAcceptor(ParametersAcceptorWithPhpDocs $parametersAcceptor): array
217
    {
218
        $dependenciesReflections = [];
219
220
        foreach ($parametersAcceptor->getParameters() as $parameter) {
221
            $referencedClasses = array_merge(
222
                $parameter->getNativeType()->getReferencedClasses(),
223
                $parameter->getPhpDocType()->getReferencedClasses()
224
            );
225
226
            foreach ($referencedClasses as $referencedClass) {
227
                $dependenciesReflections[] = $this->resolveClassReflection($referencedClass);
228
            }
229
        }
230
231
        $returnTypeReferencedClasses = array_merge(
232
            $parametersAcceptor->getNativeReturnType()->getReferencedClasses(),
233
            $parametersAcceptor->getPhpDocReturnType()->getReferencedClasses()
234
        );
235
        foreach ($returnTypeReferencedClasses as $referencedClass) {
236
            $dependenciesReflections[] = $this->resolveClassReflection($referencedClass);
237
        }
238
239
        return $dependenciesReflections;
240
    }
241
242
    protected function resolveClassReflectionOrAddUnkownDependency(string $className): ?ClassReflection
243
    {
244
        if (!is_null($classReflection = $this->resolveClassReflection($className))) {
245
            return $classReflection;
246
        }
247
248
        $this->dependencyGraphBuilder->addUnknownDependency($this->depender, $className);
249
        return null;
250
    }
251
252
    protected function addDependencyWhenResolveClassReflectionIsSucceeded(string $className): void
253
    {
254
        if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className)) {
255
            $this->dependencyGraphBuilder->addDependency($this->depender, $dependee->getNativeReflection());
256
        }
257
    }
258
259
    protected function resolveClassNode(\PhpParser\Node\Stmt\Class_ $node): void
260
    {
261
        if ($node->extends !== null) {
262
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($node->extends->toString())) {
263
                $this->dependencyGraphBuilder->addExtends($this->depender, $dependee->getNativeReflection());
264
            }
265
        }
266
        foreach ($node->implements as $className) {
267
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className->toString())) {
268
                $this->dependencyGraphBuilder->addImplements($this->depender, $dependee->getNativeReflection());
269
            }
270
        }
271
        if ($node->getDocComment() !== null) {
0 ignored issues
show
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...
introduced by
The condition $node->getDocComment() !== null is always false.
Loading history...
272
            $tokens = new TokenIterator($this->phpDocLexer->tokenize($node->getDocComment()->getText()));
273
            $phpDocNode = $this->phpDocParser->parse($tokens);
274
            foreach ($phpDocNode->getTagsByName('@dependOn') as $phpDocTagNode) {
275
                /** @var PhpDocTagNode $phpDocTagNode */
276
                preg_match('/^@dependOn\s+(.+)$/', $phpDocTagNode->__toString(), $matches);
277
278
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($matches[1]);
279
            };
280
        }
281
    }
282
283
    protected function resolveInterfaceNode(\PhpParser\Node\Stmt\Interface_ $node): void
284
    {
285
        if ($node->extends !== null) {
286
            foreach ($node->extends as $className) {
287
                if ($dependee = $this->resolveClassReflection($className->toString())) {
288
                    $this->dependencyGraphBuilder->addExtends($this->depender, $dependee->getNativeReflection());
289
                }
290
            }
291
        }
292
    }
293
294
    /**
295
     * @param \PhpParser\Node\Stmt\ClassMethod $node
296
     * @param Scope $scope
297
     * @throws \PHPStan\Reflection\MissingMethodFromReflectionException
298
     */
299
    protected function resolveClassMethod(\PhpParser\Node\Stmt\ClassMethod $node, Scope $scope)
300
    {
301
        if (!$scope->isInClass()) {
302
            throw new \PHPStan\ShouldNotHappenException();
303
        }
304
305
        $nativeMethod = $scope->getClassReflection()->getNativeMethod($node->name->name);
306
        if ($nativeMethod instanceof PhpMethodReflection) {
307
            /** @var \PHPStan\Reflection\ParametersAcceptorWithPhpDocs $parametersAcceptor */
308
            $parametersAcceptor = ParametersAcceptorSelector::selectSingle($nativeMethod->getVariants());
309
310
//            foreach ($this->extractFromParametersAcceptor($parametersAcceptor) as $classReflection) {
311
//                $this->dependencyGraphBuilder->addDependency($this->depender, $classReflection->getNativeReflection());
312
//            }
313
314
            foreach ($parametersAcceptor->getParameters() as $parameter) {
315
                $referencedClasses = array_merge(
316
                    $parameter->getNativeType()->getReferencedClasses(),
317
                    $parameter->getPhpDocType()->getReferencedClasses()
318
                );
319
320
                foreach ($referencedClasses as $referencedClass) {
321
                    $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
322
                }
323
            }
324
325
            $returnTypeReferencedClasses = array_merge(
326
                $parametersAcceptor->getNativeReturnType()->getReferencedClasses(),
327
                $parametersAcceptor->getPhpDocReturnType()->getReferencedClasses()
328
            );
329
            foreach ($returnTypeReferencedClasses as $referencedClass) {
330
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
331
            }
332
        }
333
    }
334
335
    /**
336
     * @param \PhpParser\Node\Stmt\Function_ $node
337
     * @return ReflectionWithFilename[]
338
     * @throws \PHPStan\Broker\FunctionNotFoundException
339
     */
340
    protected function resolveFunction(\PhpParser\Node\Stmt\Function_ $node): array
341
    {
342
        $functionName = $node->name->name;
343
        if (isset($node->namespacedName)) {
344
            $functionName = (string)$node->namespacedName;
345
        }
346
        $functionNameName = new \PhpParser\Node\Name($functionName);
347
        if ($this->broker->hasCustomFunction($functionNameName, null)) {
348
            $functionReflection = $this->broker->getCustomFunction($functionNameName, null);
349
350
            /** @var \PHPStan\Reflection\ParametersAcceptorWithPhpDocs $parametersAcceptor */
351
            $parametersAcceptor = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants());
352
            return $this->extractFromParametersAcceptor($parametersAcceptor);
353
        }
354
355
        return [];
356
    }
357
358
    protected function resolveClosure(\PhpParser\Node\Expr\Closure $node, Scope $scope)
359
    {
360
        /** @var ClosureType $closureType */
361
        $closureType = $scope->getType($node);
362
        foreach ($closureType->getParameters() as $parameter) {
363
            $referencedClasses = $parameter->getType()->getReferencedClasses();
364
            foreach ($referencedClasses as $referencedClass) {
365
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
366
            }
367
        }
368
369
        $returnTypeReferencedClasses = $closureType->getReturnType()->getReferencedClasses();
370
        foreach ($returnTypeReferencedClasses as $referencedClass) {
371
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
372
        }
373
    }
374
375
    protected function resolveFuncCall(\PhpParser\Node\Expr\FuncCall $node, Scope $scope)
376
    {
377
//        $functionName = $node->name;
378
//        if ($functionName instanceof \PhpParser\Node\Name) {
379
//            try {
380
//                $dependenciesReflections[] = $this->getFunctionReflection($functionName, $scope);
381
//            } catch (\PHPStan\Broker\FunctionNotFoundException $e) {
382
//                // pass
383
//            }
384
//        } else {
385
//            $variants = $scope->getType($functionName)->getCallableParametersAcceptors($scope);
386
//            foreach ($variants as $variant) {
387
//                $referencedClasses = $variant->getReturnType()->getReferencedClasses();
388
//                foreach ($referencedClasses as $referencedClass) {
389
//                    $dependenciesReflections[] = $this->resolveClassReflection($referencedClass);
390
//                }
391
//            }
392
//        }
393
394
        $returnType = $scope->getType($node);
395
        foreach ($returnType->getReferencedClasses() as $referencedClass) {
396
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
397
        }
398
    }
399
400
    protected function resolveMethodCall(\PhpParser\Node\Expr\MethodCall $node, Scope $scope)
401
    {
402
        $classNames = $scope->getType($node->var)->getReferencedClasses();
403
        foreach ($classNames as $className) {
404
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className)) {
405
                $this->dependencyGraphBuilder->addMethodCall(
406
                    $this->depender,
407
                    $dependee->getNativeReflection(),
408
                    $node->name->toString(),
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

408
                    $node->name->/** @scrutinizer ignore-call */ 
409
                                 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...
409
                    $scope->getFunctionName()
410
                );
411
            }
412
        }
413
414
        $returnType = $scope->getType($node);
415
        foreach ($returnType->getReferencedClasses() as $referencedClass) {
416
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
417
        }
418
    }
419
420
    protected function resolvePropertyFetch(\PhpParser\Node\Expr\PropertyFetch $node, Scope $scope)
421
    {
422
        $classNames = $scope->getType($node->var)->getReferencedClasses();
423
        foreach ($classNames as $className) {
424
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($className)) {
425
                $this->dependencyGraphBuilder->addPropertyFetch(
426
                    $this->depender,
427
                    $dependee->getNativeReflection(),
428
                    $node->name->toString(),
429
                    $scope->getFunctionName()
430
                );
431
            }
432
        }
433
434
        $returnType = $scope->getType($node);
435
        foreach ($returnType->getReferencedClasses() as $referencedClass) {
436
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
437
        }
438
    }
439
440
    protected function resolveStaticCall(\PhpParser\Node\Expr\StaticCall $node, Scope $scope)
441
    {
442
        if ($node->class instanceof \PhpParser\Node\Name) {
443
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) {
444
                $this->dependencyGraphBuilder->addMethodCall($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName());
445
            }
446
        } else {
447
            foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) {
448
                if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($referencedClass)) {
449
                    $this->dependencyGraphBuilder->addMethodCall($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName());
450
                }
451
            }
452
        }
453
454
        $returnType = $scope->getType($node);
455
        foreach ($returnType->getReferencedClasses() as $referencedClass) {
456
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
457
        }
458
    }
459
460
    protected function resolveClassConstFetch(\PhpParser\Node\Expr\ClassConstFetch $node, Scope $scope)
461
    {
462
        if ($node->class instanceof \PhpParser\Node\Name) {
463
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) {
464
                $this->dependencyGraphBuilder->addConstFetch(
465
                    $this->depender,
466
                    $dependee->getNativeReflection(),
467
                    $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

467
                    $node->name->/** @scrutinizer ignore-call */ 
468
                                 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...
468
                    $scope->getFunctionName()
469
                );
470
            }
471
        } else {
472
            foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) {
473
                if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($referencedClass)) {
474
                    $this->dependencyGraphBuilder->addConstFetch($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName());
475
                }
476
            }
477
        }
478
479
        $returnType = $scope->getType($node);
480
        foreach ($returnType->getReferencedClasses() as $referencedClass) {
481
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
482
        }
483
    }
484
485
    protected function resolveStaticPropertyFetch(\PhpParser\Node\Expr\StaticPropertyFetch $node, Scope $scope)
486
    {
487
        if ($node->class instanceof \PhpParser\Node\Name) {
488
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) {
489
                $this->dependencyGraphBuilder->addPropertyFetch($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName());
490
            }
491
        } else {
492
            foreach ($scope->getType($node->class)->getReferencedClasses() as $referencedClass) {
493
                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

493
                if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName(/** @scrutinizer ignore-type */ $node->class))) {
Loading history...
494
                    $this->dependencyGraphBuilder->addPropertyFetch($this->depender, $dependee->getNativeReflection(), $node->name->toString(), $scope->getFunctionName());
495
                }
496
            }
497
        }
498
499
        $returnType = $scope->getType($node);
500
        foreach ($returnType->getReferencedClasses() as $referencedClass) {
501
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
502
        }
503
    }
504
505
    protected function resolveNew(\PhpParser\Node\Expr\New_ $node, Scope $scope)
506
    {
507
        if ($node->class instanceof \PhpParser\Node\Name) {
508
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($scope->resolveName($node->class))) {
509
                $this->dependencyGraphBuilder->addNew($this->depender, $dependee->getNativeReflection(), $scope->getFunctionName());
510
            }
511
        }
512
    }
513
514
    protected function resolveTraitUse(\PhpParser\Node\Stmt\TraitUse $node)
515
    {
516
        foreach ($node->traits as $traitName) {
517
            if ($dependee = $this->resolveClassReflectionOrAddUnkownDependency($traitName->toString())) {
518
                $this->dependencyGraphBuilder->addUseTrait($this->depender, $dependee->getNativeReflection());
519
            }
520
        }
521
    }
522
523
    protected function resolveInstanceOf(\PhpParser\Node\Expr\Instanceof_ $node, Scope $scope)
524
    {
525
        if ($node->class instanceof \PhpParser\Node\Name) {
526
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($scope->resolveName($node->class));
527
        }
528
    }
529
530
    protected function resolveCatch(\PhpParser\Node\Stmt\Catch_ $node, Scope $scope)
531
    {
532
        foreach ($node->types as $type) {
533
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($scope->resolveName($type));
534
        }
535
    }
536
537
    protected function resolveArrayDimFetch(\PhpParser\Node\Expr\ArrayDimFetch $node, Scope $scope)
538
    {
539
        if ($node->dim !== null) {
540
            $varType = $scope->getType($node->var);
541
            $dimType = $scope->getType($node->dim);
542
543
            foreach ($varType->getOffsetValueType($dimType)->getReferencedClasses() as $referencedClass) {
544
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
545
            }
546
        }
547
    }
548
549
    protected function resolveForeach(\PhpParser\Node\Stmt\Foreach_ $node, Scope $scope)
550
    {
551
        $exprType = $scope->getType($node->expr);
552
        if ($node->keyVar !== null) {
553
            foreach ($exprType->getIterableKeyType()->getReferencedClasses() as $referencedClass) {
554
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
555
            }
556
        }
557
558
        foreach ($exprType->getIterableValueType()->getReferencedClasses() as $referencedClass) {
559
            $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
560
        }
561
    }
562
563
    protected function resolveArray(\PhpParser\Node\Expr\Array_ $node, Scope $scope)
564
    {
565
        $arrayType = $scope->getType($node);
566
        if (!$arrayType->isCallable()->no()) {
567
            foreach ($arrayType->getCallableParametersAcceptors($scope) as $variant) {
568
                $referencedClasses = $variant->getReturnType()->getReferencedClasses();
569
                foreach ($referencedClasses as $referencedClass) {
570
                    $this->addDependencyWhenResolveClassReflectionIsSucceeded($referencedClass);
571
                }
572
            }
573
        }
574
    }
575
576
    /**
577
     * @param \PhpParser\Node\Stmt\PropertyProperty $node
578
     * @param Scope $scope
579
     * @throws \PHPStan\Reflection\MissingPropertyFromReflectionException
580
     */
581
    protected function resolvePropertyProperty(\PhpParser\Node\Stmt\PropertyProperty $node, Scope $scope)
582
    {
583
        if (!$scope->isInClass()) {
584
            throw new \PHPStan\ShouldNotHappenException();
585
        }
586
        $nativeProperty = $scope->getClassReflection()->getNativeProperty($node->name->name);
587
        if ($nativeProperty instanceof PhpPropertyReflection) {
0 ignored issues
show
introduced by
$nativeProperty is always a sub-type of PHPStan\Reflection\Php\PhpPropertyReflection.
Loading history...
588
            $type = $nativeProperty->getType();
589
            if ($type instanceof TypeWithClassName) {
590
                $this->addDependencyWhenResolveClassReflectionIsSucceeded($type->getClassName());
591
            }
592
        }
593
    }
594
}
595