1 | <?php |
||
2 | |||
3 | namespace Sulao\HtmlQuery; |
||
4 | |||
5 | use DOMNode; |
||
6 | use DOMNodeList; |
||
7 | |||
8 | /** |
||
9 | * Class HtmlQueryNode |
||
10 | * |
||
11 | * @package Sulao\HtmlQuery |
||
12 | */ |
||
13 | abstract class HtmlQueryNode extends HtmlQueryAttribute |
||
14 | { |
||
15 | /** |
||
16 | * Insert content or node(s) before each matched node. |
||
17 | * |
||
18 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
19 | * |
||
20 | * @return static |
||
21 | 3 | */ |
|
22 | public function before($content) |
||
23 | 3 | { |
|
24 | $content = $this->contentResolve($content); |
||
25 | |||
26 | return $this->each(function (HtmlNode $node, $index) use ($content) { |
||
27 | 3 | $content->each(function (DOMNode $newNode) use ($node, $index) { |
|
28 | 3 | $newNode = $this->newNode($newNode, $index); |
|
29 | 3 | $node->before($newNode); |
|
30 | 3 | }); |
|
31 | }); |
||
32 | } |
||
33 | |||
34 | /** |
||
35 | * Insert every matched node before the target. |
||
36 | * |
||
37 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $selector |
||
38 | * |
||
39 | * @return static |
||
40 | 1 | */ |
|
41 | public function insertBefore($selector) |
||
42 | 1 | { |
|
43 | $target = $this->targetResolve($selector); |
||
44 | 1 | ||
45 | return $target->before($this); |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * Insert content or node(s) after each matched node. |
||
50 | * |
||
51 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
52 | * |
||
53 | * @return static |
||
54 | 5 | */ |
|
55 | public function after($content) |
||
56 | 5 | { |
|
57 | $content = $this->contentResolve($content); |
||
58 | |||
59 | return $this->each(function (HtmlNode $node, $index) use ($content) { |
||
60 | 5 | $content->each(function (DOMNode $newNode) use ($node, $index) { |
|
61 | 5 | $newNode = $this->newNode($newNode, $index); |
|
62 | 5 | $node->after($newNode); |
|
63 | 5 | }, true); |
|
64 | }); |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * Insert every matched node after the target. |
||
69 | * |
||
70 | * @param string|DOMNode|DOMNode[]DOMNodeList|static $selector |
||
71 | * |
||
72 | * @return static |
||
73 | 3 | */ |
|
74 | public function insertAfter($selector) |
||
75 | 3 | { |
|
76 | $target = $this->targetResolve($selector); |
||
77 | 3 | ||
78 | return $target->after($this); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Insert content or node(s) to the end of every matched node. |
||
83 | * |
||
84 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
85 | * |
||
86 | * @return static |
||
87 | 6 | */ |
|
88 | public function append($content) |
||
89 | 6 | { |
|
90 | $content = $this->contentResolve($content); |
||
91 | |||
92 | return $this->each(function (HtmlNode $node, $index) use ($content) { |
||
93 | 6 | $content->each(function (DOMNode $newNode) use ($node, $index) { |
|
94 | 6 | $newNode = $this->newNode($newNode, $index); |
|
95 | 6 | $node->append($newNode); |
|
96 | 6 | }); |
|
97 | }); |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Insert every matched node to the end of the target. |
||
102 | * |
||
103 | * @param string|DOMNode|DOMNode[]DOMNodeList|static $selector |
||
104 | * |
||
105 | * @return static |
||
106 | 2 | */ |
|
107 | public function appendTo($selector) |
||
108 | 2 | { |
|
109 | $target = $this->targetResolve($selector); |
||
110 | 2 | ||
111 | return $target->append($this); |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * Insert content or node(s) to the beginning of each matched node. |
||
116 | * |
||
117 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
118 | * |
||
119 | * @return static |
||
120 | 3 | */ |
|
121 | public function prepend($content) |
||
122 | 3 | { |
|
123 | $content = $this->contentResolve($content); |
||
124 | |||
125 | return $this->each(function (HtmlNode $node, $index) use ($content) { |
||
126 | 3 | $content->each(function (DOMNode $newNode) use ($node, $index) { |
|
127 | 3 | $newNode = $this->newNode($newNode, $index); |
|
128 | 3 | $node->prepend($newNode); |
|
129 | 3 | }, true); |
|
130 | }); |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Insert every matched node to the beginning of the target. |
||
135 | * |
||
136 | * @param string|DOMNode|DOMNode[]DOMNodeList|static $selector |
||
137 | * |
||
138 | * @return static |
||
139 | 2 | */ |
|
140 | public function prependTo($selector) |
||
141 | 2 | { |
|
142 | $target = $this->targetResolve($selector); |
||
143 | 2 | ||
144 | return $target->prepend($this); |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Replace each matched node with the provided new content or node(s) |
||
149 | * |
||
150 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
151 | * |
||
152 | * @return static |
||
153 | 2 | */ |
|
154 | public function replaceWith($content) |
||
155 | 2 | { |
|
156 | $content = $this->contentResolve($content); |
||
157 | 2 | return $this->each(function (DOMNode $node, $index) use ($content) { |
|
158 | 1 | if (!$node->parentNode) { |
|
159 | return; |
||
160 | } |
||
161 | 2 | ||
162 | 2 | $len = $content->count(); |
|
163 | $content->each( |
||
164 | 2 | function (DOMNode $newNode) use ($node, $index, $len) { |
|
165 | $newNode = $this->newNode($newNode, $index); |
||
166 | 2 | ||
167 | 2 | if ($len === 1) { |
|
168 | $node->parentNode->replaceChild($newNode, $node); |
||
169 | 2 | } else { |
|
170 | $this->resolve($newNode)->insertAfter($node); |
||
171 | 2 | } |
|
172 | 2 | }, |
|
173 | true |
||
174 | ); |
||
175 | 2 | ||
176 | 2 | if ($len !== 1) { |
|
177 | $node->parentNode->removeChild($node); |
||
178 | 2 | } |
|
179 | }); |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * Replace each target node with the matched node(s) |
||
184 | * |
||
185 | * @param string|DOMNode|DOMNode[]DOMNodeList|static $selector |
||
186 | * |
||
187 | * @return static |
||
188 | 1 | */ |
|
189 | public function replaceAll($selector) |
||
190 | 1 | { |
|
191 | $target = $this->targetResolve($selector); |
||
192 | 1 | ||
193 | return $target->replaceWith($this); |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Wrap an HTML structure around each matched node. |
||
198 | * |
||
199 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
200 | * |
||
201 | * @return static |
||
202 | 3 | */ |
|
203 | public function wrap($content) |
||
204 | 3 | { |
|
205 | 3 | $content = $this->contentResolve($content); |
|
206 | $newNode = $content[0]; |
||
207 | 3 | ||
208 | 1 | if (empty($newNode)) { |
|
209 | return $this; |
||
210 | } |
||
211 | |||
212 | 3 | return $this->each(function (DOMNode $node, $index) use ($newNode) { |
|
213 | $newNode = $this->newNode($newNode, $index); |
||
214 | 3 | ||
215 | 3 | $nodes = $this->xpathQuery('descendant::*[last()]', $newNode); |
|
216 | 1 | if (!$nodes) { |
|
0 ignored issues
–
show
|
|||
217 | throw new Exception('Invalid wrap html format.'); |
||
218 | } |
||
219 | 3 | ||
220 | 3 | $deepestNode = end($nodes); |
|
221 | 3 | $node->parentNode->replaceChild($newNode, $node); |
|
222 | 3 | $deepestNode->appendChild($node); |
|
223 | }); |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Wrap an HTML structure around the content of each matched node. |
||
228 | * |
||
229 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
230 | * |
||
231 | * @return static |
||
232 | 1 | */ |
|
233 | public function wrapInner($content) |
||
234 | 1 | { |
|
235 | 1 | $content = $this->contentResolve($content); |
|
236 | $newNode = $content[0]; |
||
237 | 1 | ||
238 | 1 | if (empty($newNode)) { |
|
239 | return $this; |
||
240 | } |
||
241 | |||
242 | 1 | return $this->each(function (DOMNode $node, $index) use ($newNode) { |
|
243 | $newNode = $this->newNode($newNode, $index); |
||
244 | 1 | ||
245 | 1 | $nodes = $this->xpathQuery('descendant::*[last()]', $newNode); |
|
246 | 1 | if (!$nodes) { |
|
0 ignored issues
–
show
The expression
$nodes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
247 | throw new Exception('Invalid wrap html format.'); |
||
248 | } |
||
249 | 1 | ||
250 | $deepestNode = end($nodes); |
||
251 | 1 | ||
252 | 1 | foreach (iterator_to_array($node->childNodes) as $childNode) { |
|
253 | $deepestNode->appendChild($childNode); |
||
254 | } |
||
255 | 1 | ||
256 | 1 | $node->appendChild($newNode); |
|
257 | }); |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * Wrap an HTML structure around all matched nodes. |
||
262 | * |
||
263 | * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content |
||
264 | * |
||
265 | * @return static |
||
266 | 1 | */ |
|
267 | public function wrapAll($content) |
||
268 | 1 | { |
|
269 | 1 | $content = $this->contentResolve($content); |
|
270 | 1 | if (!$content->count()) { |
|
271 | return $this; |
||
272 | } |
||
273 | 1 | ||
274 | $newNode = $content[0]; |
||
275 | 1 | $this->each(function (DOMNode $node, $index) use ($newNode) { |
|
276 | 1 | if ($index === 0) { |
|
277 | $this->resolve($node)->wrap($newNode); |
||
278 | 1 | } else { |
|
279 | $this->nodes[0]->parentNode->appendChild($node); |
||
280 | 1 | } |
|
281 | }); |
||
282 | 1 | ||
283 | return $this; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Remove the parents of the matched nodes from the DOM. |
||
288 | * A optional selector to check the parent node against. |
||
289 | * |
||
290 | * @param string|null $selector |
||
291 | * |
||
292 | * @return static |
||
293 | 2 | */ |
|
294 | public function unwrap(?string $selector = null) |
||
295 | 2 | { |
|
296 | return $this->parent($selector)->unwrapSelf(); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Remove the HTML tag of the matched nodes from the DOM. |
||
301 | * Leaving the child nodes in their place. |
||
302 | * |
||
303 | * @return static |
||
304 | 3 | */ |
|
305 | public function unwrapSelf() |
||
306 | { |
||
307 | 3 | return $this->each(function (HtmlNode $node) { |
|
308 | 3 | $node->unwrapSelf(); |
|
309 | }); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * When the selection needs a new node, return the original one or a clone. |
||
314 | * |
||
315 | * @param DOMNode $newNode |
||
316 | * @param int $index |
||
317 | * |
||
318 | * @return DOMNode |
||
319 | 17 | */ |
|
320 | protected function newNode(DOMNode $newNode, int $index) |
||
321 | 17 | { |
|
322 | 13 | return $index !== $this->count() - 1 |
|
323 | 17 | ? $newNode->cloneNode(true) |
|
324 | : $newNode; |
||
325 | } |
||
326 | } |
||
327 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.