Completed
Push — master ( a84270...16e29a )
by Colin
01:02
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 498
    public function previous(): ?Node
50
    {
51 498
        return $this->previous;
52
    }
53
54 1626
    public function next(): ?Node
55
    {
56 1626
        return $this->next;
57
    }
58
59 2100
    public function parent(): ?Node
60
    {
61 2100
        return $this->parent;
62
    }
63
64 2595
    protected function setParent(Node $node = null): void
65
    {
66 2595
        $this->parent = $node;
67 2595
        $this->depth = ($node === null) ? 0 : $node->depth + 1;
68 2595
    }
69
70
    /**
71
     * Inserts the $sibling node after $this
72
     *
73
     * @param Node $sibling
74
     *
75
     * @return void
76
     */
77 1929
    public function insertAfter(Node $sibling): void
78
    {
79 1929
        $sibling->detach();
80 1929
        $sibling->next = $this->next;
81
82 1929
        if ($sibling->next) {
83 774
            $sibling->next->previous = $sibling;
84
        }
85
86 1929
        $sibling->previous = $this;
87 1929
        $this->next = $sibling;
88 1929
        $sibling->setParent($this->parent);
89
90 1929
        if (!$sibling->next && $sibling->parent) {
91 1920
            $sibling->parent->lastChild = $sibling;
92
        }
93 1929
    }
94
95
    /**
96
     * Inserts the $sibling node before $this
97
     *
98
     * @param Node $sibling
99
     *
100
     * @return void
101
     */
102 222
    public function insertBefore(Node $sibling): void
103
    {
104 222
        $sibling->detach();
105 222
        $sibling->previous = $this->previous;
106
107 222
        if ($sibling->previous) {
108 114
            $sibling->previous->next = $sibling;
109
        }
110
111 222
        $sibling->next = $this;
112 222
        $this->previous = $sibling;
113 222
        $sibling->setParent($this->parent);
114
115 222
        if (!$sibling->previous && $sibling->parent) {
116 174
            $sibling->parent->firstChild = $sibling;
117
        }
118 222
    }
119
120 360
    public function replaceWith(Node $replacement): void
121
    {
122 360
        $replacement->detach();
123 360
        $this->insertAfter($replacement);
124 360
        $this->detach();
125 360
    }
126
127 2592
    public function detach(): void
128
    {
129 2592
        if ($this->previous) {
130 1281
            $this->previous->next = $this->next;
131 2592
        } elseif ($this->parent) {
132 696
            $this->parent->firstChild = $this->next;
133
        }
134
135 2592
        if ($this->next) {
136 1038
            $this->next->previous = $this->previous;
137 2592
        } elseif ($this->parent) {
138 1236
            $this->parent->lastChild = $this->previous;
139
        }
140
141 2592
        $this->parent = null;
142 2592
        $this->next = null;
143 2592
        $this->previous = null;
144 2592
        $this->depth = 0;
145 2592
    }
146
147 318
    public function hasChildren(): bool
148
    {
149 318
        return $this->firstChild !== null;
150
    }
151
152 2250
    public function firstChild(): ?Node
153
    {
154 2250
        return $this->firstChild;
155
    }
156
157 2220
    public function lastChild(): ?Node
158
    {
159 2220
        return $this->lastChild;
160
    }
161
162
    /**
163
     * @return Node[]
164
     */
165 2622
    public function children(): iterable
166
    {
167 2622
        $children = [];
168 2622
        for ($current = $this->firstChild; null !== $current; $current = $current->next) {
169 2532
            $children[] = $current;
170
        }
171
172 2622
        return $children;
173
    }
174
175 2574
    public function appendChild(Node $child): void
176
    {
177 2574
        if ($this->lastChild) {
178 1917
            $this->lastChild->insertAfter($child);
179
        } else {
180 2574
            $child->detach();
181 2574
            $child->setParent($this);
182 2574
            $this->lastChild = $this->firstChild = $child;
183
        }
184 2574
    }
185
186
    /**
187
     * Adds $child as the very first child of $this
188
     *
189
     * @param Node $child
190
     *
191
     * @return void
192
     */
193 54
    public function prependChild(Node $child): void
194
    {
195 54
        if ($this->firstChild) {
196 51
            $this->firstChild->insertBefore($child);
197
        } else {
198 6
            $child->detach();
199 6
            $child->setParent($this);
200 6
            $this->lastChild = $this->firstChild = $child;
201
        }
202 54
    }
203
204
    /**
205
     * Detaches all child nodes of given node
206
     *
207
     * @return void
208
     */
209 12
    public function detachChildren(): void
210
    {
211 12
        foreach ($this->children() as $children) {
212 12
            $children->setParent(null);
213
        }
214 12
        $this->firstChild = $this->lastChild = null;
215 12
    }
216
217
    /**
218
     * Replace all children of given node with collection of another
219
     *
220
     * @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...
221
     *
222
     * @return void
223
     */
224 3
    public function replaceChildren(iterable $children): void
225
    {
226 3
        $this->detachChildren();
227 3
        foreach ($children as $item) {
228 3
            $this->appendChild($item);
229
        }
230 3
    }
231
232 2112
    public function getDepth(): int
233
    {
234 2112
        return $this->depth;
235
    }
236
237 264
    public function walker(): NodeWalker
238
    {
239 264
        return new NodeWalker($this);
240
    }
241
242
    /**
243
     * Clone the current node and its children
244
     *
245
     * WARNING: This is a recursive function and should not be called on deeply-nested node trees!
246
     */
247 6
    public function __clone()
248
    {
249
        // Cloned nodes are detached from their parents, siblings, and children
250 6
        $this->parent = null;
251 6
        $this->previous = null;
252 6
        $this->next = null;
253
        // But save a copy of the children since we'll need that in a moment
254 6
        $children = $this->children();
255 6
        $this->detachChildren();
256
257
        // The original children get cloned and re-added
258 6
        foreach ($children as $child) {
259 6
            $this->appendChild(clone $child);
260
        }
261 6
    }
262
}
263