15
15
use PhpParser \Node \Stmt \ClassLike ;
16
16
use PhpParser \Node \Stmt \ClassMethod ;
17
17
use PhpParser \Node \Stmt \Interface_ as InterfaceNode ;
18
- use PhpParser \Node \Stmt \Property ;
18
+ use PhpParser \Node \Stmt \PropertyProperty ;
19
19
use PhpParser \Node \Stmt \Trait_ as TraitNode ;
20
+ use PhpParser \Node \Stmt \TraitUse ;
21
+ use PhpParser \NodeTraverser ;
20
22
use PhpParser \NodeVisitorAbstract ;
21
23
22
24
/**
23
25
* Implements a visitor for `class`, `interface` and `trait` nodes that generates a dependency graph.
24
26
*/
25
27
class DependencyInspectionVisitor extends NodeVisitorAbstract
26
28
{
29
+
27
30
/** @var DependencyGraph */
28
31
private $ dependencyGraph ;
29
32
30
33
/** @var NodeHelper */
31
34
private $ nodeHelper ;
32
35
36
+ /**
37
+ * @var Entity
38
+ * Holds current Entity. Stored so we can populate this entity in our dependency graph upon walking relevant child
39
+ * nodes.
40
+ */
41
+ private $ currentClassLike = null ;
42
+
33
43
/**
34
44
* Constructor.
35
45
*
@@ -43,135 +53,115 @@ public function __construct(DependencyGraph $dependencyGraph, NodeHelper $nodeHe
43
53
}
44
54
45
55
/**
46
- * @inheritDoc
56
+ * Logic to process current node. We aggressively halt walking the AST since this may contain many nodes
57
+ * If we are visiting a Classlike node, set currentClassLike so we can populate this entity in our dependency graph
58
+ * upon walking relevant child nodes like PropertyProperty and ClassMethod.
47
59
*
48
- * Inspect nodes after all visitors have run since we need the fully qualified names of nodes.
49
- */
50
- public function leaveNode (Node $ node )
51
- {
52
- if ($ node instanceof ClassNode) {
53
- $ this ->addClassNode ($ node );
54
- } elseif ($ node instanceof InterfaceNode) {
55
- $ this ->addInterfaceNode ($ node );
56
- } elseif ($ node instanceof TraitNode) {
57
- $ this ->addTraitNode ($ node );
58
- }
59
- }
60
-
61
- /**
62
- * Getter for {@link DependencyInspectionVisitor::$dependencyGraph}.
60
+ * Subparse tree we want to traverse will be something like:
61
+ * Namespace -> ClassLike -> ClassMethod
62
+ * -> TraitUse
63
+ * -> PropertyProperty
63
64
*
64
- * @return DependencyGraph
65
- */
66
- public function getDependencyGraph (): DependencyGraph
67
- {
68
- return $ this ->dependencyGraph ;
69
- }
70
-
71
- /**
72
- * @param ClassNode $node
65
+ *
66
+ * @inheritdoc
67
+ *
68
+ * @param Node $node
69
+ * @return int tells NodeTraverser whether to continue traversing
73
70
*/
74
- private function addClassNode ( ClassNode $ node )
71
+ public function enterNode ( Node $ node )
75
72
{
76
- // name is not set for anonymous classes, therefore they cannot be part of the dependency graph
77
- if ($ node ->isAnonymous ()) {
78
- return ;
79
- }
80
-
81
- $ className = (string )$ node ->namespacedName ;
82
- $ class = $ this ->dependencyGraph ->findOrCreateClass ($ className );
83
-
84
- [$ methodList , $ propertyList ] = $ this ->fetchStmtsNodes ($ node );
85
- $ class ->setMethodList ($ methodList );
86
- $ class ->setPropertyList ($ propertyList );
87
- $ class ->setIsApi ($ this ->nodeHelper ->isApiNode ($ node ));
88
-
89
- if ($ node ->extends ) {
90
- $ parentClassName = (string )$ node ->extends ;
91
- $ parentClassEntity = $ this ->dependencyGraph ->findOrCreateClass ($ parentClassName );
92
- $ class ->addExtends ($ parentClassEntity );
93
- }
94
-
95
- foreach ($ node ->implements as $ implement ) {
96
- $ interfaceName = (string )$ implement ;
97
- $ interfaceEntity = $ this ->dependencyGraph ->findOrCreateInterface ($ interfaceName );
98
- $ class ->addImplements ($ interfaceEntity );
99
- }
100
-
101
- foreach ($ this ->nodeHelper ->getTraitUses ($ node ) as $ traitUse ) {
102
- foreach ($ traitUse ->traits as $ trait ) {
103
- $ traitName = (string )$ trait ;
104
- $ traitEntity = $ this ->dependencyGraph ->findOrCreateTrait ($ traitName );
105
- $ class ->addUses ($ traitEntity );
106
- }
73
+ switch (true ) {
74
+ case $ node instanceof Node \Stmt \Namespace_:
75
+ return null ;
76
+ case $ node instanceof ClassLike:
77
+ //set currentClassLike entity
78
+ return $ this ->handleClassLike ($ node );
79
+ case $ node instanceof ClassMethod:
80
+ $ this ->currentClassLike ->addMethod ($ node );
81
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN ;
82
+ case $ node instanceof TraitUse:
83
+ foreach ($ node ->traits as $ trait ) {
84
+ $ traitName = (string )$ trait ;
85
+ $ traitEntity = $ this ->dependencyGraph ->findOrCreateTrait ($ traitName );
86
+ $ this ->currentClassLike ->addUses ($ traitEntity );
87
+ }
88
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN ;
89
+ case $ node instanceof PropertyProperty:
90
+ $ this ->currentClassLike ->addProperty ($ node );
91
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN ;
92
+ default :
93
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN ;
107
94
}
108
-
109
- $ this ->dependencyGraph ->addEntity ($ class );
110
95
}
111
96
112
97
/**
113
- * @param InterfaceNode $node
98
+ * Handles Class, Interface, and Traits nodes. Sets currentClassLike entity and will populate extends, implements,
99
+ * and API information
100
+ *
101
+ * @param ClassLike $node
102
+ * @return int|null
114
103
*/
115
- private function addInterfaceNode ( InterfaceNode $ node )
104
+ private function handleClassLike ( ClassLike $ node )
116
105
{
117
- $ interfaceName = (string )$ node ->namespacedName ;
118
- $ interface = $ this ->dependencyGraph ->findOrCreateInterface ($ interfaceName );
119
-
120
- $ interface ->setIsApi ($ this ->nodeHelper ->isApiNode ($ node ));
121
- [$ methodList ] = $ this ->fetchStmtsNodes ($ node );
122
- $ interface ->setMethodList ($ methodList );
123
-
124
- foreach ($ node ->extends as $ extend ) {
125
- $ interfaceName = (string )$ extend ;
126
- $ interfaceEntity = $ this ->dependencyGraph ->findOrCreateInterface ($ interfaceName );
127
- $ interface ->addExtends ($ interfaceEntity );
106
+ /**
107
+ * @var \PhpParser\Node\Name $namespacedName
108
+ * This is set in the NamespaceResolver visitor
109
+ */
110
+ $ namespacedName = $ node ->namespacedName ;
111
+ switch (true ) {
112
+ case $ node instanceof ClassNode:
113
+ if ($ node ->isAnonymous ()) {
114
+ return NodeTraverser::STOP_TRAVERSAL ;
115
+ }
116
+ $ this ->currentClassLike = $ this ->dependencyGraph ->findOrCreateClass ((string )$ namespacedName );
117
+ if ($ node ->extends ) {
118
+ $ parentClassName = (string )$ node ->extends ;
119
+ $ parentClassEntity = $ this ->dependencyGraph ->findOrCreateClass ($ parentClassName );
120
+ $ this ->currentClassLike ->addExtends ($ parentClassEntity );
121
+ }
122
+ foreach ($ node ->implements as $ implement ) {
123
+ $ interfaceName = (string )$ implement ;
124
+ $ interfaceEntity = $ this ->dependencyGraph ->findOrCreateInterface ($ interfaceName );
125
+ $ this ->currentClassLike ->addImplements ($ interfaceEntity );
126
+ }
127
+ break ;
128
+ case $ node instanceof InterfaceNode:
129
+ $ this ->currentClassLike = $ this ->dependencyGraph ->findOrCreateInterface ((string )$ namespacedName );
130
+ foreach ($ node ->extends as $ extend ) {
131
+ $ interfaceName = (string )$ extend ;
132
+ $ interfaceEntity = $ this ->dependencyGraph ->findOrCreateInterface ($ interfaceName );
133
+ $ this ->currentClassLike ->addExtends ($ interfaceEntity );
134
+ }
135
+ break ;
136
+ case $ node instanceof TraitNode:
137
+ $ this ->currentClassLike = $ this ->dependencyGraph ->findOrCreateTrait ((string )$ namespacedName );
138
+ break ;
128
139
}
129
-
130
- $ this -> dependencyGraph -> addEntity ( $ interface ) ;
140
+ $ this -> currentClassLike -> setIsApi ( $ this -> nodeHelper -> isApiNode ( $ node ));
141
+ return null ;
131
142
}
132
143
133
- /**
134
- * @param TraitNode $node
144
+ /*
145
+ * Unsets currentClassLike upon exiting ClassLike node. This is for cleanup, although this is not necessary since
146
+ * Classmethod, PropertyProperty, and TraitUse nodes will only be traversed after Classlike
147
+ *
148
+ * @param Node $node
149
+ * @return false|int|Node|Node[]|void|null
135
150
*/
136
- private function addTraitNode ( TraitNode $ node )
151
+ public function leaveNode ( Node $ node )
137
152
{
138
- $ traitName = (string )$ node ->namespacedName ;
139
- $ trait = $ this ->dependencyGraph ->findOrCreateTrait ($ traitName );
140
-
141
- [$ methodList , $ propertyList ] = $ this ->fetchStmtsNodes ($ node );
142
- $ trait ->setMethodList ($ methodList );
143
- $ trait ->setPropertyList ($ propertyList );
144
- $ trait ->setIsApi ($ this ->nodeHelper ->isApiNode ($ node ));
145
-
146
- foreach ($ this ->nodeHelper ->getTraitUses ($ node ) as $ traitUse ) {
147
- foreach ($ traitUse ->traits as $ parentTrait ) {
148
- $ parentTraitName = (string )$ parentTrait ;
149
- $ parentTraitEntity = $ this ->dependencyGraph ->findOrCreateTrait ($ parentTraitName );
150
- $ trait ->addUses ($ parentTraitEntity );
151
- }
153
+ if ($ node instanceof ClassLike) {
154
+ $ this ->currentClassLike = null ;
152
155
}
153
-
154
- $ this ->dependencyGraph ->addEntity ($ trait );
155
156
}
156
157
157
158
/**
158
- * @param ClassLike $node
159
- * @return array
159
+ * Getter for {@link DependencyInspectionVisitor::$dependencyGraph}.
160
+ *
161
+ * @return DependencyGraph
160
162
*/
161
- private function fetchStmtsNodes ( ClassLike $ node ): array
163
+ public function getDependencyGraph ( ): DependencyGraph
162
164
{
163
- $ methodList = [];
164
- $ propertyList = [];
165
- foreach ($ node ->stmts as $ stmt ) {
166
- if ($ stmt instanceof ClassMethod) {
167
- $ methodList [$ stmt ->name ] = $ stmt ;
168
- } elseif ($ stmt instanceof Property) {
169
- foreach ($ stmt ->props as $ prop ) {
170
- $ propertyList [$ prop ->name ] = $ prop ;
171
- }
172
- }
173
- }
174
-
175
- return [$ methodList , $ propertyList ];
165
+ return $ this ->dependencyGraph ;
176
166
}
177
167
}
0 commit comments