1 | <?php |
||||||
2 | |||||||
3 | namespace QueryPath\Helpers; |
||||||
4 | |||||||
5 | |||||||
6 | use QueryPath\CSS\ParseException; |
||||||
7 | use QueryPath\CSS\QueryPathEventHandler; |
||||||
8 | use QueryPath\DOMQuery; |
||||||
9 | use QueryPath\Exception; |
||||||
10 | use QueryPath\Query; |
||||||
11 | use QueryPath\QueryPath; |
||||||
12 | |||||||
13 | trait QueryMutators |
||||||
14 | { |
||||||
15 | |||||||
16 | /** |
||||||
17 | * Empty everything within the specified element. |
||||||
18 | * |
||||||
19 | * A convenience function for removeChildren(). This is equivalent to jQuery's |
||||||
20 | * empty() function. However, `empty` is a built-in in PHP, and cannot be used as a |
||||||
21 | * function name. |
||||||
22 | * |
||||||
23 | * @return \QueryPath\DOMQuery |
||||||
24 | * The DOMQuery object with the newly emptied elements. |
||||||
25 | * @see removeChildren() |
||||||
26 | * @since 2.1 |
||||||
27 | * @author eabrand |
||||||
28 | * @deprecated The removeChildren() function is the preferred method. |
||||||
29 | */ |
||||||
30 | public function emptyElement(): Query |
||||||
31 | { |
||||||
32 | $this->removeChildren(); |
||||||
33 | |||||||
34 | return $this; |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||||
35 | } |
||||||
36 | |||||||
37 | /** |
||||||
38 | * Insert the given markup as the last child. |
||||||
39 | * |
||||||
40 | * The markup will be inserted into each match in the set. |
||||||
41 | * |
||||||
42 | * The same element cannot be inserted multiple times into a document. DOM |
||||||
43 | * documents do not allow a single object to be inserted multiple times |
||||||
44 | * into the DOM. To insert the same XML repeatedly, we must first clone |
||||||
45 | * the object. This has one practical implication: Once you have inserted |
||||||
46 | * an element into the object, you cannot further manipulate the original |
||||||
47 | * element and expect the changes to be replciated in the appended object. |
||||||
48 | * (They are not the same -- there is no shared reference.) Instead, you |
||||||
49 | * will need to retrieve the appended object and operate on that. |
||||||
50 | * |
||||||
51 | * @param mixed $data |
||||||
52 | * This can be either a string (the usual case), or a DOM Element. |
||||||
53 | * @return \QueryPath\DOMQuery |
||||||
54 | * The DOMQuery object. |
||||||
55 | * @see appendTo() |
||||||
56 | * @see prepend() |
||||||
57 | * @throws QueryPath::Exception |
||||||
58 | * Thrown if $data is an unsupported object type. |
||||||
59 | * @throws Exception |
||||||
60 | */ |
||||||
61 | public function append($data): Query |
||||||
62 | { |
||||||
63 | $data = $this->prepareInsert($data); |
||||||
0 ignored issues
–
show
It seems like
prepareInsert() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
64 | if (isset($data)) { |
||||||
65 | if (empty($this->document->documentElement) && $this->matches->count() === 0) { |
||||||
66 | // Then we assume we are writing to the doc root |
||||||
67 | $this->document->appendChild($data); |
||||||
68 | $found = new \SplObjectStorage(); |
||||||
69 | $found->attach($this->document->documentElement); |
||||||
70 | $this->setMatches($found); |
||||||
0 ignored issues
–
show
It seems like
setMatches() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
71 | } else { |
||||||
72 | // You can only append in item once. So in cases where we |
||||||
73 | // need to append multiple times, we have to clone the node. |
||||||
74 | foreach ($this->matches as $m) { |
||||||
75 | // DOMDocumentFragments are even more troublesome, as they don't |
||||||
76 | // always clone correctly. So we have to clone their children. |
||||||
77 | if ($data instanceof \DOMDocumentFragment) { |
||||||
78 | foreach ($data->childNodes as $n) { |
||||||
79 | $m->appendChild($n->cloneNode(true)); |
||||||
80 | } |
||||||
81 | } else { |
||||||
82 | // Otherwise a standard clone will do. |
||||||
83 | $m->appendChild($data->cloneNode(true)); |
||||||
84 | } |
||||||
85 | |||||||
86 | } |
||||||
87 | } |
||||||
88 | |||||||
89 | } |
||||||
90 | |||||||
91 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
92 | } |
||||||
93 | |||||||
94 | /** |
||||||
95 | * Insert the given markup as the first child. |
||||||
96 | * |
||||||
97 | * The markup will be inserted into each match in the set. |
||||||
98 | * |
||||||
99 | * @param mixed $data |
||||||
100 | * This can be either a string (the usual case), or a DOM Element. |
||||||
101 | * @return \QueryPath\DOMQuery |
||||||
102 | * @see append() |
||||||
103 | * @see before() |
||||||
104 | * @see after() |
||||||
105 | * @see prependTo() |
||||||
106 | * @throws QueryPath::Exception |
||||||
107 | * Thrown if $data is an unsupported object type. |
||||||
108 | * @throws Exception |
||||||
109 | */ |
||||||
110 | public function prepend($data): Query |
||||||
111 | { |
||||||
112 | $data = $this->prepareInsert($data); |
||||||
113 | if (isset($data)) { |
||||||
114 | foreach ($this->matches as $m) { |
||||||
115 | $ins = $data->cloneNode(true); |
||||||
116 | if ($m->hasChildNodes()) { |
||||||
117 | $m->insertBefore($ins, $m->childNodes->item(0)); |
||||||
118 | } else { |
||||||
119 | $m->appendChild($ins); |
||||||
120 | } |
||||||
121 | } |
||||||
122 | } |
||||||
123 | |||||||
124 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
125 | } |
||||||
126 | |||||||
127 | /** |
||||||
128 | * Take all nodes in the current object and prepend them to the children nodes of |
||||||
129 | * each matched node in the passed-in DOMQuery object. |
||||||
130 | * |
||||||
131 | * This will iterate through each item in the current DOMQuery object and |
||||||
132 | * add each item to the beginning of the children of each element in the |
||||||
133 | * passed-in DOMQuery object. |
||||||
134 | * |
||||||
135 | * @see insertBefore() |
||||||
136 | * @see insertAfter() |
||||||
137 | * @see prepend() |
||||||
138 | * @see appendTo() |
||||||
139 | * @param DOMQuery $dest |
||||||
140 | * The destination DOMQuery object. |
||||||
141 | * @return \QueryPath\DOMQuery |
||||||
142 | * The original DOMQuery, unmodified. NOT the destination DOMQuery. |
||||||
143 | * @throws QueryPath::Exception |
||||||
144 | * Thrown if $data is an unsupported object type. |
||||||
145 | */ |
||||||
146 | public function prependTo(Query $dest) |
||||||
147 | { |
||||||
148 | foreach ($this->matches as $m) { |
||||||
149 | $dest->prepend($m); |
||||||
0 ignored issues
–
show
The method
prepend() does not exist on QueryPath\Query . It seems like you code against a sub-type of QueryPath\Query such as QueryPath\DOMQuery .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
150 | } |
||||||
151 | |||||||
152 | return $this; |
||||||
153 | } |
||||||
154 | |||||||
155 | /** |
||||||
156 | * Insert the given data before each element in the current set of matches. |
||||||
157 | * |
||||||
158 | * This will take the give data (XML or HTML) and put it before each of the items that |
||||||
159 | * the DOMQuery object currently contains. Contrast this with after(). |
||||||
160 | * |
||||||
161 | * @param mixed $data |
||||||
162 | * The data to be inserted. This can be XML in a string, a DomFragment, a DOMElement, |
||||||
163 | * or the other usual suspects. (See {@link qp()}). |
||||||
164 | * @return \QueryPath\DOMQuery |
||||||
165 | * Returns the DOMQuery with the new modifications. The list of elements currently |
||||||
166 | * selected will remain the same. |
||||||
167 | * @see insertBefore() |
||||||
168 | * @see after() |
||||||
169 | * @see append() |
||||||
170 | * @see prepend() |
||||||
171 | * @throws QueryPath::Exception |
||||||
172 | * Thrown if $data is an unsupported object type. |
||||||
173 | * @throws Exception |
||||||
174 | */ |
||||||
175 | public function before($data): Query |
||||||
176 | { |
||||||
177 | $data = $this->prepareInsert($data); |
||||||
178 | foreach ($this->matches as $m) { |
||||||
179 | $ins = $data->cloneNode(true); |
||||||
180 | $m->parentNode->insertBefore($ins, $m); |
||||||
181 | } |
||||||
182 | |||||||
183 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
184 | } |
||||||
185 | |||||||
186 | /** |
||||||
187 | * Insert the current elements into the destination document. |
||||||
188 | * The items are inserted before each element in the given DOMQuery document. |
||||||
189 | * That is, they will be siblings with the current elements. |
||||||
190 | * |
||||||
191 | * @param Query $dest |
||||||
192 | * Destination DOMQuery document. |
||||||
193 | * @return \QueryPath\DOMQuery |
||||||
194 | * The current DOMQuery object, unaltered. Only the destination DOMQuery |
||||||
195 | * object is altered. |
||||||
196 | * @see before() |
||||||
197 | * @see insertAfter() |
||||||
198 | * @see appendTo() |
||||||
199 | * @throws QueryPath::Exception |
||||||
200 | * Thrown if $data is an unsupported object type. |
||||||
201 | */ |
||||||
202 | public function insertBefore(Query $dest): Query |
||||||
203 | { |
||||||
204 | foreach ($this->matches as $m) { |
||||||
205 | $dest->before($m); |
||||||
0 ignored issues
–
show
The method
before() does not exist on QueryPath\Query . It seems like you code against a sub-type of QueryPath\Query such as QueryPath\DOMQuery .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
206 | } |
||||||
207 | |||||||
208 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
209 | } |
||||||
210 | |||||||
211 | /** |
||||||
212 | * Insert the contents of the current DOMQuery after the nodes in the |
||||||
213 | * destination DOMQuery object. |
||||||
214 | * |
||||||
215 | * @param Query $dest |
||||||
216 | * Destination object where the current elements will be deposited. |
||||||
217 | * @return \QueryPath\DOMQuery |
||||||
218 | * The present DOMQuery, unaltered. Only the destination object is altered. |
||||||
219 | * @see after() |
||||||
220 | * @see insertBefore() |
||||||
221 | * @see append() |
||||||
222 | * @throws QueryPath::Exception |
||||||
223 | * Thrown if $data is an unsupported object type. |
||||||
224 | */ |
||||||
225 | public function insertAfter(Query $dest): Query |
||||||
226 | { |
||||||
227 | foreach ($this->matches as $m) { |
||||||
228 | $dest->after($m); |
||||||
0 ignored issues
–
show
The method
after() does not exist on QueryPath\Query . It seems like you code against a sub-type of QueryPath\Query such as QueryPath\DOMQuery .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
229 | } |
||||||
230 | |||||||
231 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
232 | } |
||||||
233 | |||||||
234 | /** |
||||||
235 | * Insert the given data after each element in the current DOMQuery object. |
||||||
236 | * |
||||||
237 | * This inserts the element as a peer to the currently matched elements. |
||||||
238 | * Contrast this with {@link append()}, which inserts the data as children |
||||||
239 | * of matched elements. |
||||||
240 | * |
||||||
241 | * @param mixed $data |
||||||
242 | * The data to be appended. |
||||||
243 | * @return \QueryPath\DOMQuery |
||||||
244 | * The DOMQuery object (with the items inserted). |
||||||
245 | * @see before() |
||||||
246 | * @see append() |
||||||
247 | * @throws QueryPath::Exception |
||||||
248 | * Thrown if $data is an unsupported object type. |
||||||
249 | * @throws Exception |
||||||
250 | */ |
||||||
251 | public function after($data): Query |
||||||
252 | { |
||||||
253 | if (empty($data)) { |
||||||
254 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
255 | } |
||||||
256 | $data = $this->prepareInsert($data); |
||||||
257 | foreach ($this->matches as $m) { |
||||||
258 | $ins = $data->cloneNode(true); |
||||||
259 | if (isset($m->nextSibling)) { |
||||||
260 | $m->parentNode->insertBefore($ins, $m->nextSibling); |
||||||
261 | } else { |
||||||
262 | $m->parentNode->appendChild($ins); |
||||||
263 | } |
||||||
264 | } |
||||||
265 | |||||||
266 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
267 | } |
||||||
268 | |||||||
269 | /** |
||||||
270 | * Replace the existing element(s) in the list with a new one. |
||||||
271 | * |
||||||
272 | * @param mixed $new |
||||||
273 | * A DOMElement or XML in a string. This will replace all elements |
||||||
274 | * currently wrapped in the DOMQuery object. |
||||||
275 | * @return \QueryPath\DOMQuery |
||||||
276 | * The DOMQuery object wrapping <b>the items that were removed</b>. |
||||||
277 | * This remains consistent with the jQuery API. |
||||||
278 | * @throws Exception |
||||||
279 | * @throws ParseException |
||||||
280 | * @throws QueryPath |
||||||
281 | * @see append() |
||||||
282 | * @see prepend() |
||||||
283 | * @see before() |
||||||
284 | * @see after() |
||||||
285 | * @see remove() |
||||||
286 | * @see replaceAll() |
||||||
287 | */ |
||||||
288 | public function replaceWith($new): Query |
||||||
289 | { |
||||||
290 | $data = $this->prepareInsert($new); |
||||||
291 | $found = new \SplObjectStorage(); |
||||||
292 | foreach ($this->matches as $m) { |
||||||
293 | $parent = $m->parentNode; |
||||||
294 | $parent->insertBefore($data->cloneNode(true), $m); |
||||||
295 | $found->attach($parent->removeChild($m)); |
||||||
296 | } |
||||||
297 | |||||||
298 | return $this->inst($found, NULL); |
||||||
0 ignored issues
–
show
It seems like
inst() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
299 | } |
||||||
300 | |||||||
301 | /** |
||||||
302 | * Remove the parent element from the selected node or nodes. |
||||||
303 | * |
||||||
304 | * This takes the given list of nodes and "unwraps" them, moving them out of their parent |
||||||
305 | * node, and then deleting the parent node. |
||||||
306 | * |
||||||
307 | * For example, consider this: |
||||||
308 | * |
||||||
309 | * @code |
||||||
310 | * <root><wrapper><content/></wrapper></root> |
||||||
311 | * @endcode |
||||||
312 | * |
||||||
313 | * Now we can run this code: |
||||||
314 | * @code |
||||||
315 | * qp($xml, 'content')->unwrap(); |
||||||
316 | * @endcode |
||||||
317 | * |
||||||
318 | * This will result in: |
||||||
319 | * |
||||||
320 | * @code |
||||||
321 | * <root><content/></root> |
||||||
322 | * @endcode |
||||||
323 | * This is the opposite of wrap(). |
||||||
324 | * |
||||||
325 | * <b>The root element cannot be unwrapped.</b> It has no parents. |
||||||
326 | * If you attempt to use unwrap on a root element, this will throw a |
||||||
327 | * QueryPath::Exception. (You can, however, "Unwrap" a child that is |
||||||
328 | * a direct descendant of the root element. This will remove the root |
||||||
329 | * element, and replace the child as the root element. Be careful, though. |
||||||
330 | * You cannot set more than one child as a root element.) |
||||||
331 | * |
||||||
332 | * @return \QueryPath\DOMQuery |
||||||
333 | * The DOMQuery object, with the same element(s) selected. |
||||||
334 | * @throws Exception |
||||||
335 | * @see wrap() |
||||||
336 | * @since 2.1 |
||||||
337 | * @author mbutcher |
||||||
338 | */ |
||||||
339 | public function unwrap(): Query |
||||||
340 | { |
||||||
341 | // We do this in two loops in order to |
||||||
342 | // capture the case where two matches are |
||||||
343 | // under the same parent. Othwerwise we might |
||||||
344 | // remove a match before we can move it. |
||||||
345 | $parents = new \SplObjectStorage(); |
||||||
346 | foreach ($this->matches as $m) { |
||||||
347 | |||||||
348 | // Cannot unwrap the root element. |
||||||
349 | if ($m->isSameNode($m->ownerDocument->documentElement)) { |
||||||
350 | throw new \QueryPath\Exception('Cannot unwrap the root element.'); |
||||||
351 | } |
||||||
352 | |||||||
353 | // Move children to peer of parent. |
||||||
354 | $parent = $m->parentNode; |
||||||
355 | $old = $parent->removeChild($m); |
||||||
356 | $parent->parentNode->insertBefore($old, $parent); |
||||||
357 | $parents->attach($parent); |
||||||
358 | } |
||||||
359 | |||||||
360 | // Now that all the children are moved, we |
||||||
361 | // remove all of the parents. |
||||||
362 | foreach ($parents as $ele) { |
||||||
363 | $ele->parentNode->removeChild($ele); |
||||||
364 | } |
||||||
365 | |||||||
366 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
367 | } |
||||||
368 | |||||||
369 | /** |
||||||
370 | * Wrap each element inside of the given markup. |
||||||
371 | * |
||||||
372 | * Markup is usually a string, but it can also be a DOMNode, a document |
||||||
373 | * fragment, a SimpleXMLElement, or another DOMNode object (in which case |
||||||
374 | * the first item in the list will be used.) |
||||||
375 | * |
||||||
376 | * @param mixed $markup |
||||||
377 | * Markup that will wrap each element in the current list. |
||||||
378 | * @return \QueryPath\DOMQuery |
||||||
379 | * The DOMQuery object with the wrapping changes made. |
||||||
380 | * @throws Exception |
||||||
381 | * @throws QueryPath |
||||||
382 | * @see wrapAll() |
||||||
383 | * @see wrapInner() |
||||||
384 | */ |
||||||
385 | public function wrap($markup): Query |
||||||
386 | { |
||||||
387 | $data = $this->prepareInsert($markup); |
||||||
388 | |||||||
389 | // If the markup passed in is empty, we don't do any wrapping. |
||||||
390 | if (empty($data)) { |
||||||
391 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
392 | } |
||||||
393 | |||||||
394 | foreach ($this->matches as $m) { |
||||||
395 | if ($data instanceof \DOMDocumentFragment) { |
||||||
396 | $copy = $data->firstChild->cloneNode(true); |
||||||
0 ignored issues
–
show
The method
cloneNode() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
397 | } else { |
||||||
398 | $copy = $data->cloneNode(true); |
||||||
399 | } |
||||||
400 | |||||||
401 | // XXX: Should be able to avoid doing this over and over. |
||||||
402 | if ($copy->hasChildNodes()) { |
||||||
403 | $deepest = $this->deepestNode($copy); |
||||||
0 ignored issues
–
show
The method
deepestNode() does not exist on QueryPath\Helpers\QueryMutators . Did you maybe mean deepest() ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
404 | // FIXME: Does this need a different data structure? |
||||||
405 | $bottom = $deepest[0]; |
||||||
406 | } else { |
||||||
407 | $bottom = $copy; |
||||||
408 | } |
||||||
409 | |||||||
410 | $parent = $m->parentNode; |
||||||
411 | $parent->insertBefore($copy, $m); |
||||||
412 | $m = $parent->removeChild($m); |
||||||
413 | $bottom->appendChild($m); |
||||||
414 | } |
||||||
415 | |||||||
416 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
417 | } |
||||||
418 | |||||||
419 | /** |
||||||
420 | * Wrap all elements inside of the given markup. |
||||||
421 | * |
||||||
422 | * So all elements will be grouped together under this single marked up |
||||||
423 | * item. This works by first determining the parent element of the first item |
||||||
424 | * in the list. It then moves all of the matching elements under the wrapper |
||||||
425 | * and inserts the wrapper where that first element was found. (This is in |
||||||
426 | * accordance with the way jQuery works.) |
||||||
427 | * |
||||||
428 | * Markup is usually XML in a string, but it can also be a DOMNode, a document |
||||||
429 | * fragment, a SimpleXMLElement, or another DOMNode object (in which case |
||||||
430 | * the first item in the list will be used.) |
||||||
431 | * |
||||||
432 | * @param string $markup |
||||||
433 | * Markup that will wrap all elements in the current list. |
||||||
434 | * @return \QueryPath\DOMQuery |
||||||
435 | * The DOMQuery object with the wrapping changes made. |
||||||
436 | * @throws Exception |
||||||
437 | * @throws QueryPath |
||||||
438 | * @see wrap() |
||||||
439 | * @see wrapInner() |
||||||
440 | */ |
||||||
441 | public function wrapAll($markup) |
||||||
442 | { |
||||||
443 | if ($this->matches->count() === 0) { |
||||||
444 | return; |
||||||
445 | } |
||||||
446 | |||||||
447 | $data = $this->prepareInsert($markup); |
||||||
448 | |||||||
449 | if (empty($data)) { |
||||||
450 | return $this; |
||||||
451 | } |
||||||
452 | |||||||
453 | if ($data instanceof \DOMDocumentFragment) { |
||||||
454 | $data = $data->firstChild->cloneNode(true); |
||||||
455 | } else { |
||||||
456 | $data = $data->cloneNode(true); |
||||||
457 | } |
||||||
458 | |||||||
459 | if ($data->hasChildNodes()) { |
||||||
460 | $deepest = $this->deepestNode($data); |
||||||
461 | // FIXME: Does this need fixing? |
||||||
462 | $bottom = $deepest[0]; |
||||||
463 | } else { |
||||||
464 | $bottom = $data; |
||||||
465 | } |
||||||
466 | |||||||
467 | $first = $this->getFirstMatch(); |
||||||
0 ignored issues
–
show
It seems like
getFirstMatch() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
468 | $parent = $first->parentNode; |
||||||
469 | $parent->insertBefore($data, $first); |
||||||
470 | foreach ($this->matches as $m) { |
||||||
471 | $bottom->appendChild($m->parentNode->removeChild($m)); |
||||||
472 | } |
||||||
473 | |||||||
474 | return $this; |
||||||
475 | } |
||||||
476 | |||||||
477 | /** |
||||||
478 | * Wrap the child elements of each item in the list with the given markup. |
||||||
479 | * |
||||||
480 | * Markup is usually a string, but it can also be a DOMNode, a document |
||||||
481 | * fragment, a SimpleXMLElement, or another DOMNode object (in which case |
||||||
482 | * the first item in the list will be used.) |
||||||
483 | * |
||||||
484 | * @param string $markup |
||||||
485 | * Markup that will wrap children of each element in the current list. |
||||||
486 | * @return \QueryPath\DOMQuery |
||||||
487 | * The DOMQuery object with the wrapping changes made. |
||||||
488 | * @see wrap() |
||||||
489 | * @see wrapAll() |
||||||
490 | * @throws \QueryPath\Exception |
||||||
491 | * @throws QueryPath |
||||||
492 | */ |
||||||
493 | public function wrapInner($markup) |
||||||
494 | { |
||||||
495 | $data = $this->prepareInsert($markup); |
||||||
496 | |||||||
497 | // No data? Short circuit. |
||||||
498 | if (empty($data)) { |
||||||
499 | return $this; |
||||||
500 | } |
||||||
501 | |||||||
502 | foreach ($this->matches as $m) { |
||||||
503 | if ($data instanceof \DOMDocumentFragment) { |
||||||
504 | $wrapper = $data->firstChild->cloneNode(true); |
||||||
505 | } else { |
||||||
506 | $wrapper = $data->cloneNode(true); |
||||||
507 | } |
||||||
508 | |||||||
509 | if ($wrapper->hasChildNodes()) { |
||||||
510 | $deepest = $this->deepestNode($wrapper); |
||||||
511 | // FIXME: ??? |
||||||
512 | $bottom = $deepest[0]; |
||||||
513 | } else { |
||||||
514 | $bottom = $wrapper; |
||||||
515 | } |
||||||
516 | |||||||
517 | if ($m->hasChildNodes()) { |
||||||
518 | while ($m->firstChild) { |
||||||
519 | $kid = $m->removeChild($m->firstChild); |
||||||
520 | $bottom->appendChild($kid); |
||||||
521 | } |
||||||
522 | } |
||||||
523 | |||||||
524 | $m->appendChild($wrapper); |
||||||
525 | } |
||||||
526 | |||||||
527 | return $this; |
||||||
528 | } |
||||||
529 | |||||||
530 | /** |
||||||
531 | * Reduce the set of matches to the deepest child node in the tree. |
||||||
532 | * |
||||||
533 | * This loops through the matches and looks for the deepest child node of all of |
||||||
534 | * the matches. "Deepest", here, is relative to the nodes in the list. It is |
||||||
535 | * calculated as the distance from the starting node to the most distant child |
||||||
536 | * node. In other words, it is not necessarily the farthest node from the root |
||||||
537 | * element, but the farthest note from the matched element. |
||||||
538 | * |
||||||
539 | * In the case where there are multiple nodes at the same depth, all of the |
||||||
540 | * nodes at that depth will be included. |
||||||
541 | * |
||||||
542 | * @return \QueryPath\DOMQuery |
||||||
543 | * The DOMQuery wrapping the single deepest node. |
||||||
544 | * @throws ParseException |
||||||
545 | */ |
||||||
546 | public function deepest(): Query |
||||||
547 | { |
||||||
548 | $deepest = 0; |
||||||
549 | $winner = new \SplObjectStorage(); |
||||||
550 | foreach ($this->matches as $m) { |
||||||
551 | $local_deepest = 0; |
||||||
552 | $local_ele = $this->deepestNode($m, 0, NULL, $local_deepest); |
||||||
553 | |||||||
554 | // Replace with the new deepest. |
||||||
555 | if ($local_deepest > $deepest) { |
||||||
556 | $winner = new \SplObjectStorage(); |
||||||
557 | foreach ($local_ele as $lele) { |
||||||
558 | $winner->attach($lele); |
||||||
559 | } |
||||||
560 | $deepest = $local_deepest; |
||||||
561 | } // Augument with other equally deep elements. |
||||||
562 | elseif ($local_deepest === $deepest) { |
||||||
563 | foreach ($local_ele as $lele) { |
||||||
564 | $winner->attach($lele); |
||||||
565 | } |
||||||
566 | } |
||||||
567 | } |
||||||
568 | |||||||
569 | return $this->inst($winner, NULL); |
||||||
570 | } |
||||||
571 | |||||||
572 | /** |
||||||
573 | * Add a class to all elements in the current DOMQuery. |
||||||
574 | * |
||||||
575 | * This searchers for a class attribute on each item wrapped by the current |
||||||
576 | * DOMNode object. If no attribute is found, a new one is added and its value |
||||||
577 | * is set to $class. If a class attribute is found, then the value is appended |
||||||
578 | * on to the end. |
||||||
579 | * |
||||||
580 | * @param string $class |
||||||
581 | * The name of the class. |
||||||
582 | * @return \QueryPath\DOMQuery |
||||||
583 | * Returns the DOMQuery object. |
||||||
584 | * @see css() |
||||||
585 | * @see attr() |
||||||
586 | * @see removeClass() |
||||||
587 | * @see hasClass() |
||||||
588 | */ |
||||||
589 | public function addClass($class) |
||||||
590 | { |
||||||
591 | foreach ($this->matches as $m) { |
||||||
592 | if ($m->hasAttribute('class')) { |
||||||
593 | $val = $m->getAttribute('class'); |
||||||
594 | $m->setAttribute('class', $val . ' ' . $class); |
||||||
595 | } else { |
||||||
596 | $m->setAttribute('class', $class); |
||||||
597 | } |
||||||
598 | } |
||||||
599 | |||||||
600 | return $this; |
||||||
601 | } |
||||||
602 | |||||||
603 | /** |
||||||
604 | * Remove the named class from any element in the DOMQuery that has it. |
||||||
605 | * |
||||||
606 | * This may result in the entire class attribute being removed. If there |
||||||
607 | * are other items in the class attribute, though, they will not be removed. |
||||||
608 | * |
||||||
609 | * Example: |
||||||
610 | * Consider this XML: |
||||||
611 | * |
||||||
612 | * @code |
||||||
613 | * <element class="first second"/> |
||||||
614 | * @endcode |
||||||
615 | * |
||||||
616 | * Executing this fragment of code will remove only the 'first' class: |
||||||
617 | * @code |
||||||
618 | * qp(document, 'element')->removeClass('first'); |
||||||
619 | * @endcode |
||||||
620 | * |
||||||
621 | * The resulting XML will be: |
||||||
622 | * @code |
||||||
623 | * <element class="second"/> |
||||||
624 | * @endcode |
||||||
625 | * |
||||||
626 | * To remove the entire 'class' attribute, you should use {@see removeAttr()}. |
||||||
627 | * |
||||||
628 | * @param string $class |
||||||
629 | * The class name to remove. |
||||||
630 | * @return \QueryPath\DOMQuery |
||||||
631 | * The modified DOMNode object. |
||||||
632 | * @see attr() |
||||||
633 | * @see addClass() |
||||||
634 | * @see hasClass() |
||||||
635 | */ |
||||||
636 | public function removeClass($class = false): Query |
||||||
637 | { |
||||||
638 | if (empty($class)) { |
||||||
639 | foreach ($this->matches as $m) { |
||||||
640 | $m->removeAttribute('class'); |
||||||
641 | } |
||||||
642 | } else { |
||||||
643 | $to_remove = array_filter(explode(' ', $class)); |
||||||
644 | foreach ($this->matches as $m) { |
||||||
645 | if ($m->hasAttribute('class')) { |
||||||
646 | $vals = array_filter(explode(' ', $m->getAttribute('class'))); |
||||||
647 | $buf = []; |
||||||
648 | foreach ($vals as $v) { |
||||||
649 | if (!in_array($v, $to_remove)) { |
||||||
650 | $buf[] = $v; |
||||||
651 | } |
||||||
652 | } |
||||||
653 | if (empty($buf)) { |
||||||
654 | $m->removeAttribute('class'); |
||||||
655 | } else { |
||||||
656 | $m->setAttribute('class', implode(' ', $buf)); |
||||||
657 | } |
||||||
658 | } |
||||||
659 | } |
||||||
660 | } |
||||||
661 | |||||||
662 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
663 | } |
||||||
664 | |||||||
665 | /** |
||||||
666 | * Detach any items from the list if they match the selector. |
||||||
667 | * |
||||||
668 | * In other words, each item that matches the selector will be removed |
||||||
669 | * from the DOM document. The returned DOMQuery wraps the list of |
||||||
670 | * removed elements. |
||||||
671 | * |
||||||
672 | * If no selector is specified, this will remove all current matches from |
||||||
673 | * the document. |
||||||
674 | * |
||||||
675 | * @param string $selector |
||||||
676 | * A CSS Selector. |
||||||
677 | * @return \QueryPath\DOMQuery |
||||||
678 | * The Query path wrapping a list of removed items. |
||||||
679 | * @see replaceAll() |
||||||
680 | * @see replaceWith() |
||||||
681 | * @see removeChildren() |
||||||
682 | * @since 2.1 |
||||||
683 | * @author eabrand |
||||||
684 | * @throws ParseException |
||||||
685 | */ |
||||||
686 | public function detach($selector = NULL): Query |
||||||
687 | { |
||||||
688 | if (NULL !== $selector) { |
||||||
689 | $this->find($selector); |
||||||
0 ignored issues
–
show
It seems like
find() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
690 | } |
||||||
691 | |||||||
692 | $found = new \SplObjectStorage(); |
||||||
693 | $this->last = $this->matches; |
||||||
0 ignored issues
–
show
|
|||||||
694 | foreach ($this->matches as $item) { |
||||||
695 | // The item returned is (according to docs) different from |
||||||
696 | // the one passed in, so we have to re-store it. |
||||||
697 | $found->attach($item->parentNode->removeChild($item)); |
||||||
698 | } |
||||||
699 | |||||||
700 | return $this->inst($found, NULL); |
||||||
701 | } |
||||||
702 | |||||||
703 | /** |
||||||
704 | * Attach any items from the list if they match the selector. |
||||||
705 | * |
||||||
706 | * If no selector is specified, this will remove all current matches from |
||||||
707 | * the document. |
||||||
708 | * |
||||||
709 | * @param DOMQuery $dest |
||||||
710 | * A DOMQuery Selector. |
||||||
711 | * @return \QueryPath\DOMQuery |
||||||
712 | * The Query path wrapping a list of removed items. |
||||||
713 | * @see replaceAll() |
||||||
714 | * @see replaceWith() |
||||||
715 | * @see removeChildren() |
||||||
716 | * @since 2.1 |
||||||
717 | * @author eabrand |
||||||
718 | * @throws QueryPath |
||||||
719 | * @throws Exception |
||||||
720 | */ |
||||||
721 | public function attach(DOMQuery $dest): Query |
||||||
722 | { |
||||||
723 | foreach ($this->last as $m) { |
||||||
724 | $dest->append($m); |
||||||
725 | } |
||||||
726 | |||||||
727 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
728 | } |
||||||
729 | |||||||
730 | /** |
||||||
731 | * Append the current elements to the destination passed into the function. |
||||||
732 | * |
||||||
733 | * This cycles through all of the current matches and appends them to |
||||||
734 | * the context given in $destination. If a selector is provided then the |
||||||
735 | * $destination is queried (using that selector) prior to the data being |
||||||
736 | * appended. The data is then appended to the found items. |
||||||
737 | * |
||||||
738 | * @param DOMQuery $dest |
||||||
739 | * A DOMQuery object that will be appended to. |
||||||
740 | * @return \QueryPath\DOMQuery |
||||||
741 | * The original DOMQuery, unaltered. Only the destination DOMQuery will |
||||||
742 | * be modified. |
||||||
743 | * @see append() |
||||||
744 | * @see prependTo() |
||||||
745 | * @throws QueryPath::Exception |
||||||
746 | * Thrown if $data is an unsupported object type. |
||||||
747 | * @throws Exception |
||||||
748 | */ |
||||||
749 | public function appendTo(DOMQuery $dest): Query |
||||||
750 | { |
||||||
751 | foreach ($this->matches as $m) { |
||||||
752 | $dest->append($m); |
||||||
753 | } |
||||||
754 | |||||||
755 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
756 | } |
||||||
757 | |||||||
758 | /** |
||||||
759 | * Remove any items from the list if they match the selector. |
||||||
760 | * |
||||||
761 | * In other words, each item that matches the selector will be remove |
||||||
762 | * from the DOM document. The returned DOMQuery wraps the list of |
||||||
763 | * removed elements. |
||||||
764 | * |
||||||
765 | * If no selector is specified, this will remove all current matches from |
||||||
766 | * the document. |
||||||
767 | * |
||||||
768 | * @param string $selector |
||||||
769 | * A CSS Selector. |
||||||
770 | * @return \QueryPath\DOMQuery |
||||||
771 | * The Query path wrapping a list of removed items. |
||||||
772 | * @see replaceAll() |
||||||
773 | * @see replaceWith() |
||||||
774 | * @see removeChildren() |
||||||
775 | * @throws ParseException |
||||||
776 | */ |
||||||
777 | public function remove($selector = NULL): Query |
||||||
778 | { |
||||||
779 | if (!empty($selector)) { |
||||||
780 | // Do a non-destructive find. |
||||||
781 | $query = new QueryPathEventHandler($this->matches); |
||||||
782 | $query->find($selector); |
||||||
783 | $matches = $query->getMatches(); |
||||||
784 | } else { |
||||||
785 | $matches = $this->matches; |
||||||
786 | } |
||||||
787 | |||||||
788 | $found = new \SplObjectStorage(); |
||||||
789 | foreach ($matches as $item) { |
||||||
790 | // The item returned is (according to docs) different from |
||||||
791 | // the one passed in, so we have to re-store it. |
||||||
792 | $found->attach($item->parentNode->removeChild($item)); |
||||||
793 | } |
||||||
794 | |||||||
795 | // Return a clone DOMQuery with just the removed items. If |
||||||
796 | // no items are found, this will return an empty DOMQuery. |
||||||
797 | return count($found) === 0 ? new static() : new static($found); |
||||||
0 ignored issues
–
show
The call to
QueryPath\Helpers\QueryMutators::__construct() has too many arguments starting with $found .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
798 | } |
||||||
799 | |||||||
800 | /** |
||||||
801 | * This replaces everything that matches the selector with the first value |
||||||
802 | * in the current list. |
||||||
803 | * |
||||||
804 | * This is the reverse of replaceWith. |
||||||
805 | * |
||||||
806 | * Unlike jQuery, DOMQuery cannot assume a default document. Consequently, |
||||||
807 | * you must specify the intended destination document. If it is omitted, the |
||||||
808 | * present document is assumed to be tthe document. However, that can result |
||||||
809 | * in undefined behavior if the selector and the replacement are not sufficiently |
||||||
810 | * distinct. |
||||||
811 | * |
||||||
812 | * @param string $selector |
||||||
813 | * The selector. |
||||||
814 | * @param \DOMDocument $document |
||||||
815 | * The destination document. |
||||||
816 | * @return \QueryPath\DOMQuery |
||||||
817 | * The DOMQuery wrapping the modified document. |
||||||
818 | * @deprecated Due to the fact that this is not a particularly friendly method, |
||||||
819 | * and that it can be easily replicated using {@see replaceWith()}, it is to be |
||||||
820 | * considered deprecated. |
||||||
821 | * @see remove() |
||||||
822 | * @see replaceWith() |
||||||
823 | * @throws ParseException |
||||||
824 | */ |
||||||
825 | public function replaceAll($selector, \DOMDocument $document): Query |
||||||
826 | { |
||||||
827 | $replacement = $this->matches->count() > 0 ? $this->getFirstMatch() : $this->document->createTextNode(''); |
||||||
828 | |||||||
829 | $c = new QueryPathEventHandler($document); |
||||||
830 | $c->find($selector); |
||||||
831 | $temp = $c->getMatches(); |
||||||
832 | foreach ($temp as $item) { |
||||||
833 | $node = $replacement->cloneNode(); |
||||||
834 | $node = $document->importNode($node); |
||||||
835 | $item->parentNode->replaceChild($node, $item); |
||||||
836 | } |
||||||
837 | |||||||
838 | return QueryPath::with($document, NULL, $this->options); |
||||||
839 | } |
||||||
840 | |||||||
841 | /** |
||||||
842 | * Add more elements to the current set of matches. |
||||||
843 | * |
||||||
844 | * This begins the new query at the top of the DOM again. The results found |
||||||
845 | * when running this selector are then merged into the existing results. In |
||||||
846 | * this way, you can add additional elements to the existing set. |
||||||
847 | * |
||||||
848 | * @param string $selector |
||||||
849 | * A valid selector. |
||||||
850 | * @return \QueryPath\DOMQuery |
||||||
851 | * The DOMQuery object with the newly added elements. |
||||||
852 | * @see append() |
||||||
853 | * @see after() |
||||||
854 | * @see andSelf() |
||||||
855 | * @see end() |
||||||
856 | */ |
||||||
857 | public function add($selector): Query |
||||||
858 | { |
||||||
859 | |||||||
860 | // This is destructive, so we need to set $last: |
||||||
861 | $this->last = $this->matches; |
||||||
0 ignored issues
–
show
|
|||||||
862 | |||||||
863 | foreach (QueryPath::with($this->document, $selector, $this->options)->get() as $item) { |
||||||
864 | $this->matches->attach($item); |
||||||
865 | } |
||||||
866 | |||||||
867 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
868 | } |
||||||
869 | |||||||
870 | /** |
||||||
871 | * Remove all child nodes. |
||||||
872 | * |
||||||
873 | * This is equivalent to jQuery's empty() function. (However, empty() is a |
||||||
874 | * PHP built-in, and cannot be used as a method name.) |
||||||
875 | * |
||||||
876 | * @return \QueryPath\DOMQuery |
||||||
877 | * The DOMQuery object with the child nodes removed. |
||||||
878 | * @see replaceWith() |
||||||
879 | * @see replaceAll() |
||||||
880 | * @see remove() |
||||||
881 | */ |
||||||
882 | public function removeChildren(): Query |
||||||
883 | { |
||||||
884 | foreach ($this->matches as $m) { |
||||||
885 | while ($kid = $m->firstChild) { |
||||||
886 | $m->removeChild($kid); |
||||||
887 | } |
||||||
888 | } |
||||||
889 | |||||||
890 | return $this; |
||||||
0 ignored issues
–
show
|
|||||||
891 | } |
||||||
892 | |||||||
893 | /** |
||||||
894 | * Get/set an attribute. |
||||||
895 | * - If no parameters are specified, this returns an associative array of all |
||||||
896 | * name/value pairs. |
||||||
897 | * - If both $name and $value are set, then this will set the attribute name/value |
||||||
898 | * pair for all items in this object. |
||||||
899 | * - If $name is set, and is an array, then |
||||||
900 | * all attributes in the array will be set for all items in this object. |
||||||
901 | * - If $name is a string and is set, then the attribute value will be returned. |
||||||
902 | * |
||||||
903 | * When an attribute value is retrieved, only the attribute value of the FIRST |
||||||
904 | * match is returned. |
||||||
905 | * |
||||||
906 | * @param mixed $name |
||||||
907 | * The name of the attribute or an associative array of name/value pairs. |
||||||
908 | * @param string $value |
||||||
909 | * A value (used only when setting an individual property). |
||||||
910 | * @return mixed |
||||||
911 | * If this was a setter request, return the DOMQuery object. If this was |
||||||
912 | * an access request (getter), return the string value. |
||||||
913 | * @see removeAttr() |
||||||
914 | * @see tag() |
||||||
915 | * @see hasAttr() |
||||||
916 | * @see hasClass() |
||||||
917 | */ |
||||||
918 | public function attr($name = NULL, $value = NULL) |
||||||
919 | { |
||||||
920 | // Default case: Return all attributes as an assoc array. |
||||||
921 | if (is_null($name)) { |
||||||
922 | if ($this->matches->count() === 0) { |
||||||
923 | return NULL; |
||||||
924 | } |
||||||
925 | $ele = $this->getFirstMatch(); |
||||||
926 | $buffer = []; |
||||||
927 | |||||||
928 | // This does not appear to be part of the DOM |
||||||
929 | // spec. Nor is it documented. But it works. |
||||||
930 | foreach ($ele->attributes as $name => $attrNode) { |
||||||
931 | $buffer[$name] = $attrNode->value; |
||||||
932 | } |
||||||
933 | |||||||
934 | return $buffer; |
||||||
935 | } |
||||||
936 | |||||||
937 | // multi-setter |
||||||
938 | if (is_array($name)) { |
||||||
939 | foreach ($name as $k => $v) { |
||||||
940 | foreach ($this->matches as $m) { |
||||||
941 | $m->setAttribute($k, $v); |
||||||
942 | } |
||||||
943 | } |
||||||
944 | |||||||
945 | return $this; |
||||||
946 | } |
||||||
947 | // setter |
||||||
948 | if (isset($value)) { |
||||||
949 | foreach ($this->matches as $m) { |
||||||
950 | $m->setAttribute($name, $value); |
||||||
951 | } |
||||||
952 | |||||||
953 | return $this; |
||||||
954 | } |
||||||
955 | |||||||
956 | //getter |
||||||
957 | if ($this->matches->count() === 0) { |
||||||
958 | return NULL; |
||||||
959 | } |
||||||
960 | |||||||
961 | // Special node type handler: |
||||||
962 | if ($name === 'nodeType') { |
||||||
963 | return $this->getFirstMatch()->nodeType; |
||||||
964 | } |
||||||
965 | |||||||
966 | // Always return first match's attr. |
||||||
967 | return $this->getFirstMatch()->getAttribute($name); |
||||||
968 | } |
||||||
969 | |||||||
970 | /** |
||||||
971 | * Set/get a CSS value for the current element(s). |
||||||
972 | * This sets the CSS value for each element in the DOMQuery object. |
||||||
973 | * It does this by setting (or getting) the style attribute (without a namespace). |
||||||
974 | * |
||||||
975 | * For example, consider this code: |
||||||
976 | * |
||||||
977 | * @code |
||||||
978 | * <?php |
||||||
979 | * qp(HTML_STUB, 'body')->css('background-color','red')->html(); |
||||||
980 | * ?> |
||||||
981 | * @endcode |
||||||
982 | * This will return the following HTML: |
||||||
983 | * @code |
||||||
984 | * <body style="background-color: red"/> |
||||||
985 | * @endcode |
||||||
986 | * |
||||||
987 | * If no parameters are passed into this function, then the current style |
||||||
988 | * element will be returned unparsed. Example: |
||||||
989 | * @code |
||||||
990 | * <?php |
||||||
991 | * qp(HTML_STUB, 'body')->css('background-color','red')->css(); |
||||||
992 | * ?> |
||||||
993 | * @endcode |
||||||
994 | * This will return the following: |
||||||
995 | * @code |
||||||
996 | * background-color: red |
||||||
997 | * @endcode |
||||||
998 | * |
||||||
999 | * As of QueryPath 2.1, existing style attributes will be merged with new attributes. |
||||||
1000 | * (In previous versions of QueryPath, a call to css() overwrite the existing style |
||||||
1001 | * values). |
||||||
1002 | * |
||||||
1003 | * @param mixed $name |
||||||
1004 | * If this is a string, it will be used as a CSS name. If it is an array, |
||||||
1005 | * this will assume it is an array of name/value pairs of CSS rules. It will |
||||||
1006 | * apply all rules to all elements in the set. |
||||||
1007 | * @param string $value |
||||||
1008 | * The value to set. This is only set if $name is a string. |
||||||
1009 | * @return \QueryPath\DOMQuery |
||||||
1010 | */ |
||||||
1011 | public function css($name = NULL, $value = '') |
||||||
1012 | { |
||||||
1013 | if (empty($name)) { |
||||||
1014 | return $this->attr('style'); |
||||||
1015 | } |
||||||
1016 | |||||||
1017 | // Get any existing CSS. |
||||||
1018 | $css = []; |
||||||
1019 | foreach ($this->matches as $match) { |
||||||
1020 | $style = $match->getAttribute('style'); |
||||||
1021 | if (!empty($style)) { |
||||||
1022 | // XXX: Is this sufficient? |
||||||
1023 | $style_array = explode(';', $style); |
||||||
1024 | foreach ($style_array as $item) { |
||||||
1025 | $item = trim($item); |
||||||
1026 | |||||||
1027 | // Skip empty attributes. |
||||||
1028 | if ($item === '') { |
||||||
1029 | continue; |
||||||
1030 | } |
||||||
1031 | |||||||
1032 | [$css_att, $css_val] = explode(':', $item, 2); |
||||||
1033 | $css[$css_att] = trim($css_val); |
||||||
1034 | } |
||||||
1035 | } |
||||||
1036 | } |
||||||
1037 | |||||||
1038 | if (is_array($name)) { |
||||||
1039 | // Use array_merge instead of + to preserve order. |
||||||
1040 | $css = array_merge($css, $name); |
||||||
1041 | } else { |
||||||
1042 | $css[$name] = $value; |
||||||
1043 | } |
||||||
1044 | |||||||
1045 | // Collapse CSS into a string. |
||||||
1046 | $format = '%s: %s;'; |
||||||
1047 | $css_string = ''; |
||||||
1048 | foreach ($css as $n => $v) { |
||||||
1049 | $css_string .= sprintf($format, $n, trim($v)); |
||||||
1050 | } |
||||||
1051 | |||||||
1052 | $this->attr('style', $css_string); |
||||||
1053 | |||||||
1054 | return $this; |
||||||
1055 | } |
||||||
1056 | } |