Passed
Push — master ( d5f140...355cc7 )
by Pol
02:12
created

Node::replace()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

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