Node   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 36
eloc 79
dl 0
loc 240
ccs 98
cts 98
cp 1
rs 9.52
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A firstChild() 0 3 1
A hasChildren() 0 3 1
A parent() 0 3 1
A insertBefore() 0 15 4
A replaceWith() 0 5 1
A replaceChildren() 0 5 2
A appendChild() 0 8 2
A getDepth() 0 3 1
A children() 0 8 2
A walker() 0 3 1
A __clone() 0 13 2
A detach() 0 18 5
A previous() 0 3 1
A next() 0 3 1
A insertAfter() 0 15 4
A setParent() 0 4 2
A prependChild() 0 8 2
A detachChildren() 0 7 2
A lastChild() 0 3 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
abstract class Node
20
{
21
    /**
22
     * @var int
23
     *
24
     * @psalm-readonly-allow-private-mutation
25
     */
26
    protected $depth = 0;
27
28
    /**
29
     * @var Node|null
30
     *
31
     * @psalm-readonly-allow-private-mutation
32
     */
33
    protected $parent;
34
35
    /**
36
     * @var Node|null
37
     *
38
     * @psalm-readonly-allow-private-mutation
39
     */
40
    protected $previous;
41
42
    /**
43
     * @var Node|null
44
     *
45
     * @psalm-readonly-allow-private-mutation
46
     */
47
    protected $next;
48
49
    /**
50
     * @var Node|null
51
     *
52
     * @psalm-readonly-allow-private-mutation
53
     */
54
    protected $firstChild;
55
56
    /**
57
     * @var Node|null
58
     *
59
     * @psalm-readonly-allow-private-mutation
60
     */
61
    protected $lastChild;
62
63 519
    public function previous(): ?Node
64
    {
65 519
        return $this->previous;
66
    }
67
68 1950
    public function next(): ?Node
69
    {
70 1950
        return $this->next;
71
    }
72
73 2439
    public function parent(): ?Node
74
    {
75 2439
        return $this->parent;
76
    }
77
78 2970
    protected function setParent(?Node $node = null): void
79
    {
80 2970
        $this->parent = $node;
81 2970
        $this->depth  = $node === null ? 0 : $node->depth + 1;
82 2970
    }
83
84
    /**
85
     * Inserts the $sibling node after $this
86
     */
87 2241
    public function insertAfter(Node $sibling): void
88
    {
89 2241
        $sibling->detach();
90 2241
        $sibling->next = $this->next;
91
92 2241
        if ($sibling->next) {
93 810
            $sibling->next->previous = $sibling;
94
        }
95
96 2241
        $sibling->previous = $this;
97 2241
        $this->next        = $sibling;
98 2241
        $sibling->setParent($this->parent);
99
100 2241
        if (! $sibling->next && $sibling->parent) {
101 2232
            $sibling->parent->lastChild = $sibling;
102
        }
103 2241
    }
104
105
    /**
106
     * Inserts the $sibling node before $this
107
     */
108 234
    public function insertBefore(Node $sibling): void
109
    {
110 234
        $sibling->detach();
111 234
        $sibling->previous = $this->previous;
112
113 234
        if ($sibling->previous) {
114 114
            $sibling->previous->next = $sibling;
115
        }
116
117 234
        $sibling->next  = $this;
118 234
        $this->previous = $sibling;
119 234
        $sibling->setParent($this->parent);
120
121 234
        if (! $sibling->previous && $sibling->parent) {
122 186
            $sibling->parent->firstChild = $sibling;
123
        }
124 234
    }
125
126 396
    public function replaceWith(Node $replacement): void
127
    {
128 396
        $replacement->detach();
129 396
        $this->insertAfter($replacement);
130 396
        $this->detach();
131 396
    }
132
133 2967
    public function detach(): void
134
    {
135 2967
        if ($this->previous) {
136 1386
            $this->previous->next = $this->next;
137 2967
        } elseif ($this->parent) {
138 714
            $this->parent->firstChild = $this->next;
139
        }
140
141 2967
        if ($this->next) {
142 1146
            $this->next->previous = $this->previous;
143 2967
        } elseif ($this->parent) {
144 1284
            $this->parent->lastChild = $this->previous;
145
        }
146
147 2967
        $this->parent   = null;
148 2967
        $this->next     = null;
149 2967
        $this->previous = null;
150 2967
        $this->depth    = 0;
151 2967
    }
152
153 627
    public function hasChildren(): bool
154
    {
155 627
        return $this->firstChild !== null;
156
    }
157
158 2610
    public function firstChild(): ?Node
159
    {
160 2610
        return $this->firstChild;
161
    }
162
163 2547
    public function lastChild(): ?Node
164
    {
165 2547
        return $this->lastChild;
166
    }
167
168
    /**
169
     * @return Node[]
170
     */
171 2991
    public function children(): iterable
172
    {
173 2991
        $children = [];
174 2991
        for ($current = $this->firstChild; $current !== null; $current = $current->next) {
175 2877
            $children[] = $current;
176
        }
177
178 2991
        return $children;
179
    }
180
181 2949
    public function appendChild(Node $child): void
182
    {
183 2949
        if ($this->lastChild) {
184 2229
            $this->lastChild->insertAfter($child);
185
        } else {
186 2949
            $child->detach();
187 2949
            $child->setParent($this);
188 2949
            $this->lastChild = $this->firstChild = $child;
189
        }
190 2949
    }
191
192
    /**
193
     * Adds $child as the very first child of $this
194
     */
195 69
    public function prependChild(Node $child): void
196
    {
197 69
        if ($this->firstChild) {
198 63
            $this->firstChild->insertBefore($child);
199
        } else {
200 9
            $child->detach();
201 9
            $child->setParent($this);
202 9
            $this->lastChild = $this->firstChild = $child;
203
        }
204 69
    }
205
206
    /**
207
     * Detaches all child nodes of given node
208
     */
209 15
    public function detachChildren(): void
210
    {
211 15
        foreach ($this->children() as $children) {
212 15
            $children->setParent(null);
213
        }
214
215 15
        $this->firstChild = $this->lastChild = null;
216 15
    }
217
218
    /**
219
     * Replace all children of given node with collection of another
220
     *
221
     * @param iterable<Node> $children
222
     */
223 3
    public function replaceChildren(iterable $children): void
224
    {
225 3
        $this->detachChildren();
226 3
        foreach ($children as $item) {
227 3
            $this->appendChild($item);
228
        }
229 3
    }
230
231 2196
    public function getDepth(): int
232
    {
233 2196
        return $this->depth;
234
    }
235
236 576
    public function walker(): NodeWalker
237
    {
238 576
        return new NodeWalker($this);
239
    }
240
241
    /**
242
     * Clone the current node and its children
243
     *
244
     * WARNING: This is a recursive function and should not be called on deeply-nested node trees!
245
     */
246 6
    public function __clone()
247
    {
248
        // Cloned nodes are detached from their parents, siblings, and children
249 6
        $this->parent   = null;
250 6
        $this->previous = null;
251 6
        $this->next     = null;
252
        // But save a copy of the children since we'll need that in a moment
253 6
        $children = $this->children();
254 6
        $this->detachChildren();
255
256
        // The original children get cloned and re-added
257 6
        foreach ($children as $child) {
258 6
            $this->appendChild(clone $child);
259
        }
260 6
    }
261
}
262