Passed
Push — improvements ( 54d781 )
by Colin
02:54
created

Node::assertInstanceOf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

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 2
nc 2
nop 1
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 3651
    public function __construct()
73
    {
74 3651
        $this->data = new Data([
75 3651
            'attributes' => [],
76
        ]);
77 3651
    }
78
79 618
    public function previous(): ?Node
80
    {
81 618
        return $this->previous;
82
    }
83
84 2052
    public function next(): ?Node
85
    {
86 2052
        return $this->next;
87
    }
88
89 2631
    public function parent(): ?Node
90
    {
91 2631
        return $this->parent;
92
    }
93
94 3204
    protected function setParent(?Node $node = null): void
95
    {
96 3204
        $this->parent = $node;
97 3204
        $this->depth  = $node === null ? 0 : $node->depth + 1;
98 3204
    }
99
100
    /**
101
     * Inserts the $sibling node after $this
102
     */
103 2490
    public function insertAfter(Node $sibling): void
104
    {
105 2490
        $sibling->detach();
106 2490
        $sibling->next = $this->next;
107
108 2490
        if ($sibling->next) {
109 885
            $sibling->next->previous = $sibling;
110
        }
111
112 2490
        $sibling->previous = $this;
113 2490
        $this->next        = $sibling;
114 2490
        $sibling->setParent($this->parent);
115
116 2490
        if (! $sibling->next && $sibling->parent) {
117 2481
            $sibling->parent->lastChild = $sibling;
118
        }
119 2490
    }
120
121
    /**
122
     * Inserts the $sibling node before $this
123
     */
124 129
    public function insertBefore(Node $sibling): void
125
    {
126 129
        $sibling->detach();
127 129
        $sibling->previous = $this->previous;
128
129 129
        if ($sibling->previous) {
130 42
            $sibling->previous->next = $sibling;
131
        }
132
133 129
        $sibling->next  = $this;
134 129
        $this->previous = $sibling;
135 129
        $sibling->setParent($this->parent);
136
137 129
        if (! $sibling->previous && $sibling->parent) {
138 81
            $sibling->parent->firstChild = $sibling;
139
        }
140 129
    }
141
142 474
    public function replaceWith(Node $replacement): void
143
    {
144 474
        $replacement->detach();
145 474
        $this->insertAfter($replacement);
146 474
        $this->detach();
147 474
    }
148
149 3201
    public function detach(): void
150
    {
151 3201
        if ($this->previous) {
152 1401
            $this->previous->next = $this->next;
153 3201
        } elseif ($this->parent) {
154 807
            $this->parent->firstChild = $this->next;
155
        }
156
157 3201
        if ($this->next) {
158 1263
            $this->next->previous = $this->previous;
159 3201
        } elseif ($this->parent) {
160 1296
            $this->parent->lastChild = $this->previous;
161
        }
162
163 3201
        $this->parent   = null;
164 3201
        $this->next     = null;
165 3201
        $this->previous = null;
166 3201
        $this->depth    = 0;
167 3201
    }
168
169 657
    public function hasChildren(): bool
170
    {
171 657
        return $this->firstChild !== null;
172
    }
173
174 2820
    public function firstChild(): ?Node
175
    {
176 2820
        return $this->firstChild;
177
    }
178
179 2709
    public function lastChild(): ?Node
180
    {
181 2709
        return $this->lastChild;
182
    }
183
184
    /**
185
     * @return Node[]
186
     */
187 3198
    public function children(): iterable
188
    {
189 3198
        $children = [];
190 3198
        for ($current = $this->firstChild; $current !== null; $current = $current->next) {
191 3078
            $children[] = $current;
192
        }
193
194 3198
        return $children;
195
    }
196
197 3183
    public function appendChild(Node $child): void
198
    {
199 3183
        if ($this->lastChild) {
200 2472
            $this->lastChild->insertAfter($child);
201
        } else {
202 3183
            $child->detach();
203 3183
            $child->setParent($this);
204 3183
            $this->lastChild = $this->firstChild = $child;
205
        }
206 3183
    }
207
208
    /**
209
     * Adds $child as the very first child of $this
210
     */
211 84
    public function prependChild(Node $child): void
212
    {
213 84
        if ($this->firstChild) {
214 78
            $this->firstChild->insertBefore($child);
215
        } else {
216 9
            $child->detach();
217 9
            $child->setParent($this);
218 9
            $this->lastChild = $this->firstChild = $child;
219
        }
220 84
    }
221
222
    /**
223
     * Detaches all child nodes of given node
224
     */
225 33
    public function detachChildren(): void
226
    {
227 33
        foreach ($this->children() as $children) {
228 18
            $children->setParent(null);
229
        }
230
231 33
        $this->firstChild = $this->lastChild = null;
232 33
    }
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 2340
    public function getDepth(): int
248
    {
249 2340
        return $this->depth;
250
    }
251
252 576
    public function walker(): NodeWalker
253
    {
254 576
        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 3264
    public static function assertInstanceOf(Node $node): void
279
    {
280 3264
        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 3192
    }
284
}
285