Issues (33)

src/DependencyGraph.php (1 issue)

Labels
Severity
1
<?php
2
declare(strict_types=1);
3
4
namespace DependencyAnalyzer;
5
6
use DependencyAnalyzer\DependencyGraph\ClassLike;
7
use DependencyAnalyzer\DependencyGraph\DependencyArrow;
8
use DependencyAnalyzer\DependencyGraph\DependencyTypes\Base as DependencyType;
9
use DependencyAnalyzer\DependencyGraph\Path;
10
use DependencyAnalyzer\Exceptions\InvalidEdgeOnDependencyGraphException;
11
use DependencyAnalyzer\DependencyGraph\StructuralElementPatternMatcher;
12
use Fhaculty\Graph\Edge\Directed;
13
use Fhaculty\Graph\Graph;
14
use Fhaculty\Graph\Set\Vertices;
15
use Fhaculty\Graph\Vertex;
16
17
class DependencyGraph implements \Countable
18
{
19
    const DEPENDENCY_TYPE_KEY = 'dependency_types';
20
21
    const TYPE_SOME_DEPENDENCY = 'some_dependency';
22
    const TYPE_NEW = 'new';
23
    const TYPE_METHOD_CALL = 'method_call';
24
    const TYPE_PROPERTY_FETCH = 'property_fetch';
25
    const TYPE_CONSTANT_FETCH = 'constant_fetch';
26
    const TYPE_EXTENDS = 'extends';
27
    const TYPE_IMPLEMENTS = 'implements';
28
    const TYPE_USE_TRAIT = 'use_trait';
29
30
    /**
31
     * @var Graph
32
     */
33
    private $graph;
34
35
    /**
36
     * @param Graph $graph
37
     * @da-internal \DependencyAnalyzer\DependencyGraphBuilder
38
     */
39
    public function __construct(Graph $graph)
40
    {
41
        foreach ($graph->getEdges() as $edge) {
42
            if (!$edge instanceof Directed) {
43
                throw new InvalidEdgeOnDependencyGraphException($edge);
44
            }
45
        }
46
        $this->graph = $graph;
47
    }
48
49
    public function getGraph()
50
    {
51
        return $this->graph;
52
    }
53
54
    /**
55
     * @return ClassLike[]
56
     */
57
    public function getClasses(): array
58
    {
59
        $ret = [];
60
        foreach ($this->graph->getVertices() as $vertex) {
61
            $ret[] = new ClassLike($vertex);
62
        }
63
64
        return $ret;
65
    }
66
67
    /**
68
     * @return DependencyArrow[]
69
     */
70
    public function getDependencyArrows(): array
71
    {
72
        $ret = [];
73
74
        foreach ($this->graph->getEdges() as $edge) {
75
            $ret[] = new DependencyArrow($edge);
76
        }
77
78
        return $ret;
79
    }
80
81
    public function groupByPattern(string $name, StructuralElementPatternMatcher $pattern)
82
    {
83
        $graph = new Graph();
84
        $graph->createVertex($name);
0 ignored issues
show
$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

84
        $graph->createVertex(/** @scrutinizer ignore-type */ $name);
Loading history...
85
        foreach ($this->getClasses() as $class) {
86
            if (!$pattern->isMatch($class->getName())) {
87
                $graph->createVertex($class->getName());
88
            }
89
        }
90
91
        foreach ($this->getDependencyArrows() as $dependencyArrow) {
92
            $start = $pattern->isMatch($dependencyArrow->getDependerName()) ? $name : $dependencyArrow->getDependerName();
93
            $end = $pattern->isMatch($dependencyArrow->getDependeeName()) ? $name : $dependencyArrow->getDependeeName();
94
95
            if ($start !== $end && !$graph->getVertex($start)->hasEdgeTo($graph->getVertex($end))) {
96
                $graph->getVertex($start)->createEdgeTo($graph->getVertex($end));
97
            }
98
        }
99
100
        return new self($graph);
101
    }
102
103
    public function getConnectedSubGraphsStartFrom(Vertex $vertex)
104
    {
105
        $vertices = $this->collectConnectedVertices($vertex);
106
        return new self($this->graph->createGraphCloneVertices($vertices));
107
    }
108
109
    protected function collectConnectedVertices(Vertex $start)
110
    {
111
        $visited = [];
112
113
        /**
114
         * @var Vertex[] $queue
115
         */
116
        $queue = [$start];
117
        // Breadth first search
118
        do {
119
            $target = array_shift($queue);
120
            $visited[$target->getId()]= $target;
121
122
            foreach ($target->getVerticesEdgeTo()->getMap() as $id => $vertexTo) {
123
                if (!isset($visited[$id])) {
124
                    $queue[] = $vertexTo;
125
                }
126
            }
127
        } while ($queue);
128
129
        return new Vertices(array_values($visited));
130
    }
131
132
    public function walkOnPath(callable $carry)
133
    {
134
        foreach ($this->graph->getEdges() as $edge) {
135
            $this->walkThroughEdge($edge, new Path(), $carry);
136
        }
137
    }
138
139
    protected function walkThroughEdge(Directed $edge, Path $path, callable $carry)
140
    {
141
        $path = $path->addEdge($edge);
142
        $carry($path);
143
144
        if (!$path->haveCycle()) {
145
            $edgesOut = $edge->getVertexEnd()->getEdgesOut();
146
            foreach ($edgesOut as $edgeOut) {
147
                $this->walkThroughEdge($edgeOut, $path, $carry);
148
            }
149
        }
150
    }
151
152
    public function toArray()
153
    {
154
        $ret = [];
155
156
        foreach ($this->graph->getVertices() as $vertex) {
157
            $ret[$vertex->getId()] = [];
158
159
            foreach ($vertex->getEdgesOut() as $edge) {
160
                /** @var Directed $edge */
161
                $types = $edge->getAttribute(DependencyGraph::DEPENDENCY_TYPE_KEY) ?? [];
162
163
                $ret[$vertex->getId()][$edge->getVertexEnd()->getId()] = array_map(function (DependencyType $type) {
164
                    return $type->toString();
165
                }, $types);
166
            }
167
        }
168
169
        return $ret;
170
    }
171
172
    public function count()
173
    {
174
        return count($this->graph->getVertices());
175
    }
176
}
177