Passed
Push — master ( 176455...e166a4 )
by Satoshi
02:24
created

DependencyGraph::groupByPattern()   B

Complexity

Conditions 8
Paths 27

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 11
nc 27
nop 2
dl 0
loc 22
rs 8.4444
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace DependencyAnalyzer;
5
6
use DependencyAnalyzer\DependencyGraph\ExtraPhpDocTagResolver;
7
use DependencyAnalyzer\DependencyGraph\Path;
8
use DependencyAnalyzer\Exceptions\InvalidEdgeOnDependencyGraphException;
9
use DependencyAnalyzer\Patterns\QualifiedNamePattern;
10
use Fhaculty\Graph\Edge\Directed;
11
use Fhaculty\Graph\Graph;
12
use Fhaculty\Graph\Set\Vertices;
13
use Fhaculty\Graph\Vertex;
14
15
class DependencyGraph implements \Countable
16
{
17
    /**
18
     * @var Graph
19
     */
20
    private $graph;
21
22
    public function __construct(Graph $graph)
23
    {
24
        foreach ($graph->getEdges() as $edge) {
25
            if (!$edge instanceof Directed) {
26
                throw new InvalidEdgeOnDependencyGraphException($edge);
27
            }
28
        }
29
        $this->graph = $graph;
30
    }
31
32
    public function getClasses()
33
    {
34
        return $this->graph->getVertices();
35
    }
36
37
    public function getDependencyArrows()
38
    {
39
        return $this->graph->getEdges();
40
    }
41
42
    public function groupByPattern(string $name, QualifiedNamePattern $pattern)
43
    {
44
        $graph = new Graph();
45
        $graph->createVertex($name);
0 ignored issues
show
Bug introduced by
$name of type string is incompatible with the type integer|null expected by parameter $id of Fhaculty\Graph\Graph::createVertex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

45
        $graph->createVertex(/** @scrutinizer ignore-type */ $name);
Loading history...
46
        foreach ($this->getClasses() as $class) {
47
            /** @var Vertex $class */
48
            if (!$pattern->isMatch($class->getId())) {
49
                $graph->createVertex($class->getId());
50
            }
51
        }
52
53
        foreach ($this->getDependencyArrows() as $dependencyArrow) {
54
            /** @var Directed $dependencyArrow */
55
            $start = $pattern->isMatch($dependencyArrow->getVertexStart()->getId()) ? $name : $dependencyArrow->getVertexStart()->getId();
56
            $end = $pattern->isMatch($dependencyArrow->getVertexEnd()->getId()) ? $name : $dependencyArrow->getVertexEnd()->getId();
57
58
            if ($start !== $end && !$graph->getVertex($start)->hasEdgeTo($graph->getVertex($end))) {
59
                $graph->getVertex($start)->createEdgeTo($graph->getVertex($end));
60
            }
61
        }
62
63
        return new self($graph);
64
    }
65
66
    public function getConnectedSubGraphsStartFrom(Vertex $vertex)
67
    {
68
        $vertices = $this->collectConnectedVertices($vertex);
69
        return new self($this->graph->createGraphCloneVertices($vertices));
70
    }
71
72
    protected function collectConnectedVertices(Vertex $start)
73
    {
74
        $visited = [];
75
76
        /**
77
         * @var Vertex[] $queue
78
         */
79
        $queue = [$start];
80
        // Breadth first search
81
        do {
82
            $target = array_shift($queue);
83
            $visited[$target->getId()]= $target;
84
85
            foreach ($target->getVerticesEdgeTo()->getMap() as $id => $vertexTo) {
86
                if (!isset($visited[$id])) {
87
                    $queue[] = $vertexTo;
88
                }
89
            }
90
        } while ($queue);
91
92
        return new Vertices(array_values($visited));
93
    }
94
95
    public function walkOnPath(callable $carry)
96
    {
97
        foreach ($this->graph->getEdges() as $edge) {
98
            $this->walkThroughEdge($edge, new Path(), $carry);
99
        }
100
    }
101
102
    protected function walkThroughEdge(Directed $edge, Path $path, callable $carry)
103
    {
104
        $path = $path->addEdge($edge);
105
        $carry($path);
106
107
        if (!$path->haveCycle()) {
108
            $edgesOut = $edge->getVertexEnd()->getEdgesOut();
109
            foreach ($edgesOut as $edgeOut) {
110
                $this->walkThroughEdge($edgeOut, $path, $carry);
111
            }
112
        }
113
    }
114
115
    public function getClassesHaveOnlyUsedTag(): array
116
    {
117
        $classes = [];
118
        foreach ($this->getClasses() as $class) {
119
            /** @var Vertex $class */
120
            if (!empty($classNames = $class->getAttribute(ExtraPhpDocTagResolver::ONLY_USED_BY_TAGS))) {
121
                $classes[$class->getId()] = $classNames;
122
            }
123
        }
124
125
        return $classes;
126
    }
127
128
    public function toArray()
129
    {
130
        $ret = [];
131
132
        foreach ($this->graph->getVertices() as $vertex) {
133
            $ret[$vertex->getId()] = [];
134
135
            foreach ($vertex->getVerticesEdgeTo() as $edgeTo) {
136
                $ret[$vertex->getId()][] = $edgeTo->getId();
137
            }
138
        }
139
140
        return $ret;
141
    }
142
143
    public function count()
144
    {
145
        return count($this->graph->getVertices());
146
    }
147
}
148