Node::hasChildren()   A
last analyzed

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
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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 3618
    public function __construct()
73
    {
74 3618
        $this->data = new Data([
75 3618
            'attributes' => [],
76
        ]);
77 3618
    }
78
79 597
    public function previous(): ?Node
80
    {
81 597
        return $this->previous;
82
    }
83
84 2034
    public function next(): ?Node
85
    {
86 2034
        return $this->next;
87
    }
88
89 2610
    public function parent(): ?Node
90
    {
91 2610
        return $this->parent;
92
    }
93
94 3183
    protected function setParent(?Node $node = null): void
95
    {
96 3183
        $this->parent = $node;
97 3183
        $this->depth  = $node === null ? 0 : $node->depth + 1;
98 3183
    }
99
100
    /**
101
     * Inserts the $sibling node after $this
102
     */
103 2472
    public function insertAfter(Node $sibling): void
104
    {
105 2472
        $sibling->detach();
106 2472
        $sibling->next = $this->next;
107
108 2472
        if ($sibling->next) {
109 867
            $sibling->next->previous = $sibling;
110
        }
111
112 2472
        $sibling->previous = $this;
113 2472
        $this->next        = $sibling;
114 2472
        $sibling->setParent($this->parent);
115
116 2472
        if (! $sibling->next && $sibling->parent) {
117 2463
            $sibling->parent->lastChild = $sibling;
118
        }
119 2472
    }
120
121
    /**
122
     * Inserts the $sibling node before $this
123
     */
124 126
    public function insertBefore(Node $sibling): void
125
    {
126 126
        $sibling->detach();
127 126
        $sibling->previous = $this->previous;
128
129 126
        if ($sibling->previous) {
130 42
            $sibling->previous->next = $sibling;
131
        }
132
133 126
        $sibling->next  = $this;
134 126
        $this->previous = $sibling;
135 126
        $sibling->setParent($this->parent);
136
137 126
        if (! $sibling->previous && $sibling->parent) {
138 78
            $sibling->parent->firstChild = $sibling;
139
        }
140 126
    }
141
142 429
    public function replaceWith(Node $replacement): void
143
    {
144 429
        $replacement->detach();
145 429
        $this->insertAfter($replacement);
146 429
        $this->detach();
147 429
    }
148
149 3180
    public function detach(): void
150
    {
151 3180
        if ($this->previous) {
152 1383
            $this->previous->next = $this->next;
153 3180
        } elseif ($this->parent) {
154 792
            $this->parent->firstChild = $this->next;
155
        }
156
157 3180
        if ($this->next) {
158 1242
            $this->next->previous = $this->previous;
159 3180
        } elseif ($this->parent) {
160 1272
            $this->parent->lastChild = $this->previous;
161
        }
162
163 3180
        $this->parent   = null;
164 3180
        $this->next     = null;
165 3180
        $this->previous = null;
166 3180
        $this->depth    = 0;
167 3180
    }
168
169 591
    public function hasChildren(): bool
170
    {
171 591
        return $this->firstChild !== null;
172
    }
173
174 2799
    public function firstChild(): ?Node
175
    {
176 2799
        return $this->firstChild;
177
    }
178
179 2706
    public function lastChild(): ?Node
180
    {
181 2706
        return $this->lastChild;
182
    }
183
184
    /**
185
     * @return Node[]
186
     */
187 3174
    public function children(): iterable
188
    {
189 3174
        $children = [];
190 3174
        for ($current = $this->firstChild; $current !== null; $current = $current->next) {
191 3057
            $children[] = $current;
192
        }
193
194 3174
        return $children;
195
    }
196
197 3162
    public function appendChild(Node $child): void
198
    {
199 3162
        if ($this->lastChild) {
200 2454
            $this->lastChild->insertAfter($child);
201
        } else {
202 3162
            $child->detach();
203 3162
            $child->setParent($this);
204 3162
            $this->lastChild = $this->firstChild = $child;
205
        }
206 3162
    }
207
208
    /**
209
     * Adds $child as the very first child of $this
210
     */
211 81
    public function prependChild(Node $child): void
212
    {
213 81
        if ($this->firstChild) {
214 75
            $this->firstChild->insertBefore($child);
215
        } else {
216 9
            $child->detach();
217 9
            $child->setParent($this);
218 9
            $this->lastChild = $this->firstChild = $child;
219
        }
220 81
    }
221
222
    /**
223
     * Detaches all child nodes of given node
224
     */
225 15
    public function detachChildren(): void
226
    {
227 15
        foreach ($this->children() as $children) {
228 15
            $children->setParent(null);
229
        }
230
231 15
        $this->firstChild = $this->lastChild = null;
232 15
    }
233
234
    /**
235
     * Replace all children of given node with collection of another
236
     *
237
     * @param iterable<Node> $children
238
     */
239 3
    public function replaceChildren(iterable $children): void
240
    {
241 3
        $this->detachChildren();
242 3
        foreach ($children as $item) {
243 3
            $this->appendChild($item);
244
        }
245 3
    }
246
247 2340
    public function getDepth(): int
248
    {
249 2340
        return $this->depth;
250
    }
251
252 510
    public function walker(): NodeWalker
253
    {
254 510
        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 6
    public function __clone()
263
    {
264
        // Cloned nodes are detached from their parents, siblings, and children
265 6
        $this->parent   = null;
266 6
        $this->previous = null;
267 6
        $this->next     = null;
268
        // But save a copy of the children since we'll need that in a moment
269 6
        $children = $this->children();
270 6
        $this->detachChildren();
271
272
        // The original children get cloned and re-added
273 6
        foreach ($children as $child) {
274 6
            $this->appendChild(clone $child);
275
        }
276 6
    }
277
}
278