Passed
Push — master ( 50f068...bac002 )
by Satoshi
02:14
created

CollectDependenciesVisitor::__invoke()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 12
nc 16
nop 2
dl 0
loc 22
rs 8.8333
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace DependencyAnalyzer\DependencyDumper;
5
6
use DependencyAnalyzer\DependencyGraph;
7
use DependencyAnalyzer\DependencyGraph\DependencyGraphBuilder;
8
use DependencyAnalyzer\Exceptions\ResolveDependencyException;
9
use DependencyAnalyzer\Exceptions\ShouldNotHappenException;
10
use PHPStan\Analyser\Scope;
11
use PHPStan\Reflection\ClassReflection;
12
use PHPStan\Reflection\Php\PhpFunctionReflection;
13
14
/**
15
 * @canOnlyUsedBy \DependencyAnalyzer\DependencyDumper
16
 */
17
class CollectDependenciesVisitor
18
{
19
    /**
20
     * @var DependencyResolver
21
     */
22
    protected $dependencyResolver;
23
24
    /**
25
     * @var DependencyGraphBuilder
26
     */
27
    protected $dependencyGraphBuilder;
28
29
    /**
30
     * @var ObserverInterface
31
     */
32
    protected $observer;
33
34
    /**
35
     * @var string
36
     */
37
    protected $file = null;
38
39
    public function __construct(DependencyResolver $dependencyResolver, DependencyGraphBuilder $dependencyGraphBuilder)
40
    {
41
        $this->dependencyResolver = $dependencyResolver;
42
        $this->dependencyGraphBuilder = $dependencyGraphBuilder;
43
    }
44
45
    public function setFile(string $file)
46
    {
47
        $this->file = $file;
48
    }
49
50
    public function setObserver(ObserverInterface $observer = null)
51
    {
52
        $this->observer = $observer;
53
    }
54
55
    public function __invoke(\PhpParser\Node $node, Scope $scope): void
56
    {
57
        try {
58
            foreach ($this->dependencyResolver->resolveDependencies($node, $scope) as $dependeeReflection) {
59
                if ($dependeeReflection instanceof ClassReflection) {
60
                    $this->addDependency($node, $scope, $dependeeReflection);
61
                } elseif ($dependeeReflection instanceof UnknownClassReflection) {
62
                    $this->addDependency($node, $scope, $dependeeReflection);
63
                } elseif ($dependeeReflection instanceof PhpFunctionReflection) {
64
                    // function call
65
                    // ex:
66
                    //   array_map(...);
67
                    //   var_dump(...);
68
                } else {
69
                    // error of DependencyResolver
70
                    throw new ResolveDependencyException($node, 'resolving node dependency is failed.');
71
                }
72
            }
73
        } catch (ResolveDependencyException $e) {
74
            // TODO: error handling... But, don't throw Exception, because NodeScopeResolver will die.
75
            if ($this->observer) {
76
                $this->observer->notifyResolveDependencyError($this->file, $e);
77
            }
78
        }
79
    }
80
81
    /**
82
     * @param \PhpParser\Node $node
83
     * @param Scope $scope
84
     * @param ClassReflection|UnknownClassReflection $dependeeReflection
85
     */
86
    protected function addDependency(\PhpParser\Node $node, Scope $scope, $dependeeReflection): void
87
    {
88
        if ($scope->isInClass()) {
89
            if ($scope->getClassReflection()->getDisplayName() === $dependeeReflection->getDisplayName()) {
90
                // call same class method/property
91
            } else {
92
                $this->dependencyGraphBuilder->addDependency($scope->getClassReflection(), $dependeeReflection);
93
            }
94
        } else {
95
            // Maybe, class declare statement
96
            // ex:
97
            //   class Hoge {}
98
            //   abstract class Hoge {}
99
            //   interface Hoge {}
100
            if ($node instanceof \PhpParser\Node\Stmt\ClassLike) {
101
                $dependerReflection = $this->dependencyResolver->resolveClassReflection($node->namespacedName->toString());
102
                if ($dependerReflection instanceof ClassReflection) {
103
                    $this->dependencyGraphBuilder->addDependency($dependerReflection, $dependeeReflection);
104
                } else {
105
                    throw new ResolveDependencyException($node, 'resolving node dependency is failed, because unexpected node.');
106
                }
107
            }
108
        }
109
    }
110
111
    public function createDependencyGraph(): DependencyGraph
112
    {
113
        return $this->dependencyGraphBuilder->build();
114
    }
115
}
116