Failed Conditions
Push — master ( ea4232...3b6e69 )
by Grégoire
17:30 queued 17:24
created

DependencyOrderCalculator::addNode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Internal;
6
7
use function array_reverse;
8
9
/**
10
 * DependencyOrderCalculator implements topological sorting, which is an ordering
11
 * algorithm for directed graphs (DG) and/or directed acyclic graphs (DAG) by
12
 * using a depth-first searching (DFS) to traverse the graph built in memory.
13
 * This algorithm have a linear running time based on nodes (V) and dependency
14
 * between the nodes (E), resulting in a computational complexity of O(V + E).
15
 */
16
final class DependencyOrderCalculator
17
{
18
    public const NOT_VISITED = 0;
19
    public const IN_PROGRESS = 1;
20
    public const VISITED     = 2;
21
22
    /**
23
     * Matrix of nodes (aka. vertex).
24
     * Keys are provided hashes and values are the node definition objects.
25
     *
26
     * @var array<string,DependencyOrderNode>
27
     */
28
    private $nodeList = [];
29
30
    /**
31
     * Volatile variable holding calculated nodes during sorting process.
32
     *
33
     * @var array<object>
34
     */
35
    private $sortedNodeList = [];
36
37
    /**
38
     * Checks for node (vertex) existence in graph.
39
     */
40
    public function hasNode(string $hash) : bool
41
    {
42
        return isset($this->nodeList[$hash]);
43
    }
44
45
    /**
46
     * Adds a new node (vertex) to the graph, assigning its hash and value.
47
     *
48
     * @param object $node
49
     */
50
    public function addNode(string $hash, $node) : void
51
    {
52
        $vertex = new DependencyOrderNode();
53
54
        $vertex->hash  = $hash;
55
        $vertex->state = self::NOT_VISITED;
56
        $vertex->value = $node;
57
58
        $this->nodeList[$hash] = $vertex;
59
    }
60
61
    /**
62
     * Adds a new dependency (edge) to the graph using their hashes.
63
     */
64
    public function addDependency(string $fromHash, string $toHash) : void
65
    {
66
        $vertex = $this->nodeList[$fromHash];
67
        $edge   = new DependencyOrderEdge();
68
69
        $edge->from = $fromHash;
70
        $edge->to   = $toHash;
71
72
        $vertex->dependencyList[$toHash] = $edge;
73
    }
74
75
    /**
76
     * Return a valid order list of all current nodes.
77
     * The desired topological sorting is the reverse post order of these searches.
78
     *
79
     * {@internal Highly performance-sensitive method.}
80
     *
81
     * @return array<object>
82
     */
83
    public function sort() : array
84
    {
85
        foreach ($this->nodeList as $vertex) {
86
            if ($vertex->state !== self::NOT_VISITED) {
87
                continue;
88
            }
89
90
            $this->visit($vertex);
91
        }
92
93
        $sortedList = $this->sortedNodeList;
94
95
        $this->nodeList       = [];
96
        $this->sortedNodeList = [];
97
98
        return array_reverse($sortedList);
99
    }
100
101
    /**
102
     * Visit a given node definition for reordering.
103
     *
104
     * {@internal Highly performance-sensitive method.}
105
     */
106
    private function visit(DependencyOrderNode $vertex) : void
107
    {
108
        $vertex->state = self::IN_PROGRESS;
109
110
        foreach ($vertex->dependencyList as $edge) {
111
            $adjacentVertex = $this->nodeList[$edge->to];
112
113
            switch ($adjacentVertex->state) {
114
                case self::VISITED:
115
                case self::IN_PROGRESS:
116
                    // Do nothing, since node was already visited or is
117
                    // currently visited
118
                    break;
119
120
                case self::NOT_VISITED:
121
                    $this->visit($adjacentVertex);
122
            }
123
        }
124
125
        if ($vertex->state === self::VISITED) {
126
            return;
127
        }
128
129
        $vertex->state = self::VISITED;
130
131
        $this->sortedNodeList[] = $vertex->value;
132
    }
133
}
134