Gv   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Test Coverage

Coverage 98.67%

Importance

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