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

Node::offsetUnset()   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 1
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\Node;
6
7
use InvalidArgumentException;
8
use Traversable;
9
10
use function array_key_exists;
11
use function count;
12
use function in_array;
13
14
/**
15
 * Class Node.
16
 */
17
class Node implements NodeInterface
18
{
19
    /**
20
     * @var array<\loophp\phptree\Node\NodeInterface>
21
     */
22
    private $children = [];
23
24
    /**
25
     * @var NodeInterface|null
26
     */
27
    private $parent;
28
29
    /**
30
     * Node constructor.
31
     */
32 73
    public function __construct(?NodeInterface $parent = null)
33
    {
34 73
        $this->parent = $parent;
35 73
        $this->children = [];
36 73
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41 11
    public function __clone()
42
    {
43
        /** @var NodeInterface $child */
44 11
        foreach ($this->children as $id => $child) {
45 9
            $this->children[$id] = $child->clone()->setParent($this);
46
        }
47 11
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52 54
    public function add(NodeInterface ...$nodes): NodeInterface
53
    {
54 54
        foreach ($nodes as $node) {
55 54
            $this->children[] = $node->setParent($this);
56
        }
57
58 54
        return $this;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 27
    public function all(): Traversable
65
    {
66 27
        yield $this;
67
68
        /** @var NodeInterface $child */
69 27
        foreach ($this->children() as $child) {
70 26
            yield from $child->all();
71
        }
72 27
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77 50
    public function children(): Traversable
78
    {
79 50
        yield from $this->children;
80 50
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 10
    public function clone(): NodeInterface
86
    {
87 10
        return clone $this;
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 21
    public function count(): int
94
    {
95 21
        return iterator_count($this->all()) - 1;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101 26
    public function degree(): int
102
    {
103 26
        return count($this->children);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 1
    public function delete(NodeInterface $node, ?NodeInterface $root = null): ?NodeInterface
110
    {
111 1
        $root = $root ?? $this;
112
113 1
        if (null !== ($candidate = $this->find($node))) {
114 1
            if ($candidate === $root) {
115 1
                throw new InvalidArgumentException('Unable to delete root node.');
116
            }
117
118 1
            if (null !== $parent = $candidate->getParent()) {
119 1
                $parent->remove($node);
120
            }
121
122 1
            return $candidate->setParent(null);
123
        }
124
125 1
        return null;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 3
    public function depth(): int
132
    {
133 3
        return iterator_count($this->getAncestors());
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 1
    public function find(NodeInterface $node): ?NodeInterface
140
    {
141
        /** @var NodeInterface $candidate */
142 1
        foreach ($this->all() as $candidate) {
143 1
            if ($candidate === $node) {
144 1
                return $node;
145
            }
146
        }
147
148 1
        return null;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 5
    public function getAncestors(): Traversable
155
    {
156 5
        $node = $this;
157
158 5
        while ($node = $node->getParent()) {
159 5
            yield $node;
160
        }
161 5
    }
162
163
    /**
164
     * @return Traversable<\loophp\phptree\Node\NodeInterface>
165
     */
166 1
    public function getIterator()
167
    {
168 1
        yield from $this->all();
169 1
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174 16
    public function getParent(): ?NodeInterface
175
    {
176 16
        return $this->parent;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182 1
    public function getSibblings(): Traversable
183
    {
184 1
        $parent = $this->parent;
185
186 1
        if (null === $parent) {
187 1
            return [];
188
        }
189
190 1
        foreach ($parent->children() as $child) {
191 1
            if ($child === $this) {
192 1
                continue;
193
            }
194
195 1
            yield $child;
196
        }
197 1
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 1
    public function height(): int
203
    {
204 1
        $height = $this->depth();
205
206
        /** @var NodeInterface $child */
207 1
        foreach ($this->children() as $child) {
208 1
            $height = max($height, $child->height());
209
        }
210
211 1
        return $height;
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217 11
    public function isLeaf(): bool
218
    {
219 11
        return 0 === $this->degree();
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 3
    public function isRoot(): bool
226
    {
227 3
        return null === $this->parent;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233 1
    public function label(): string
234
    {
235 1
        return sha1(spl_object_hash($this));
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241 1
    public function level(int $level): Traversable
242
    {
243
        /** @var NodeInterface $node */
244 1
        foreach ($this->all() as $node) {
245 1
            if ($node->depth() === $level) {
246 1
                yield $node;
247
            }
248
        }
249 1
    }
250
251
    /**
252
     * @param mixed $offset
253
     *
254
     * @return bool
255
     */
256 1
    public function offsetExists($offset)
257
    {
258 1
        return array_key_exists($offset, $this->children);
259
    }
260
261
    /**
262
     * @param mixed $offset
263
     *
264
     * @return NodeInterface
265
     */
266 4
    public function offsetGet($offset)
267
    {
268 4
        return $this->children[$offset];
269
    }
270
271
    /**
272
     * @param mixed $offset
273
     * @param mixed $value
274
     */
275 2
    public function offsetSet($offset, $value): void
276
    {
277 2
        if (!($value instanceof NodeInterface)) {
278 1
            throw new InvalidArgumentException(
279 1
                'The value must implements NodeInterface.'
280
            );
281
        }
282
283 2
        $this->children[$offset] = $value->setParent($this);
284 2
    }
285
286
    /**
287
     * @param mixed $offset
288
     *
289
     * @return void
290
     */
291 1
    public function offsetUnset($offset)
292
    {
293 1
        unset($this->children[$offset]);
294 1
    }
295
296
    /**
297
     * {@inheritdoc}
298
     */
299 6
    public function remove(NodeInterface ...$nodes): NodeInterface
300
    {
301 6
        $this->children =
302 6
            array_filter(
303 6
                $this->children,
304
                static function ($child) use ($nodes) {
305 6
                    return !in_array($child, $nodes, true);
306 6
                }
307
            );
308
309 6
        return $this;
310
    }
311
312
    /**
313
     * {@inheritdoc}
314
     */
315 1
    public function replace(NodeInterface $node): ?NodeInterface
316
    {
317 1
        if (null === $parent = $this->getParent()) {
318 1
            return null;
319
        }
320
321
        // Find the key of the current node in the parent.
322 1
        foreach ($parent->children() as $key => $child) {
323 1
            if ($this === $child) {
324 1
                $parent[$key] = $node;
325
326 1
                break;
327
            }
328
        }
329
330 1
        return $parent;
331
    }
332
333
    /**
334
     * {@inheritdoc}
335
     */
336 54
    public function setParent(?NodeInterface $node): NodeInterface
337
    {
338 54
        $this->parent = $node;
339
340 54
        return $this;
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346 3
    public function withChildren(?NodeInterface ...$nodes): NodeInterface
347
    {
348 3
        $clone = clone $this;
349 3
        $clone->children = [];
350
351 3
        $nodes = array_filter($nodes);
352
353 3
        return [] === $nodes ?
354 3
            $clone :
355 3
            $clone->add(...$nodes);
356
    }
357
}
358