Passed
Push — master ( 97996b...04a345 )
by Pol
02:40
created

Gv   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Test Coverage

Coverage 98.67%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 73
dl 0
loc 228
ccs 74
cts 75
cp 0.9867
rs 10
c 2
b 0
f 0
wmc 20

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getDirected() 0 3 1
A setGraphAttributes() 0 5 1
A setDirected() 0 5 1
A export() 0 47 5
A findEdges() 0 6 2
A attributesArrayToText() 0 19 4
A getNodeAttributes() 0 13 3
A getGv() 0 19 2
A getHash() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace loophp\phptree\Exporter;
6
7
use Generator;
8
use loophp\phptree\Node\AttributeNodeInterface;
9
use loophp\phptree\Node\NodeInterface;
10
11
use function is_array;
12
13
use const PHP_EOL;
14
15
/**
16
 * Class Gv.
17
 */
18
final class Gv implements ExporterInterface
19
{
20
    /**
21
     * The graph attributes.
22
     *
23
     * @var string[]|string[][]
24
     */
25
    private $attributes = [];
26
27
    /**
28
     * The graph type.
29
     *
30
     * @var bool
31
     */
32
    private $directed = true;
33
34
    /**
35
     * {@inheritdoc}
36
     */
37 2
    public function export(NodeInterface $node): string
38
    {
39 2
        $attributes = array_map(
40
            function ($key, $data) {
41 1
                if (is_array($data)) {
42 1
                    return sprintf(
43 1
                        '  %s %s',
44
                        $key,
45 1
                        $this->attributesArrayToText($data)
46
                    );
47
                }
48
49 1
                return sprintf(
50 1
                    '  %s = %s',
51
                    $key,
52
                    $data
53
                );
54 2
            },
55 2
            array_keys($this->attributes),
56 2
            $this->attributes
57
        );
58
59 2
        $nodes = [];
60
61 2
        foreach ($node->all() as $child) {
62 2
            $nodes[] = sprintf(
63 2
                '  "%s" %s',
64 2
                $this->getHash($child),
65 2
                $this->attributesArrayToText($this->getNodeAttributes($child))
66
            );
67
        }
68
69 2
        $edges = [];
70
71 2
        foreach ($this->findEdges($node) as $parent => $child) {
72 1
            $edges[] = sprintf(
73 1
                '  "%s" %s "%s";',
74 1
                $this->getHash($parent),
75 1
                $this->getDirected() ? '->' : '--',
76 1
                $this->getHash($child)
77
            );
78
        }
79
80 2
        return $this->getGv(
81 2
            implode(PHP_EOL, $attributes),
82 2
            implode(PHP_EOL, $nodes),
83 2
            implode(PHP_EOL, $edges)
84
        );
85
    }
86
87
    /**
88
     * Check if the graph is directed or undirected.
89
     *
90
     * @return bool
91
     *   True if directed, false otherwise.
92
     */
93 2
    public function getDirected(): bool
94
    {
95 2
        return $this->directed;
96
    }
97
98
    /**
99
     * Set the graph type, directed or undirected.
100
     *
101
     * @param bool $directed
102
     *   True for a directed graph, false otherwise.
103
     *
104
     * @return \loophp\phptree\Exporter\Gv
105
     *   The exporter.
106
     */
107 1
    public function setDirected(bool $directed = true): self
108
    {
109 1
        $this->directed = $directed;
110
111 1
        return $this;
112
    }
113
114
    /**
115
     * Set the graph attributes.
116
     *
117
     * @param array<mixed, mixed> $attributes
118
     *   The graph attributes.
119
     *
120
     * @return \loophp\phptree\Exporter\Gv
121
     *   The exporter.
122
     */
123 1
    public function setGraphAttributes(array $attributes): self
124
    {
125 1
        $this->attributes = $attributes;
126
127 1
        return $this;
128
    }
129
130
    /**
131
     * Converts an attributes array to string.
132
     *
133
     * @param array<mixed, mixed> $attributes
134
     *   The attributes.
135
     *
136
     * @return string
137
     *   The attributes as string.
138
     */
139 2
    private function attributesArrayToText(array $attributes): string
140
    {
141 2
        $attributesText = array_filter(
142 2
            array_map(
143
                static function ($key, $value) {
144 2
                    if (null === $value || is_scalar($value) || method_exists($value, '__toString')) {
145 2
                        $value = (string) $value;
146
                    } else {
147
                        return null;
148
                    }
149
150 2
                    return sprintf('%s="%s"', $key, $value);
151 2
                },
152 2
                array_keys($attributes),
153
                $attributes
154
            )
155
        );
156
157 2
        return '[' . implode(' ', $attributesText) . ']';
158
    }
159
160
    /**
161
     * Recursively find all the edges in a tree.
162
     *
163
     * @param \loophp\phptree\Node\NodeInterface $node
164
     *   The root node.
165
     *
166
     * @return Generator<NodeInterface, NodeInterface>
167
     *   Yield the parent and child node.
168
     */
169 2
    private function findEdges(NodeInterface $node): iterable
170
    {
171 2
        foreach ($node->children() as $child) {
172 1
            yield $node => $child;
173
174 1
            yield from $this->findEdges($child);
175
        }
176 2
    }
177
178
    /**
179
     * Get the default GV file content.
180
     *
181
     * @param string $edges
182
     *   The edges.
183
     *
184
     * @return string
185
     *   The content of the .gv file.
186
     */
187 2
    private function getGv(string $attributes = '', string $nodes = '', string $edges = ''): string
188
    {
189 2
        $graphType = $this->getDirected() ?
190 2
            'digraph' :
191 2
            'graph';
192
193 2
        return implode(
194 2
            PHP_EOL,
195
            [
196 2
                sprintf('%s PHPTreeGraph {', $graphType),
197 2
                '// The graph attributes.',
198 2
                $attributes,
199 2
                '',
200 2
                '// The graph nodes.',
201 2
                $nodes,
202 2
                '',
203 2
                '// The graph edges.',
204 2
                $edges,
205 2
                '}',
206
            ]
207
        );
208
    }
209
210
    /**
211
     * Get the hash of a node.
212
     *
213
     * @param \loophp\phptree\Node\NodeInterface $node
214
     *   The node.
215
     *
216
     * @return string
217
     *   The hash of the node.
218
     */
219 2
    private function getHash(NodeInterface $node): string
220
    {
221 2
        return sha1(spl_object_hash($node));
222
    }
223
224
    /**
225
     * Get the node attributes.
226
     *
227
     * @param \loophp\phptree\Node\NodeInterface $node
228
     *   The node interface.
229
     *
230
     * @return array<mixed, mixed>
231
     *   The attributes as an array.
232
     */
233 2
    private function getNodeAttributes(NodeInterface $node): array
234
    {
235
        $attributes = [
236 2
            'label' => $node->label(),
237
        ];
238
239 2
        if ($node instanceof AttributeNodeInterface) {
240 1
            foreach ($node->getAttributes() as $key => $value) {
241 1
                $attributes[$key] = $value;
242
            }
243
        }
244
245 2
        return $attributes;
246
    }
247
}
248