Completed
Push — 1.5 ( 93aa3f...fe2226 )
by Colin
01:05
created

Node::__clone()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 15
c 0
b 0
f 0
ccs 9
cts 9
cp 1
rs 9.7666
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 *
8
 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
9
 *  - (c) John MacFarlane
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace League\CommonMark\Node;
16
17
abstract class Node
18
{
19
    /**
20
     * @var int
21
     */
22
    protected $depth = 0;
23
24
    /**
25
     * @var Node|null
26
     */
27
    protected $parent;
28
29
    /**
30
     * @var Node|null
31
     */
32
    protected $previous;
33
34
    /**
35
     * @var Node|null
36
     */
37
    protected $next;
38
39
    /**
40
     * @var Node|null
41
     */
42
    protected $firstChild;
43
44
    /**
45
     * @var Node|null
46
     */
47
    protected $lastChild;
48
49 537
    public function previous(): ?Node
50
    {
51 537
        return $this->previous;
52
    }
53
54 2529
    public function next(): ?Node
55
    {
56 2529
        return $this->next;
57
    }
58
59 2559
    public function parent(): ?Node
60
    {
61 2559
        return $this->parent;
62
    }
63
64
    /**
65
     * @param Node|null $node
66
     *
67
     * @return void
68
     */
69 2613
    protected function setParent(Node $node = null)
70
    {
71 2613
        $this->parent = $node;
72 2613
        $this->depth = ($node === null) ? 0 : $node->depth + 1;
73 2613
    }
74
75
    /**
76
     * Inserts the $sibling node after $this
77
     *
78
     * @param Node $sibling
79
     *
80
     * @return void
81
     */
82 2091
    public function insertAfter(Node $sibling)
83
    {
84 2091
        $sibling->detach();
85 2091
        $sibling->next = $this->next;
86
87 2091
        if ($sibling->next) {
88 813
            $sibling->next->previous = $sibling;
89
        }
90
91 2091
        $sibling->previous = $this;
92 2091
        $this->next = $sibling;
93 2091
        $sibling->setParent($this->parent);
94
95 2091
        if (!$sibling->next && $sibling->parent) {
96 2082
            $sibling->parent->lastChild = $sibling;
97
        }
98 2091
    }
99
100
    /**
101
     * Inserts the $sibling node before $this
102
     *
103
     * @param Node $sibling
104
     *
105
     * @return void
106
     */
107 222
    public function insertBefore(Node $sibling)
108
    {
109 222
        $sibling->detach();
110 222
        $sibling->previous = $this->previous;
111
112 222
        if ($sibling->previous) {
113 114
            $sibling->previous->next = $sibling;
114
        }
115
116 222
        $sibling->next = $this;
117 222
        $this->previous = $sibling;
118 222
        $sibling->setParent($this->parent);
119
120 222
        if (!$sibling->previous && $sibling->parent) {
121 174
            $sibling->parent->firstChild = $sibling;
122
        }
123 222
    }
124
125
    /**
126
     * @param Node $replacement
127
     *
128
     * @return void
129
     */
130 486
    public function replaceWith(Node $replacement)
131
    {
132 486
        $replacement->detach();
133 486
        $this->insertAfter($replacement);
134 486
        $this->detach();
135 486
    }
136
137
    /**
138
     * @return void
139
     */
140 2610
    public function detach()
141
    {
142 2610
        if ($this->previous) {
143 1338
            $this->previous->next = $this->next;
144 2610
        } elseif ($this->parent) {
145 693
            $this->parent->firstChild = $this->next;
146
        }
147
148 2610
        if ($this->next) {
149 1197
            $this->next->previous = $this->previous;
150 2610
        } elseif ($this->parent) {
151 1155
            $this->parent->lastChild = $this->previous;
152
        }
153
154 2610
        $this->parent = null;
155 2610
        $this->next = null;
156 2610
        $this->previous = null;
157 2610
        $this->depth = 0;
158 2610
    }
159
160
    abstract public function isContainer(): bool;
161
162 2526
    public function firstChild(): ?Node
163
    {
164 2526
        return $this->firstChild;
165
    }
166
167 2508
    public function lastChild(): ?Node
168
    {
169 2508
        return $this->lastChild;
170
    }
171
172
    /**
173
     * @return Node[]
174
     */
175 2616
    public function children(): iterable
176
    {
177 2616
        $children = [];
178 2616
        for ($current = $this->firstChild; null !== $current; $current = $current->next) {
179 2526
            $children[] = $current;
180
        }
181
182 2616
        return $children;
183
    }
184
185
    /**
186
     * @param Node $child
187
     *
188
     * @return void
189
     */
190 2592
    public function appendChild(Node $child)
191
    {
192 2592
        if ($this->lastChild) {
193 2064
            $this->lastChild->insertAfter($child);
194
        } else {
195 2592
            $child->detach();
196 2592
            $child->setParent($this);
197 2592
            $this->lastChild = $this->firstChild = $child;
198
        }
199 2592
    }
200
201
    /**
202
     * Adds $child as the very first child of $this
203
     *
204
     * @param Node $child
205
     *
206
     * @return void
207
     */
208 54
    public function prependChild(Node $child)
209
    {
210 54
        if ($this->firstChild) {
211 51
            $this->firstChild->insertBefore($child);
212
        } else {
213 6
            $child->detach();
214 6
            $child->setParent($this);
215 6
            $this->lastChild = $this->firstChild = $child;
216
        }
217 54
    }
218
219
    /**
220
     * Detaches all child nodes of given node
221
     *
222
     * @return void
223
     */
224 12
    public function detachChildren()
225
    {
226 12
        foreach ($this->children() as $children) {
227 12
            $children->setParent(null);
228
        }
229 12
        $this->firstChild = $this->lastChild = null;
230 12
    }
231
232
    /**
233
     * Replace all children of given node with collection of another
234
     *
235
     * @param iterable<Node> $children
0 ignored issues
show
Documentation introduced by
The doc-type iterable<Node> could not be parsed: Expected "|" or "end of type", but got "<" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
236
     *
237
     * @return $this
238
     */
239 3
    public function replaceChildren(iterable $children)
240
    {
241 3
        $this->detachChildren();
242 3
        foreach ($children as $item) {
243 3
            $this->appendChild($item);
244
        }
245
246 3
        return $this;
247
    }
248
249 435
    public function getDepth(): int
250
    {
251 435
        return $this->depth;
252
    }
253
254 2511
    public function walker(): NodeWalker
255
    {
256 2511
        return new NodeWalker($this);
257
    }
258
259
    /**
260
     * Clone the current node and its children
261
     *
262
     * WARNING: This is a recursive function and should not be called on deeply-nested node trees!
263
     */
264 6
    public function __clone()
265
    {
266
        // Cloned nodes are detached from their parents, siblings, and children
267 6
        $this->parent = null;
268 6
        $this->previous = null;
269 6
        $this->next = null;
270
        // But save a copy of the children since we'll need that in a moment
271 6
        $children = $this->children();
272 6
        $this->detachChildren();
273
274
        // The original children get cloned and re-added
275 6
        foreach ($children as $child) {
276 6
            $this->appendChild(clone $child);
277
        }
278 6
    }
279
}
280