Skip to content

Commit a9fb58d

Browse files
roribioRrego6
andauthored
SVC Release 5.0.0 (#32)
* MC-30776: Improve Performance of SVC (#28) - Added nikic/php-parser as hard dependency in composer - Improved performance of DependencyMap creation. - Remove stms when adding ClassMethod nodes to dependency tree - Added better doc comments - Addeed more aggresive AbstractApiVisitor traversal termination - Refactored code to use switch statements Co-authored-by: Raoul Rego <[email protected]>
1 parent 41dc553 commit a9fb58d

File tree

7 files changed

+184
-157
lines changed

7 files changed

+184
-157
lines changed

composer.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "magento/magento-semver",
33
"description": "Magento semantic version checker",
4-
"version": "4.0.0",
4+
"version": "5.0.0",
55
"license": [
66
"OSL-3.0",
77
"AFL-3.0"
@@ -13,7 +13,8 @@
1313
"symfony/console": "~4.1.0||~4.4.0",
1414
"tomzx/php-semver-checker": "^0.13.0",
1515
"wikimedia/less.php": "~1.8.0",
16-
"zendframework/zend-stdlib": "^3.2.1"
16+
"zendframework/zend-stdlib": "^3.2.1",
17+
"nikic/php-parser": "^3.1"
1718
},
1819
"require-dev": {
1920
"phpunit/phpunit": "^6.5.0",

composer.lock

+16-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ClassHierarchy/DependencyInspectionVisitor.php

+100-110
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,31 @@
1515
use PhpParser\Node\Stmt\ClassLike;
1616
use PhpParser\Node\Stmt\ClassMethod;
1717
use PhpParser\Node\Stmt\Interface_ as InterfaceNode;
18-
use PhpParser\Node\Stmt\Property;
18+
use PhpParser\Node\Stmt\PropertyProperty;
1919
use PhpParser\Node\Stmt\Trait_ as TraitNode;
20+
use PhpParser\Node\Stmt\TraitUse;
21+
use PhpParser\NodeTraverser;
2022
use PhpParser\NodeVisitorAbstract;
2123

2224
/**
2325
* Implements a visitor for `class`, `interface` and `trait` nodes that generates a dependency graph.
2426
*/
2527
class 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

Comments
 (0)