1515use PhpParser \Node \Stmt \ClassLike ;
1616use PhpParser \Node \Stmt \ClassMethod ;
1717use PhpParser \Node \Stmt \Interface_ as InterfaceNode ;
18- use PhpParser \Node \Stmt \Property ;
18+ use PhpParser \Node \Stmt \PropertyProperty ;
1919use PhpParser \Node \Stmt \Trait_ as TraitNode ;
20+ use PhpParser \Node \Stmt \TraitUse ;
21+ use PhpParser \NodeTraverser ;
2022use PhpParser \NodeVisitorAbstract ;
2123
2224/**
2325 * Implements a visitor for `class`, `interface` and `trait` nodes that generates a dependency graph.
2426 */
2527class DependencyInspectionVisitor extends NodeVisitorAbstract
2628{
29+
2730 /** @var DependencyGraph */
2831 private $ dependencyGraph ;
2932
3033 /** @var NodeHelper */
3134 private $ nodeHelper ;
3235
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+
3343 /**
3444 * Constructor.
3545 *
@@ -43,135 +53,115 @@ public function __construct(DependencyGraph $dependencyGraph, NodeHelper $nodeHe
4353 }
4454
4555 /**
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.
4759 *
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
6364 *
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
7370 */
74- private function addClassNode ( ClassNode $ node )
71+ public function enterNode ( Node $ node )
7572 {
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 ;
10794 }
108-
109- $ this ->dependencyGraph ->addEntity ($ class );
11095 }
11196
11297 /**
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
114103 */
115- private function addInterfaceNode ( InterfaceNode $ node )
104+ private function handleClassLike ( ClassLike $ node )
116105 {
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 ;
128139 }
129-
130- $ this -> dependencyGraph -> addEntity ( $ interface ) ;
140+ $ this -> currentClassLike -> setIsApi ( $ this -> nodeHelper -> isApiNode ( $ node ));
141+ return null ;
131142 }
132143
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
135150 */
136- private function addTraitNode ( TraitNode $ node )
151+ public function leaveNode ( Node $ node )
137152 {
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 ;
152155 }
153-
154- $ this ->dependencyGraph ->addEntity ($ trait );
155156 }
156157
157158 /**
158- * @param ClassLike $node
159- * @return array
159+ * Getter for {@link DependencyInspectionVisitor::$dependencyGraph}.
160+ *
161+ * @return DependencyGraph
160162 */
161- private function fetchStmtsNodes ( ClassLike $ node ): array
163+ public function getDependencyGraph ( ): DependencyGraph
162164 {
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 ;
176166 }
177167}
0 commit comments