Passed
Push — latest ( 981bad...ca3ef7 )
by Colin
02:16 queued 10s
created

Node::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 3
cts 3
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 3453
    public function __construct()
73
    {
74 3453
        $this->data = new Data([
75 3453
            'attributes' => [],
76
        ]);
77 3453
    }
78
79 522
    public function previous(): ?Node
80
    {
81 522
        return $this->previous;
82
    }
83
84 1923
    public function next(): ?Node
85
    {
86 1923
        return $this->next;
87
    }
88
89 2475
    public function parent(): ?Node
90
    {
91 2475
        return $this->parent;
92
    }
93
94 3033
    protected function setParent(?Node $node = null): void
95
    {
96 3033
        $this->parent = $node;
97 3033
        $this->depth  = $node === null ? 0 : $node->depth + 1;
98 3033
    }
99
100
    /**
101
     * Inserts the $sibling node after $this
102
     */
103 2340
    public function insertAfter(Node $sibling): void
104
    {
105 2340
        $sibling->detach();
106 2340
        $sibling->next = $this->next;
107
108 2340
        if ($sibling->next) {
109 813
            $sibling->next->previous = $sibling;
110
        }
111
112 2340
        $sibling->previous = $this;
113 2340
        $this->next        = $sibling;
114 2340
        $sibling->setParent($this->parent);
115
116 2340
        if (! $sibling->next && $sibling->parent) {
117 2331
            $sibling->parent->lastChild = $sibling;
118
        }
119 2340
    }
120
121
    /**
122
     * Inserts the $sibling node before $this
123
     */
124 123
    public function insertBefore(Node $sibling): void
125
    {
126 123
        $sibling->detach();
127 123
        $sibling->previous = $this->previous;
128
129 123
        if ($sibling->previous) {
130 42
            $sibling->previous->next = $sibling;
131
        }
132
133 123
        $sibling->next  = $this;
134 123
        $this->previous = $sibling;
135 123
        $sibling->setParent($this->parent);
136
137 123
        if (! $sibling->previous && $sibling->parent) {
138 75
            $sibling->parent->firstChild = $sibling;
139
        }
140 123
    }
141
142 402
    public function replaceWith(Node $replacement): void
143
    {
144 402
        $replacement->detach();
145 402
        $this->insertAfter($replacement);
146 402
        $this->detach();
147 402
    }
148
149 3030
    public function detach(): void
150
    {
151 3030
        if ($this->previous) {
152 1299
            $this->previous->next = $this->next;
153 3030
        } elseif ($this->parent) {
154 723
            $this->parent->firstChild = $this->next;
155
        }
156
157 3030
        if ($this->next) {
158 1164
            $this->next->previous = $this->previous;
159 3030
        } elseif ($this->parent) {
160 1185
            $this->parent->lastChild = $this->previous;
161
        }
162
163 3030
        $this->parent   = null;
164 3030
        $this->next     = null;
165 3030
        $this->previous = null;
166 3030
        $this->depth    = 0;
167 3030
    }
168
169 501
    public function hasChildren(): bool
170
    {
171 501
        return $this->firstChild !== null;
172
    }
173
174 2670
    public function firstChild(): ?Node
175
    {
176 2670
        return $this->firstChild;
177
    }
178
179 2604
    public function lastChild(): ?Node
180
    {
181 2604
        return $this->lastChild;
182
    }
183
184
    /**
185
     * @return Node[]
186
     */
187 3048
    public function children(): iterable
188
    {
189 3048
        $children = [];
190 3048
        for ($current = $this->firstChild; $current !== null; $current = $current->next) {
191 2931
            $children[] = $current;
192
        }
193
194 3048
        return $children;
195
    }
196
197 3012
    public function appendChild(Node $child): void
198
    {
199 3012
        if ($this->lastChild) {
200 2322
            $this->lastChild->insertAfter($child);
201
        } else {
202 3012
            $child->detach();
203 3012
            $child->setParent($this);
204 3012
            $this->lastChild = $this->firstChild = $child;
205
        }
206 3012
    }
207
208
    /**
209
     * Adds $child as the very first child of $this
210
     */
211 78
    public function prependChild(Node $child): void
212
    {
213 78
        if ($this->firstChild) {
214 72
            $this->firstChild->insertBefore($child);
215
        } else {
216 9
            $child->detach();
217 9
            $child->setParent($this);
218 9
            $this->lastChild = $this->firstChild = $child;
219
        }
220 78
    }
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 2238
    public function getDepth(): int
248
    {
249 2238
        return $this->depth;
250
    }
251
252 423
    public function walker(): NodeWalker
253
    {
254 423
        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