1 | <?php |
||||||
2 | |||||||
3 | namespace Wa72\HtmlPageDom; |
||||||
4 | |||||||
5 | use Symfony\Component\DomCrawler\Crawler; |
||||||
6 | |||||||
7 | /** |
||||||
8 | * Extends \Symfony\Component\DomCrawler\Crawler by adding tree manipulation functions |
||||||
9 | * for HTML documents inspired by jQuery such as setInnerHtml(), css(), append(), prepend(), before(), |
||||||
10 | * addClass(), removeClass() |
||||||
11 | * |
||||||
12 | * @author Christoph Singer |
||||||
13 | * @license MIT |
||||||
14 | * |
||||||
15 | */ |
||||||
16 | class HtmlPageCrawler extends Crawler |
||||||
17 | { |
||||||
18 | /** |
||||||
19 | * the (internal) root element name used when importing html fragments |
||||||
20 | * */ |
||||||
21 | public const FRAGMENT_ROOT_TAGNAME = '_root'; |
||||||
22 | |||||||
23 | /** |
||||||
24 | * Get an HtmlPageCrawler object from a HTML string, DOMNode, DOMNodeList or HtmlPageCrawler |
||||||
25 | * |
||||||
26 | * This is the equivalent to jQuery's $() function when used for wrapping DOMNodes or creating DOMElements from HTML code. |
||||||
27 | * |
||||||
28 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList|array $content |
||||||
29 | * @return HtmlPageCrawler |
||||||
30 | * @api |
||||||
31 | */ |
||||||
32 | 136 | public static function create($content) |
|||||
33 | { |
||||||
34 | 136 | if ($content instanceof HtmlPageCrawler) { |
|||||
35 | 24 | return $content; |
|||||
36 | } |
||||||
37 | |||||||
38 | 136 | return new HtmlPageCrawler($content); |
|||||
39 | } |
||||||
40 | |||||||
41 | /** |
||||||
42 | * Adds the specified class(es) to each element in the set of matched elements. |
||||||
43 | * |
||||||
44 | * @param string $name One or more space-separated classes to be added to the class attribute of each matched element. |
||||||
45 | * @return HtmlPageCrawler $this for chaining |
||||||
46 | * @api |
||||||
47 | */ |
||||||
48 | 8 | public function addClass($name) |
|||||
49 | { |
||||||
50 | 8 | foreach ($this as $node) { |
|||||
51 | 8 | if ($node instanceof \DOMElement) { |
|||||
52 | /** @var \DOMElement $node */ |
||||||
53 | 8 | $classes = preg_split('/\s+/s', $node->getAttribute('class')); |
|||||
54 | 8 | $found = false; |
|||||
55 | 8 | $count = count($classes); |
|||||
56 | 8 | for ($i = 0; $i < $count; $i++) { |
|||||
57 | 8 | if ($classes[$i] == $name) { |
|||||
58 | 8 | $found = true; |
|||||
59 | } |
||||||
60 | } |
||||||
61 | 8 | if (!$found) { |
|||||
62 | 8 | $classes[] = $name; |
|||||
63 | 8 | $node->setAttribute('class', trim(join(' ', $classes))); |
|||||
64 | } |
||||||
65 | } |
||||||
66 | } |
||||||
67 | |||||||
68 | 8 | return $this; |
|||||
69 | } |
||||||
70 | |||||||
71 | /** |
||||||
72 | * Insert content, specified by the parameter, after each element in the set of matched elements. |
||||||
73 | * |
||||||
74 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content |
||||||
75 | * @return HtmlPageCrawler $this for chaining |
||||||
76 | * @api |
||||||
77 | */ |
||||||
78 | 24 | public function after($content) |
|||||
79 | { |
||||||
80 | 24 | $content = self::create($content); |
|||||
81 | 24 | $newnodes = []; |
|||||
82 | 24 | foreach ($this as $i => $node) { |
|||||
83 | /** @var \DOMNode $node */ |
||||||
84 | 24 | $refnode = $node->nextSibling; |
|||||
85 | 24 | foreach ($content as $newnode) { |
|||||
86 | /** @var \DOMNode $newnode */ |
||||||
87 | 24 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
88 | 24 | if ($refnode === null) { |
|||||
89 | 24 | $node->parentNode->appendChild($newnode); |
|||||
0 ignored issues
–
show
|
|||||||
90 | } else { |
||||||
91 | 8 | $node->parentNode->insertBefore($newnode, $refnode); |
|||||
92 | } |
||||||
93 | 24 | $newnodes[] = $newnode; |
|||||
94 | } |
||||||
95 | } |
||||||
96 | 24 | $content->clear(); |
|||||
97 | 24 | $content->add($newnodes); |
|||||
98 | |||||||
99 | 24 | return $this; |
|||||
100 | } |
||||||
101 | |||||||
102 | /** |
||||||
103 | * Insert HTML content as child nodes of each element after existing children |
||||||
104 | * |
||||||
105 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content HTML code fragment or DOMNode to append |
||||||
106 | * @return HtmlPageCrawler $this for chaining |
||||||
107 | * @api |
||||||
108 | */ |
||||||
109 | 16 | public function append($content) |
|||||
110 | { |
||||||
111 | 16 | $content = self::create($content); |
|||||
112 | 16 | $newnodes = []; |
|||||
113 | 16 | foreach ($this as $i => $node) { |
|||||
114 | /** @var \DOMNode $node */ |
||||||
115 | 16 | foreach ($content as $newnode) { |
|||||
116 | /** @var \DOMNode $newnode */ |
||||||
117 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
118 | 16 | $node->appendChild($newnode); |
|||||
119 | 16 | $newnodes[] = $newnode; |
|||||
120 | } |
||||||
121 | } |
||||||
122 | 16 | $content->clear(); |
|||||
123 | 16 | $content->add($newnodes); |
|||||
124 | |||||||
125 | 16 | return $this; |
|||||
126 | } |
||||||
127 | |||||||
128 | /** |
||||||
129 | * Insert every element in the set of matched elements to the end of the target. |
||||||
130 | * |
||||||
131 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $element |
||||||
132 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler A new Crawler object containing all elements appended to the target elements |
||||||
133 | * @api |
||||||
134 | */ |
||||||
135 | 16 | public function appendTo($element) |
|||||
136 | { |
||||||
137 | 16 | $e = self::create($element); |
|||||
138 | 16 | $newnodes = []; |
|||||
139 | 16 | foreach ($e as $i => $node) { |
|||||
140 | /** @var \DOMNode $node */ |
||||||
141 | 16 | foreach ($this as $newnode) { |
|||||
142 | /** @var \DOMNode $newnode */ |
||||||
143 | 16 | if ($node !== $newnode) { |
|||||
144 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
145 | 16 | $node->appendChild($newnode); |
|||||
146 | } |
||||||
147 | 16 | $newnodes[] = $newnode; |
|||||
148 | } |
||||||
149 | } |
||||||
150 | |||||||
151 | 16 | return self::create($newnodes); |
|||||
152 | } |
||||||
153 | |||||||
154 | /** |
||||||
155 | * Sets an attribute on each element |
||||||
156 | * |
||||||
157 | * @param string $name |
||||||
158 | * @param string $value |
||||||
159 | * @return HtmlPageCrawler $this for chaining |
||||||
160 | * @api |
||||||
161 | */ |
||||||
162 | 24 | public function setAttribute($name, $value) |
|||||
163 | { |
||||||
164 | 24 | foreach ($this as $node) { |
|||||
165 | 24 | if ($node instanceof \DOMElement) { |
|||||
166 | /** @var \DOMElement $node */ |
||||||
167 | 24 | $node->setAttribute($name, $value); |
|||||
168 | } |
||||||
169 | } |
||||||
170 | |||||||
171 | 24 | return $this; |
|||||
172 | } |
||||||
173 | |||||||
174 | /** |
||||||
175 | * Returns the attribute value of the first node of the list. |
||||||
176 | * This is just an alias for attr() for naming consistency with setAttribute() |
||||||
177 | * |
||||||
178 | * @param string $name The attribute name |
||||||
179 | * @throws \InvalidArgumentException When current node is empty |
||||||
180 | * @return string|null The attribute value or null if the attribute does not exist |
||||||
181 | */ |
||||||
182 | 8 | public function getAttribute($name) |
|||||
183 | { |
||||||
184 | 8 | return parent::attr($name); |
|||||
185 | } |
||||||
186 | |||||||
187 | /** |
||||||
188 | * Insert content, specified by the parameter, before each element in the set of matched elements. |
||||||
189 | * |
||||||
190 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content |
||||||
191 | * @return HtmlPageCrawler $this for chaining |
||||||
192 | * @api |
||||||
193 | */ |
||||||
194 | 16 | public function before($content) |
|||||
195 | { |
||||||
196 | 16 | $content = self::create($content); |
|||||
197 | 16 | $newnodes = []; |
|||||
198 | 16 | foreach ($this as $i => $node) { |
|||||
199 | /** @var \DOMNode $node */ |
||||||
200 | 16 | foreach ($content as $newnode) { |
|||||
201 | /** @var \DOMNode $newnode */ |
||||||
202 | 16 | if ($node !== $newnode) { |
|||||
203 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
204 | 16 | $node->parentNode->insertBefore($newnode, $node); |
|||||
205 | 16 | $newnodes[] = $newnode; |
|||||
206 | } |
||||||
207 | } |
||||||
208 | } |
||||||
209 | 16 | $content->clear(); |
|||||
210 | 16 | $content->add($newnodes); |
|||||
211 | |||||||
212 | 16 | return $this; |
|||||
213 | } |
||||||
214 | |||||||
215 | /** |
||||||
216 | * Create a deep copy of the set of matched elements. |
||||||
217 | * |
||||||
218 | * Equivalent to clone() in jQuery (clone is not a valid PHP function name) |
||||||
219 | * |
||||||
220 | * @return HtmlPageCrawler |
||||||
221 | * @api |
||||||
222 | */ |
||||||
223 | 8 | public function makeClone() |
|||||
224 | { |
||||||
225 | 8 | return clone $this; |
|||||
226 | } |
||||||
227 | |||||||
228 | 8 | public function __clone() |
|||||
229 | { |
||||||
230 | 8 | $newnodes = []; |
|||||
231 | 8 | foreach ($this as $node) { |
|||||
232 | /** @var \DOMNode $node */ |
||||||
233 | 8 | $newnodes[] = $node->cloneNode(true); |
|||||
234 | } |
||||||
235 | 8 | $this->clear(); |
|||||
236 | 8 | $this->add($newnodes); |
|||||
237 | 8 | } |
|||||
238 | |||||||
239 | /** |
||||||
240 | * Get one CSS style property of the first element or set it for all elements in the list |
||||||
241 | * |
||||||
242 | * Function is here for compatibility with jQuery; it is the same as getStyle() and setStyle() |
||||||
243 | * |
||||||
244 | * @see HtmlPageCrawler::getStyle() |
||||||
245 | * @see HtmlPageCrawler::setStyle() |
||||||
246 | * |
||||||
247 | * @param string $key The name of the style property |
||||||
248 | * @param null|string $value The CSS value to set, or NULL to get the current value |
||||||
249 | * @return HtmlPageCrawler|string If no param is provided, returns the CSS styles of the first element |
||||||
250 | * @api |
||||||
251 | */ |
||||||
252 | 8 | public function css($key, $value = null) |
|||||
253 | { |
||||||
254 | 8 | if (null === $value) { |
|||||
255 | 8 | return $this->getStyle($key); |
|||||
256 | } |
||||||
257 | |||||||
258 | 8 | return $this->setStyle($key, $value); |
|||||
259 | } |
||||||
260 | |||||||
261 | /** |
||||||
262 | * get one CSS style property of the first element |
||||||
263 | * |
||||||
264 | * @param string $key name of the property |
||||||
265 | * @return string|null value of the property |
||||||
266 | */ |
||||||
267 | 8 | public function getStyle($key) |
|||||
268 | { |
||||||
269 | 8 | $styles = Helpers::cssStringToArray($this->getAttribute('style')); |
|||||
270 | |||||||
271 | 8 | return (isset($styles[$key]) ? $styles[$key] : null); |
|||||
272 | } |
||||||
273 | |||||||
274 | /** |
||||||
275 | * set one CSS style property for all elements in the list |
||||||
276 | * |
||||||
277 | * @param string $key name of the property |
||||||
278 | * @param string $value value of the property |
||||||
279 | * @return HtmlPageCrawler $this for chaining |
||||||
280 | */ |
||||||
281 | 8 | public function setStyle($key, $value) |
|||||
282 | { |
||||||
283 | 8 | foreach ($this as $node) { |
|||||
284 | 8 | if ($node instanceof \DOMElement) { |
|||||
285 | /** @var \DOMElement $node */ |
||||||
286 | 8 | $styles = Helpers::cssStringToArray($node->getAttribute('style')); |
|||||
287 | 8 | if ($value != '') { |
|||||
288 | 8 | $styles[$key] = $value; |
|||||
289 | 8 | } elseif (isset($styles[$key])) { |
|||||
290 | 8 | unset($styles[$key]); |
|||||
291 | } |
||||||
292 | 8 | $node->setAttribute('style', Helpers::cssArrayToString($styles)); |
|||||
293 | } |
||||||
294 | } |
||||||
295 | |||||||
296 | 8 | return $this; |
|||||
297 | } |
||||||
298 | |||||||
299 | /** |
||||||
300 | * Removes all child nodes and text from all nodes in set |
||||||
301 | * |
||||||
302 | * Equivalent to jQuery's empty() function which is not a valid function name in PHP |
||||||
303 | * @return HtmlPageCrawler $this |
||||||
304 | * @api |
||||||
305 | */ |
||||||
306 | 8 | public function makeEmpty() |
|||||
307 | { |
||||||
308 | 8 | foreach ($this as $node) { |
|||||
309 | 8 | $node->nodeValue = ''; |
|||||
310 | } |
||||||
311 | |||||||
312 | 8 | return $this; |
|||||
313 | } |
||||||
314 | |||||||
315 | /** |
||||||
316 | * Determine whether any of the matched elements are assigned the given class. |
||||||
317 | * |
||||||
318 | * @param string $name |
||||||
319 | * @return bool |
||||||
320 | * @api |
||||||
321 | */ |
||||||
322 | 16 | public function hasClass($name) |
|||||
323 | { |
||||||
324 | 16 | foreach ($this as $node) { |
|||||
325 | 16 | if ($node instanceof \DOMElement && $class = $node->getAttribute('class')) { |
|||||
326 | 16 | $classes = preg_split('/\s+/s', $class); |
|||||
327 | 16 | if (in_array($name, $classes)) { |
|||||
328 | 16 | return true; |
|||||
329 | } |
||||||
330 | } |
||||||
331 | } |
||||||
332 | |||||||
333 | 16 | return false; |
|||||
334 | } |
||||||
335 | |||||||
336 | /** |
||||||
337 | * Set the HTML contents of each element |
||||||
338 | * |
||||||
339 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content HTML code fragment |
||||||
340 | * @return HtmlPageCrawler $this for chaining |
||||||
341 | * @api |
||||||
342 | */ |
||||||
343 | 24 | public function setInnerHtml($content) |
|||||
344 | { |
||||||
345 | 24 | $content = self::create($content); |
|||||
346 | 24 | foreach ($this as $node) { |
|||||
347 | 24 | $node->nodeValue = ''; |
|||||
348 | 24 | foreach ($content as $newnode) { |
|||||
349 | /** @var \DOMNode $node */ |
||||||
350 | /** @var \DOMNode $newnode */ |
||||||
351 | 24 | $newnode = static::importNewnode($newnode, $node); |
|||||
352 | 24 | $node->appendChild($newnode); |
|||||
353 | } |
||||||
354 | } |
||||||
355 | |||||||
356 | 24 | return $this; |
|||||
357 | } |
||||||
358 | |||||||
359 | /** |
||||||
360 | * Alias for Crawler::html() for naming consistency with setInnerHtml() |
||||||
361 | * |
||||||
362 | * @return string |
||||||
363 | * @api |
||||||
364 | */ |
||||||
365 | 8 | public function getInnerHtml() |
|||||
366 | { |
||||||
367 | 8 | return parent::html(); |
|||||
368 | } |
||||||
369 | |||||||
370 | /** |
||||||
371 | * Insert every element in the set of matched elements after the target. |
||||||
372 | * |
||||||
373 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $element |
||||||
374 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler A new Crawler object containing all elements appended to the target elements |
||||||
375 | * @api |
||||||
376 | */ |
||||||
377 | 16 | public function insertAfter($element) |
|||||
378 | { |
||||||
379 | 16 | $e = self::create($element); |
|||||
380 | 16 | $newnodes = []; |
|||||
381 | 16 | foreach ($e as $i => $node) { |
|||||
382 | /** @var \DOMNode $node */ |
||||||
383 | 16 | $refnode = $node->nextSibling; |
|||||
384 | 16 | foreach ($this as $newnode) { |
|||||
385 | /** @var \DOMNode $newnode */ |
||||||
386 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
387 | 16 | if ($refnode === null) { |
|||||
388 | 16 | $node->parentNode->appendChild($newnode); |
|||||
389 | } else { |
||||||
390 | 8 | $node->parentNode->insertBefore($newnode, $refnode); |
|||||
391 | } |
||||||
392 | 16 | $newnodes[] = $newnode; |
|||||
393 | } |
||||||
394 | } |
||||||
395 | |||||||
396 | 16 | return self::create($newnodes); |
|||||
397 | } |
||||||
398 | |||||||
399 | /** |
||||||
400 | * Insert every element in the set of matched elements before the target. |
||||||
401 | * |
||||||
402 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $element |
||||||
403 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler A new Crawler object containing all elements appended to the target elements |
||||||
404 | * @api |
||||||
405 | */ |
||||||
406 | 16 | public function insertBefore($element) |
|||||
407 | { |
||||||
408 | 16 | $e = self::create($element); |
|||||
409 | 16 | $newnodes = []; |
|||||
410 | 16 | foreach ($e as $i => $node) { |
|||||
411 | /** @var \DOMNode $node */ |
||||||
412 | 16 | foreach ($this as $newnode) { |
|||||
413 | /** @var \DOMNode $newnode */ |
||||||
414 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
415 | 16 | if ($newnode !== $node) { |
|||||
416 | 16 | $node->parentNode->insertBefore($newnode, $node); |
|||||
417 | } |
||||||
418 | 16 | $newnodes[] = $newnode; |
|||||
419 | } |
||||||
420 | } |
||||||
421 | |||||||
422 | 16 | return self::create($newnodes); |
|||||
423 | } |
||||||
424 | |||||||
425 | /** |
||||||
426 | * Insert content, specified by the parameter, to the beginning of each element in the set of matched elements. |
||||||
427 | * |
||||||
428 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content HTML code fragment |
||||||
429 | * @return HtmlPageCrawler $this for chaining |
||||||
430 | * @api |
||||||
431 | */ |
||||||
432 | 16 | public function prepend($content) |
|||||
433 | { |
||||||
434 | 16 | $content = self::create($content); |
|||||
435 | 16 | $newnodes = []; |
|||||
436 | 16 | foreach ($this as $i => $node) { |
|||||
437 | 16 | $refnode = $node->firstChild; |
|||||
438 | /** @var \DOMNode $node */ |
||||||
439 | 16 | foreach ($content as $newnode) { |
|||||
440 | /** @var \DOMNode $newnode */ |
||||||
441 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
442 | 16 | if ($refnode === null) { |
|||||
443 | 8 | $node->appendChild($newnode); |
|||||
444 | 16 | } elseif ($refnode !== $newnode) { |
|||||
445 | 16 | $node->insertBefore($newnode, $refnode); |
|||||
446 | } |
||||||
447 | 16 | $newnodes[] = $newnode; |
|||||
448 | } |
||||||
449 | } |
||||||
450 | 16 | $content->clear(); |
|||||
451 | 16 | $content->add($newnodes); |
|||||
452 | |||||||
453 | 16 | return $this; |
|||||
454 | } |
||||||
455 | |||||||
456 | /** |
||||||
457 | * Insert every element in the set of matched elements to the beginning of the target. |
||||||
458 | * |
||||||
459 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $element |
||||||
460 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler A new Crawler object containing all elements prepended to the target elements |
||||||
461 | * @api |
||||||
462 | */ |
||||||
463 | 8 | public function prependTo($element) |
|||||
464 | { |
||||||
465 | 8 | $e = self::create($element); |
|||||
466 | 8 | $newnodes = []; |
|||||
467 | 8 | foreach ($e as $i => $node) { |
|||||
468 | 8 | $refnode = $node->firstChild; |
|||||
469 | /** @var \DOMNode $node */ |
||||||
470 | 8 | foreach ($this as $newnode) { |
|||||
471 | /** @var \DOMNode $newnode */ |
||||||
472 | 8 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
473 | 8 | if ($newnode !== $node) { |
|||||
474 | 8 | if ($refnode === null) { |
|||||
475 | 8 | $node->appendChild($newnode); |
|||||
476 | } else { |
||||||
477 | 8 | $node->insertBefore($newnode, $refnode); |
|||||
478 | } |
||||||
479 | } |
||||||
480 | 8 | $newnodes[] = $newnode; |
|||||
481 | } |
||||||
482 | } |
||||||
483 | |||||||
484 | 8 | return self::create($newnodes); |
|||||
485 | } |
||||||
486 | |||||||
487 | /** |
||||||
488 | * Remove the set of matched elements from the DOM. |
||||||
489 | * |
||||||
490 | * (as opposed to Crawler::clear() which detaches the nodes only from Crawler |
||||||
491 | * but leaves them in the DOM) |
||||||
492 | * |
||||||
493 | * @api |
||||||
494 | */ |
||||||
495 | 16 | public function remove() |
|||||
496 | { |
||||||
497 | 16 | foreach ($this as $node) { |
|||||
498 | /** |
||||||
499 | * @var \DOMNode $node |
||||||
500 | */ |
||||||
501 | 16 | if ($node->parentNode instanceof \DOMElement) { |
|||||
502 | 16 | $node->parentNode->removeChild($node); |
|||||
503 | } |
||||||
504 | } |
||||||
505 | 16 | $this->clear(); |
|||||
506 | 16 | } |
|||||
507 | |||||||
508 | /** |
||||||
509 | * Remove an attribute from each element in the set of matched elements. |
||||||
510 | * |
||||||
511 | * Alias for removeAttribute for compatibility with jQuery |
||||||
512 | * |
||||||
513 | * @param string $name |
||||||
514 | * @return HtmlPageCrawler |
||||||
515 | * @api |
||||||
516 | */ |
||||||
517 | 8 | public function removeAttr($name) |
|||||
518 | { |
||||||
519 | 8 | return $this->removeAttribute($name); |
|||||
520 | } |
||||||
521 | |||||||
522 | /** |
||||||
523 | * Remove an attribute from each element in the set of matched elements. |
||||||
524 | * |
||||||
525 | * @param string $name |
||||||
526 | * @return HtmlPageCrawler |
||||||
527 | */ |
||||||
528 | 8 | public function removeAttribute($name) |
|||||
529 | { |
||||||
530 | 8 | foreach ($this as $node) { |
|||||
531 | 8 | if ($node instanceof \DOMElement) { |
|||||
532 | /** @var \DOMElement $node */ |
||||||
533 | 8 | if ($node->hasAttribute($name)) { |
|||||
534 | 8 | $node->removeAttribute($name); |
|||||
535 | } |
||||||
536 | } |
||||||
537 | } |
||||||
538 | |||||||
539 | 8 | return $this; |
|||||
540 | } |
||||||
541 | |||||||
542 | /** |
||||||
543 | * Remove a class from each element in the list |
||||||
544 | * |
||||||
545 | * @param string $name |
||||||
546 | * @return HtmlPageCrawler $this for chaining |
||||||
547 | * @api |
||||||
548 | */ |
||||||
549 | 16 | public function removeClass($name) |
|||||
550 | { |
||||||
551 | 16 | foreach ($this as $node) { |
|||||
552 | 16 | if ($node instanceof \DOMElement) { |
|||||
553 | /** @var \DOMElement $node */ |
||||||
554 | 16 | $classes = preg_split('/\s+/s', $node->getAttribute('class')); |
|||||
555 | 16 | $count = count($classes); |
|||||
556 | 16 | for ($i = 0; $i < $count; $i++) { |
|||||
557 | 16 | if ($classes[$i] == $name) { |
|||||
558 | 16 | unset($classes[$i]); |
|||||
559 | } |
||||||
560 | } |
||||||
561 | 16 | $node->setAttribute('class', trim(join(' ', $classes))); |
|||||
562 | } |
||||||
563 | } |
||||||
564 | |||||||
565 | 16 | return $this; |
|||||
566 | } |
||||||
567 | |||||||
568 | /** |
||||||
569 | * Replace each target element with the set of matched elements. |
||||||
570 | * |
||||||
571 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $element |
||||||
572 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler A new Crawler object containing all elements appended to the target elements |
||||||
573 | * @api |
||||||
574 | */ |
||||||
575 | 16 | public function replaceAll($element) |
|||||
576 | { |
||||||
577 | 16 | $e = self::create($element); |
|||||
578 | 16 | $newnodes = []; |
|||||
579 | 16 | foreach ($e as $i => $node) { |
|||||
580 | /** @var \DOMNode $node */ |
||||||
581 | 16 | $parent = $node->parentNode; |
|||||
582 | 16 | $refnode = $node->nextSibling; |
|||||
583 | 16 | foreach ($this as $j => $newnode) { |
|||||
584 | /** @var \DOMNode $newnode */ |
||||||
585 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
586 | 16 | if ($j == 0) { |
|||||
587 | 16 | $parent->replaceChild($newnode, $node); |
|||||
0 ignored issues
–
show
The method
replaceChild() 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. ![]() |
|||||||
588 | } else { |
||||||
589 | 8 | $parent->insertBefore($newnode, $refnode); |
|||||
590 | } |
||||||
591 | 16 | $newnodes[] = $newnode; |
|||||
592 | } |
||||||
593 | } |
||||||
594 | |||||||
595 | 16 | return self::create($newnodes); |
|||||
596 | } |
||||||
597 | |||||||
598 | /** |
||||||
599 | * Replace each element in the set of matched elements with the provided new content and return the set of elements that was removed. |
||||||
600 | * |
||||||
601 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content |
||||||
602 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler $this for chaining |
||||||
603 | * @api |
||||||
604 | */ |
||||||
605 | 16 | public function replaceWith($content) |
|||||
606 | { |
||||||
607 | 16 | $content = self::create($content); |
|||||
608 | 16 | $newnodes = []; |
|||||
609 | 16 | foreach ($this as $i => $node) { |
|||||
610 | /** @var \DOMNode $node */ |
||||||
611 | 16 | $parent = $node->parentNode; |
|||||
612 | 16 | $refnode = $node->nextSibling; |
|||||
613 | 16 | foreach ($content as $j => $newnode) { |
|||||
614 | /** @var \DOMNode $newnode */ |
||||||
615 | 16 | $newnode = static::importNewnode($newnode, $node, $i); |
|||||
616 | 16 | if ($j == 0) { |
|||||
617 | 16 | $parent->replaceChild($newnode, $node); |
|||||
618 | } else { |
||||||
619 | 8 | $parent->insertBefore($newnode, $refnode); |
|||||
620 | } |
||||||
621 | 16 | $newnodes[] = $newnode; |
|||||
622 | } |
||||||
623 | } |
||||||
624 | 16 | $content->clear(); |
|||||
625 | 16 | $content->add($newnodes); |
|||||
626 | |||||||
627 | 16 | return $this; |
|||||
628 | } |
||||||
629 | |||||||
630 | /** |
||||||
631 | * Get the combined text contents of each element in the set of matched elements, including their descendants. |
||||||
632 | * This is what the jQuery text() function does, contrary to the Crawler::text() method that returns only |
||||||
633 | * the text of the first node. |
||||||
634 | * |
||||||
635 | * @return string |
||||||
636 | * @api |
||||||
637 | */ |
||||||
638 | 8 | public function getCombinedText() |
|||||
639 | { |
||||||
640 | 8 | $text = ''; |
|||||
641 | 8 | foreach ($this as $node) { |
|||||
642 | /** @var \DOMNode $node */ |
||||||
643 | 8 | $text .= $node->nodeValue; |
|||||
644 | } |
||||||
645 | |||||||
646 | 8 | return $text; |
|||||
647 | } |
||||||
648 | |||||||
649 | /** |
||||||
650 | * Set the text contents of the matched elements. |
||||||
651 | * |
||||||
652 | * @param string $text |
||||||
653 | * @return HtmlPageCrawler |
||||||
654 | * @api |
||||||
655 | */ |
||||||
656 | 16 | public function setText($text) |
|||||
657 | { |
||||||
658 | 16 | $text = htmlspecialchars($text); |
|||||
659 | 16 | foreach ($this as $node) { |
|||||
660 | /** @var \DOMNode $node */ |
||||||
661 | 16 | $node->nodeValue = $text; |
|||||
662 | } |
||||||
663 | |||||||
664 | 16 | return $this; |
|||||
665 | } |
||||||
666 | |||||||
667 | /** |
||||||
668 | * Add or remove one or more classes from each element in the set of matched elements, depending the class’s presence. |
||||||
669 | * |
||||||
670 | * @param string $classname One or more classnames separated by spaces |
||||||
671 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler $this for chaining |
||||||
672 | * @api |
||||||
673 | */ |
||||||
674 | 8 | public function toggleClass($classname) |
|||||
675 | { |
||||||
676 | 8 | $classes = explode(' ', $classname); |
|||||
677 | 8 | foreach ($this as $i => $node) { |
|||||
678 | 8 | $c = self::create($node); |
|||||
679 | /** @var \DOMNode $node */ |
||||||
680 | 8 | foreach ($classes as $class) { |
|||||
681 | 8 | if ($c->hasClass($class)) { |
|||||
682 | 8 | $c->removeClass($class); |
|||||
683 | } else { |
||||||
684 | 8 | $c->addClass($class); |
|||||
685 | } |
||||||
686 | } |
||||||
687 | } |
||||||
688 | |||||||
689 | 8 | return $this; |
|||||
690 | } |
||||||
691 | |||||||
692 | /** |
||||||
693 | * Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place. |
||||||
694 | * |
||||||
695 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler $this for chaining |
||||||
696 | * @api |
||||||
697 | */ |
||||||
698 | 8 | public function unwrap() |
|||||
699 | { |
||||||
700 | 8 | $parents = []; |
|||||
701 | 8 | foreach ($this as $i => $node) { |
|||||
702 | 8 | $parents[] = $node->parentNode; |
|||||
703 | } |
||||||
704 | |||||||
705 | 8 | self::create($parents)->unwrapInner(); |
|||||
706 | |||||||
707 | 8 | return $this; |
|||||
708 | } |
||||||
709 | |||||||
710 | /** |
||||||
711 | * Remove the matched elements, but promote the children to take their place. |
||||||
712 | * |
||||||
713 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler $this for chaining |
||||||
714 | * @api |
||||||
715 | */ |
||||||
716 | 16 | public function unwrapInner() |
|||||
717 | { |
||||||
718 | 16 | foreach ($this as $i => $node) { |
|||||
719 | 16 | if (!$node->parentNode instanceof \DOMElement) { |
|||||
720 | 8 | throw new \InvalidArgumentException('DOMElement does not have a parent DOMElement node.'); |
|||||
721 | } |
||||||
722 | |||||||
723 | /** @var \DOMNode[] $children */ |
||||||
724 | 16 | $children = iterator_to_array($node->childNodes); |
|||||
725 | 16 | foreach ($children as $child) { |
|||||
726 | 8 | $node->parentNode->insertBefore($child, $node); |
|||||
727 | } |
||||||
728 | |||||||
729 | 16 | $node->parentNode->removeChild($node); |
|||||
730 | } |
||||||
731 | 16 | } |
|||||
732 | |||||||
733 | |||||||
734 | /** |
||||||
735 | * Wrap an HTML structure around each element in the set of matched elements |
||||||
736 | * |
||||||
737 | * The HTML structure must contain only one root node, e.g.: |
||||||
738 | * Works: <div><div></div></div> |
||||||
739 | * Does not work: <div></div><div></div> |
||||||
740 | * |
||||||
741 | * @param string|HtmlPageCrawler|\DOMNode $wrappingElement |
||||||
742 | * @return HtmlPageCrawler $this for chaining |
||||||
743 | * @api |
||||||
744 | */ |
||||||
745 | 8 | public function wrap($wrappingElement) |
|||||
746 | { |
||||||
747 | 8 | $content = self::create($wrappingElement); |
|||||
748 | 8 | $newnodes = []; |
|||||
749 | 8 | foreach ($this as $i => $node) { |
|||||
750 | /** @var \DOMNode $node */ |
||||||
751 | 8 | $newnode = $content->getNode(0); |
|||||
752 | /** @var \DOMNode $newnode */ |
||||||
753 | // $newnode = static::importNewnode($newnode, $node, $i); |
||||||
754 | 8 | if ($newnode->ownerDocument !== $node->ownerDocument) { |
|||||
755 | 8 | $newnode = $node->ownerDocument->importNode($newnode, true); |
|||||
0 ignored issues
–
show
The method
importNode() 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. ![]() |
|||||||
756 | } else { |
||||||
757 | if ($i > 0) { |
||||||
758 | $newnode = $newnode->cloneNode(true); |
||||||
759 | } |
||||||
760 | } |
||||||
761 | 8 | $oldnode = $node->parentNode->replaceChild($newnode, $node); |
|||||
762 | 8 | while ($newnode->hasChildNodes()) { |
|||||
763 | 8 | $elementFound = false; |
|||||
764 | 8 | foreach ($newnode->childNodes as $child) { |
|||||
765 | 8 | if ($child instanceof \DOMElement) { |
|||||
766 | 8 | $newnode = $child; |
|||||
767 | 8 | $elementFound = true; |
|||||
768 | |||||||
769 | 8 | break; |
|||||
770 | } |
||||||
771 | } |
||||||
772 | 8 | if (!$elementFound) { |
|||||
773 | 8 | break; |
|||||
774 | } |
||||||
775 | } |
||||||
776 | 8 | $newnode->appendChild($oldnode); |
|||||
777 | 8 | $newnodes[] = $newnode; |
|||||
778 | } |
||||||
779 | 8 | $content->clear(); |
|||||
780 | 8 | $content->add($newnodes); |
|||||
781 | |||||||
782 | 8 | return $this; |
|||||
783 | } |
||||||
784 | |||||||
785 | /** |
||||||
786 | * Wrap an HTML structure around all elements in the set of matched elements. |
||||||
787 | * |
||||||
788 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content |
||||||
789 | * @throws \LogicException |
||||||
790 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler $this for chaining |
||||||
791 | * @api |
||||||
792 | */ |
||||||
793 | 8 | public function wrapAll($content) |
|||||
794 | { |
||||||
795 | 8 | $content = self::create($content); |
|||||
796 | 8 | $parent = $this->getNode(0)->parentNode; |
|||||
797 | 8 | foreach ($this as $i => $node) { |
|||||
798 | /** @var \DOMNode $node */ |
||||||
799 | 8 | if ($node->parentNode !== $parent) { |
|||||
800 | throw new \LogicException('Nodes to be wrapped with wrapAll() must all have the same parent'); |
||||||
801 | } |
||||||
802 | } |
||||||
803 | |||||||
804 | 8 | $newnode = $content->getNode(0); |
|||||
805 | /** @var \DOMNode $newnode */ |
||||||
806 | 8 | $newnode = static::importNewnode($newnode, $parent); |
|||||
807 | |||||||
808 | 8 | $newnode = $parent->insertBefore($newnode, $this->getNode(0)); |
|||||
809 | 8 | $content->clear(); |
|||||
810 | 8 | $content->add($newnode); |
|||||
811 | |||||||
812 | 8 | while ($newnode->hasChildNodes()) { |
|||||
813 | 8 | $elementFound = false; |
|||||
814 | 8 | foreach ($newnode->childNodes as $child) { |
|||||
815 | 8 | if ($child instanceof \DOMElement) { |
|||||
816 | 8 | $newnode = $child; |
|||||
817 | 8 | $elementFound = true; |
|||||
818 | |||||||
819 | 8 | break; |
|||||
820 | } |
||||||
821 | } |
||||||
822 | 8 | if (!$elementFound) { |
|||||
823 | break; |
||||||
824 | } |
||||||
825 | } |
||||||
826 | 8 | foreach ($this as $i => $node) { |
|||||
827 | /** @var \DOMNode $node */ |
||||||
828 | 8 | $newnode->appendChild($node); |
|||||
829 | } |
||||||
830 | |||||||
831 | 8 | return $this; |
|||||
832 | } |
||||||
833 | |||||||
834 | /** |
||||||
835 | * Wrap an HTML structure around the content of each element in the set of matched elements. |
||||||
836 | * |
||||||
837 | * @param string|HtmlPageCrawler|\DOMNode|\DOMNodeList $content |
||||||
838 | * @return \Wa72\HtmlPageDom\HtmlPageCrawler $this for chaining |
||||||
839 | * @api |
||||||
840 | */ |
||||||
841 | 8 | public function wrapInner($content) |
|||||
842 | { |
||||||
843 | 8 | foreach ($this as $i => $node) { |
|||||
844 | /** @var \DOMNode $node */ |
||||||
845 | 8 | self::create($node->childNodes)->wrapAll($content); |
|||||
846 | } |
||||||
847 | |||||||
848 | 8 | return $this; |
|||||
849 | } |
||||||
850 | |||||||
851 | /** |
||||||
852 | * Get the HTML code fragment of all elements and their contents. |
||||||
853 | * |
||||||
854 | * If the first node contains a complete HTML document return only |
||||||
855 | * the full code of this document. |
||||||
856 | * |
||||||
857 | * @return string HTML code (fragment) |
||||||
858 | * @api |
||||||
859 | */ |
||||||
860 | 64 | public function saveHTML() |
|||||
861 | { |
||||||
862 | 64 | if ($this->isHtmlDocument()) { |
|||||
863 | 8 | return $this->getDOMDocument()->saveHTML(); |
|||||
864 | } |
||||||
865 | 64 | $doc = new \DOMDocument('1.0', 'UTF-8'); |
|||||
866 | 64 | $root = $doc->appendChild($doc->createElement('_root')); |
|||||
867 | 64 | foreach ($this as $node) { |
|||||
868 | 64 | $root->appendChild($doc->importNode($node, true)); |
|||||
869 | } |
||||||
870 | 64 | $html = trim($doc->saveHTML()); |
|||||
871 | |||||||
872 | 64 | return preg_replace('@^<' . self::FRAGMENT_ROOT_TAGNAME . '[^>]*>|</' . self::FRAGMENT_ROOT_TAGNAME . '>$@', '', $html); |
|||||
873 | } |
||||||
874 | |||||||
875 | 32 | public function __toString() |
|||||
876 | { |
||||||
877 | 32 | return $this->saveHTML(); |
|||||
878 | } |
||||||
879 | |||||||
880 | /** |
||||||
881 | * checks whether the first node contains a complete html document |
||||||
882 | * (as opposed to a document fragment) |
||||||
883 | * |
||||||
884 | * @return boolean |
||||||
885 | */ |
||||||
886 | 64 | public function isHtmlDocument() |
|||||
887 | { |
||||||
888 | 64 | $node = $this->getNode(0); |
|||||
889 | 64 | if ($node instanceof \DOMElement |
|||||
890 | 64 | && $node->ownerDocument instanceof \DOMDocument |
|||||
891 | 64 | && $node->ownerDocument->documentElement === $node |
|||||
892 | 64 | && $node->nodeName == 'html' |
|||||
893 | ) { |
||||||
894 | 8 | return true; |
|||||
895 | } |
||||||
896 | |||||||
897 | 64 | return false; |
|||||
898 | } |
||||||
899 | |||||||
900 | /** |
||||||
901 | * get ownerDocument of the first element |
||||||
902 | * |
||||||
903 | * @return \DOMDocument|null |
||||||
904 | */ |
||||||
905 | 8 | public function getDOMDocument() |
|||||
906 | { |
||||||
907 | 8 | $node = $this->getNode(0); |
|||||
908 | 8 | $r = null; |
|||||
909 | 8 | if ($node instanceof \DOMElement |
|||||
910 | 8 | && $node->ownerDocument instanceof \DOMDocument |
|||||
911 | ) { |
||||||
912 | 8 | $r = $node->ownerDocument; |
|||||
913 | } |
||||||
914 | |||||||
915 | 8 | return $r; |
|||||
916 | } |
||||||
917 | |||||||
918 | /** |
||||||
919 | * Filters the list of nodes with a CSS selector. |
||||||
920 | * |
||||||
921 | * @param string $selector |
||||||
922 | * @return HtmlPageCrawler |
||||||
923 | */ |
||||||
924 | 56 | public function filter($selector) |
|||||
925 | { |
||||||
926 | 56 | return parent::filter($selector); |
|||||
927 | } |
||||||
928 | |||||||
929 | /** |
||||||
930 | * Filters the list of nodes with an XPath expression. |
||||||
931 | * |
||||||
932 | * @param string $xpath An XPath expression |
||||||
933 | * |
||||||
934 | * @return HtmlPageCrawler A new instance of Crawler with the filtered list of nodes |
||||||
935 | * |
||||||
936 | * @api |
||||||
937 | */ |
||||||
938 | 16 | public function filterXPath($xpath) |
|||||
939 | { |
||||||
940 | 16 | return parent::filterXPath($xpath); |
|||||
941 | } |
||||||
942 | |||||||
943 | /** |
||||||
944 | * Adds HTML/XML content to the HtmlPageCrawler object (but not to the DOM of an already attached node). |
||||||
945 | * |
||||||
946 | * Function overriden from Crawler because HTML fragments are always added as complete documents there |
||||||
947 | * |
||||||
948 | * |
||||||
949 | * @param string $content A string to parse as HTML/XML |
||||||
950 | * @param null|string $type The content type of the string |
||||||
951 | * |
||||||
952 | * @return null|void |
||||||
953 | */ |
||||||
954 | 136 | public function addContent($content, $type = null) |
|||||
955 | { |
||||||
956 | 136 | if (empty($type)) { |
|||||
957 | 136 | $type = 'text/html;charset=UTF-8'; |
|||||
958 | } |
||||||
959 | 136 | if (substr($type, 0, 9) == 'text/html' && !preg_match('/<html\b[^>]*>/i', $content)) { |
|||||
960 | // string contains no <html> Tag => no complete document but an HTML fragment! |
||||||
961 | 128 | $this->addHtmlFragment($content); |
|||||
962 | } else { |
||||||
963 | 16 | parent::addContent($content, $type); |
|||||
964 | } |
||||||
965 | 136 | } |
|||||
966 | |||||||
967 | 120 | public function addHtmlFragment($content, $charset = 'UTF-8') |
|||||
968 | { |
||||||
969 | 120 | $d = new \DOMDocument('1.0', $charset); |
|||||
970 | 120 | $d->preserveWhiteSpace = false; |
|||||
971 | 120 | $root = $d->appendChild($d->createElement(self::FRAGMENT_ROOT_TAGNAME)); |
|||||
972 | 120 | $bodynode = Helpers::getBodyNodeFromHtmlFragment($content, $charset); |
|||||
973 | 120 | foreach ($bodynode->childNodes as $child) { |
|||||
974 | 120 | $inode = $root->appendChild($d->importNode($child, true)); |
|||||
975 | 120 | if ($inode) { |
|||||
976 | 120 | $this->addNode($inode); |
|||||
977 | } |
||||||
978 | } |
||||||
979 | 120 | } |
|||||
980 | |||||||
981 | /** |
||||||
982 | * Adds a node to the current list of nodes. |
||||||
983 | * |
||||||
984 | * This method uses the appropriate specialized add*() method based |
||||||
985 | * on the type of the argument. |
||||||
986 | * |
||||||
987 | * Overwritten from parent to allow Crawler to be added |
||||||
988 | * |
||||||
989 | * @param null|\DOMNodeList|array|\DOMNode|Crawler $node A node |
||||||
990 | * |
||||||
991 | * @api |
||||||
992 | */ |
||||||
993 | 224 | public function add($node) |
|||||
994 | { |
||||||
995 | 224 | if ($node instanceof Crawler) { |
|||||
996 | 8 | foreach ($node as $childnode) { |
|||||
997 | 8 | $this->addNode($childnode); |
|||||
998 | } |
||||||
999 | } else { |
||||||
1000 | 224 | parent::add($node); |
|||||
1001 | } |
||||||
1002 | 224 | } |
|||||
1003 | |||||||
1004 | /** |
||||||
1005 | * @param \DOMNode $newnode |
||||||
1006 | * @param \DOMNode $referencenode |
||||||
1007 | * @param int $clone |
||||||
1008 | * @return \DOMNode |
||||||
1009 | */ |
||||||
1010 | 48 | protected static function importNewnode(\DOMNode $newnode, \DOMNode $referencenode, $clone = 0) |
|||||
1011 | { |
||||||
1012 | 48 | if ($newnode->ownerDocument !== $referencenode->ownerDocument) { |
|||||
1013 | 40 | $referencenode->ownerDocument->preserveWhiteSpace = false; |
|||||
1014 | 40 | $newnode = $referencenode->ownerDocument->importNode($newnode, true); |
|||||
0 ignored issues
–
show
The method
importNode() 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. ![]() |
|||||||
1015 | } else { |
||||||
1016 | 16 | if ($clone > 0) { |
|||||
1017 | $newnode = $newnode->cloneNode(true); |
||||||
1018 | } |
||||||
1019 | } |
||||||
1020 | |||||||
1021 | 48 | return $newnode; |
|||||
1022 | } |
||||||
1023 | |||||||
1024 | // /** |
||||||
1025 | // * Checks whether the first node in the set is disconnected (has no parent node) |
||||||
1026 | // * |
||||||
1027 | // * @return bool |
||||||
1028 | // */ |
||||||
1029 | // public function isDisconnected() |
||||||
1030 | // { |
||||||
1031 | // $parent = $this->getNode(0)->parentNode; |
||||||
1032 | // return ($parent == null || $parent->tagName == self::FRAGMENT_ROOT_TAGNAME); |
||||||
1033 | // } |
||||||
1034 | |||||||
1035 | 8 | public function __get($name) |
|||||
1036 | { |
||||||
1037 | 8 | switch ($name) { |
|||||
1038 | 8 | case 'count': |
|||||
1039 | 8 | case 'length': |
|||||
1040 | 8 | return count($this); |
|||||
1041 | } |
||||||
1042 | |||||||
1043 | 8 | throw new \Exception('No such property ' . $name); |
|||||
1044 | } |
||||||
1045 | } |
||||||
1046 |
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.