Passed
Push — main ( 989696...2152d5 )
by Colin
05:14 queued 02:09
created

Node::detachChildren()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the league/commonmark package.
7
 *
8
 * (c) Colin O'Dell <[email protected]>
9
 *
10
 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
11
 *  - (c) John MacFarlane
12
 *
13
 * For the full copyright and license information, please view the LICENSE
14
 * file that was distributed with this source code.
15
 */
16
17
namespace League\CommonMark\Node;
18
19
use Dflydev\DotAccessData\Data;
20
21
abstract class Node
22
{
23
    /**
24
     * @var Data
25
     *
26
     * @psalm-readonly
27
     */
28
    public $data;
29
30
    /**
31
     * @var int
32
     *
33
     * @psalm-readonly-allow-private-mutation
34
     */
35
    protected $depth = 0;
36
37
    /**
38
     * @var Node|null
39
     *
40
     * @psalm-readonly-allow-private-mutation
41
     */
42
    protected $parent;
43
44
    /**
45
     * @var Node|null
46
     *
47
     * @psalm-readonly-allow-private-mutation
48
     */
49
    protected $previous;
50
51
    /**
52
     * @var Node|null
53
     *
54
     * @psalm-readonly-allow-private-mutation
55
     */
56
    protected $next;
57
58
    /**
59
     * @var Node|null
60
     *
61
     * @psalm-readonly-allow-private-mutation
62
     */
63
    protected $firstChild;
64
65
    /**
66
     * @var Node|null
67
     *
68
     * @psalm-readonly-allow-private-mutation
69
     */
70
    protected $lastChild;
71
72 3732
    public function __construct()
73
    {
74 3732
        $this->data = new Data([
75 3732
            'attributes' => [],
76
        ]);
77 3732
    }
78
79 636
    public function previous(): ?Node
80
    {
81 636
        return $this->previous;
82
    }
83
84 2175
    public function next(): ?Node
85
    {
86 2175
        return $this->next;
87
    }
88
89 2754
    public function parent(): ?Node
90
    {
91 2754
        return $this->parent;
92
    }
93
94 3282
    protected function setParent(?Node $node = null): void
95
    {
96 3282
        $this->parent = $node;
97 3282
        $this->depth  = $node === null ? 0 : $node->depth + 1;
98 3282
    }
99
100
    /**
101
     * Inserts the $sibling node after $this
102
     */
103 2568
    public function insertAfter(Node $sibling): void
104
    {
105 2568
        $sibling->detach();
106 2568
        $sibling->next = $this->next;
107
108 2568
        if ($sibling->next) {
109 918
            $sibling->next->previous = $sibling;
110
        }
111
112 2568
        $sibling->previous = $this;
113 2568
        $this->next        = $sibling;
114 2568
        $sibling->setParent($this->parent);
115
116 2568
        if (! $sibling->next && $sibling->parent) {
117 2559
            $sibling->parent->lastChild = $sibling;
118
        }
119 2568
    }
120
121
    /**
122
     * Inserts the $sibling node before $this
123
     */
124 174
    public function insertBefore(Node $sibling): void
125
    {
126 174
        $sibling->detach();
127 174
        $sibling->previous = $this->previous;
128
129 174
        if ($sibling->previous) {
130 48
            $sibling->previous->next = $sibling;
131
        }
132
133 174
        $sibling->next  = $this;
134 174
        $this->previous = $sibling;
135 174
        $sibling->setParent($this->parent);
136
137 174
        if (! $sibling->previous && $sibling->parent) {
138 123
            $sibling->parent->firstChild = $sibling;
139
        }
140 174
    }
141
142 498
    public function replaceWith(Node $replacement): void
143
    {
144 498
        $replacement->detach();
145 498
        $this->insertAfter($replacement);
146 498
        $this->detach();
147 498
    }
148
149 3279
    public function detach(): void
150
    {
151 3279
        if ($this->previous) {
152 1443
            $this->previous->next = $this->next;
153 3279
        } elseif ($this->parent) {
154 786
            $this->parent->firstChild = $this->next;
155
        }
156
157 3279
        if ($this->next) {
158 1314
            $this->next->previous = $this->previous;
159 3279
        } elseif ($this->parent) {
160 1287
            $this->parent->lastChild = $this->previous;
161
        }
162
163 3279
        $this->parent   = null;
164 3279
        $this->next     = null;
165 3279
        $this->previous = null;
166 3279
        $this->depth    = 0;
167 3279
    }
168
169 738
    public function hasChildren(): bool
170
    {
171 738
        return $this->firstChild !== null;
172
    }
173
174 2901
    public function firstChild(): ?Node
175
    {
176 2901
        return $this->firstChild;
177
    }
178
179 2781
    public function lastChild(): ?Node
180
    {
181 2781
        return $this->lastChild;
182
    }
183
184
    /**
185
     * @return Node[]
186
     */
187 3147
    public function children(): iterable
188
    {
189 3147
        $children = [];
190 3147
        for ($current = $this->firstChild; $current !== null; $current = $current->next) {
191 3027
            $children[] = $current;
192
        }
193
194 3147
        return $children;
195
    }
196
197 3261
    public function appendChild(Node $child): void
198
    {
199 3261
        if ($this->lastChild) {
200 2550
            $this->lastChild->insertAfter($child);
201
        } else {
202 3261
            $child->detach();
203 3261
            $child->setParent($this);
204 3261
            $this->lastChild = $this->firstChild = $child;
205
        }
206 3261
    }
207
208
    /**
209
     * Adds $child as the very first child of $this
210
     */
211 126
    public function prependChild(Node $child): void
212
    {
213 126
        if ($this->firstChild) {
214 120
            $this->firstChild->insertBefore($child);
215
        } else {
216 9
            $child->detach();
217 9
            $child->setParent($this);
218 9
            $this->lastChild = $this->firstChild = $child;
219
        }
220 126
    }
221
222
    /**
223
     * Detaches all child nodes of given node
224
     */
225 36
    public function detachChildren(): void
226
    {
227 36
        foreach ($this->children() as $children) {
228 21
            $children->setParent(null);
229
        }
230
231 36
        $this->firstChild = $this->lastChild = null;
232 36
    }
233
234
    /**
235
     * Replace all children of given node with collection of another
236
     *
237
     * @param iterable<Node> $children
238
     */
239 21
    public function replaceChildren(iterable $children): void
240
    {
241 21
        $this->detachChildren();
242 21
        foreach ($children as $item) {
243 21
            $this->appendChild($item);
244
        }
245 21
    }
246
247 2391
    public function getDepth(): int
248
    {
249 2391
        return $this->depth;
250
    }
251
252 708
    public function walker(): NodeWalker
253
    {
254 708
        return new NodeWalker($this);
255
    }
256
257
    /**
258
     * Clone the current node and its children
259
     *
260
     * WARNING: This is a recursive function and should not be called on deeply-nested node trees!
261
     */
262 9
    public function __clone()
263
    {
264
        // Cloned nodes are detached from their parents, siblings, and children
265 9
        $this->parent   = null;
266 9
        $this->previous = null;
267 9
        $this->next     = null;
268
        // But save a copy of the children since we'll need that in a moment
269 9
        $children = $this->children();
270 9
        $this->detachChildren();
271
272
        // The original children get cloned and re-added
273 9
        foreach ($children as $child) {
274 9
            $this->appendChild(clone $child);
275
        }
276 9
    }
277
278 3312
    public static function assertInstanceOf(Node $node): void
279
    {
280 3312
        if (! $node instanceof static) {
0 ignored issues
show
introduced by
$node is always a sub-type of static.
Loading history...
281 72
            throw new \InvalidArgumentException(\sprintf('Incompatible node type: expected %s, got %s', static::class, \get_class($node)));
282
        }
283 3240
    }
284
}
285