Passed
Push — master ( 28c5ec...7a4f55 )
by Pol
04:19
created

Gv::getDirected()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
class Gv extends AbstractExporter
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
                true === $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
    protected function attributesArrayToText(array $attributes): string
140
    {
141 2
        $attributesText = array_filter(
142
            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
    protected 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 $attributes
182
     * @param string $nodes
183
     * @param string $edges
184
     *   The edges.
185
     *
186
     * @return string
187
     *   The content of the .gv file.
188
     */
189 2
    protected function getGv(string $attributes = '', string $nodes = '', string $edges = ''): string
190
    {
191 2
        $graphType = $this->getDirected() ?
192 2
            'digraph' :
193 2
            'graph';
194
195
        return <<<EOF
196 2
{$graphType} PHPTreeGraph {
197
// The graph attributes.
198 2
{$attributes}
199
200
// The graph nodes.
201 2
{$nodes}
202
203
// The graph edges.
204 2
{$edges}
205
}
206
EOF;
207
    }
208
209
    /**
210
     * Get the hash of a node.
211
     *
212
     * @param \loophp\phptree\Node\NodeInterface $node
213
     *   The node.
214
     *
215
     * @return string
216
     *   The hash of the node.
217
     */
218 2
    protected function getHash(NodeInterface $node): string
219
    {
220 2
        return sha1(spl_object_hash($node));
221
    }
222
223
    /**
224
     * Get the node attributes.
225
     *
226
     * @param \loophp\phptree\Node\NodeInterface $node
227
     *   The node interface.
228
     *
229
     * @return array<mixed, mixed>
230
     *   The attributes as an array.
231
     */
232 2
    protected function getNodeAttributes(NodeInterface $node): array
233
    {
234
        $attributes = [
235 2
            'label' => $this->getNodeRepresentation($node),
236
        ];
237
238 2
        if ($node instanceof AttributeNodeInterface) {
239 1
            foreach ($node->getAttributes() as $key => $value) {
240 1
                $attributes[$key] = $value;
241
            }
242
        }
243
244 2
        return $attributes;
245
    }
246
}
247