|
| 1 | +--TEST-- |
| 2 | +Dom\Element::getElementsByClassName() item() random access (cold and backwards) |
| 3 | +--EXTENSIONS-- |
| 4 | +dom |
| 5 | +--FILE-- |
| 6 | +<?php |
| 7 | + |
| 8 | +/* Regression: the item() lookup must return the n-th match for random access, |
| 9 | + * not only for a strictly ascending / foreach walk. A fresh collection whose |
| 10 | + * first access is item(n) exercises the uncached path, and a decreasing index |
| 11 | + * on the same collection exercises the cache-discard path. Non-matching and |
| 12 | + * nested elements verify that iteration advances to the next matching element |
| 13 | + * in tree order, not merely to the next sibling. */ |
| 14 | + |
| 15 | +$dom = Dom\HTMLDocument::createFromString(<<<HTML |
| 16 | +<!DOCTYPE html> |
| 17 | +<body> |
| 18 | + <span class="x" id="E0"></span> |
| 19 | + <div class="y" id="skip1"><span class="x" id="E1"></span></div> |
| 20 | + <span class="x" id="E2"></span> |
| 21 | + <p class="z"><b class="x" id="E3"></b></p> |
| 22 | + <span class="x" id="E4"></span> |
| 23 | +</body> |
| 24 | +HTML); |
| 25 | + |
| 26 | +$body = $dom->getElementsByTagName('body')->item(0); |
| 27 | + |
| 28 | +function id(?Dom\Element $e): string { |
| 29 | + return $e === null ? 'NULL' : $e->id; |
| 30 | +} |
| 31 | + |
| 32 | +echo "-- cold random access (fresh collection per call) --\n"; |
| 33 | +foreach ([0, 1, 2, 3, 4, 5] as $i) { |
| 34 | + $collection = $body->getElementsByClassName('x'); |
| 35 | + echo "item($i) = ", id($collection->item($i)), "\n"; |
| 36 | +} |
| 37 | + |
| 38 | +echo "-- backwards seek on one collection --\n"; |
| 39 | +$collection = $body->getElementsByClassName('x'); |
| 40 | +foreach ([4, 2, 0, 3, 1] as $i) { |
| 41 | + echo "item($i) = ", id($collection->item($i)), "\n"; |
| 42 | +} |
| 43 | + |
| 44 | +echo "-- item() seed then foreach --\n"; |
| 45 | +$collection = $body->getElementsByClassName('x'); |
| 46 | +$collection->item(3); |
| 47 | +$ids = []; |
| 48 | +foreach ($collection as $node) { |
| 49 | + $ids[] = $node->id; |
| 50 | +} |
| 51 | +echo implode(" ", $ids), "\n"; |
| 52 | + |
| 53 | +echo "-- last-element idiom --\n"; |
| 54 | +$collection = $body->getElementsByClassName('x'); |
| 55 | +echo "length = ", $collection->length, ", last = ", id($collection->item($collection->length - 1)), "\n"; |
| 56 | + |
| 57 | +echo "-- live collection after mutation --\n"; |
| 58 | +$collection = $body->getElementsByClassName('x'); |
| 59 | +echo "item(1) = ", id($collection->item(1)), "\n"; |
| 60 | +$dom->getElementById('E1')->remove(); |
| 61 | +echo "item(1) = ", id($collection->item(1)), ", length = ", $collection->length, "\n"; |
| 62 | + |
| 63 | +?> |
| 64 | +--EXPECT-- |
| 65 | +-- cold random access (fresh collection per call) -- |
| 66 | +item(0) = E0 |
| 67 | +item(1) = E1 |
| 68 | +item(2) = E2 |
| 69 | +item(3) = E3 |
| 70 | +item(4) = E4 |
| 71 | +item(5) = NULL |
| 72 | +-- backwards seek on one collection -- |
| 73 | +item(4) = E4 |
| 74 | +item(2) = E2 |
| 75 | +item(0) = E0 |
| 76 | +item(3) = E3 |
| 77 | +item(1) = E1 |
| 78 | +-- item() seed then foreach -- |
| 79 | +E0 E1 E2 E3 E4 |
| 80 | +-- last-element idiom -- |
| 81 | +length = 5, last = E4 |
| 82 | +-- live collection after mutation -- |
| 83 | +item(1) = E1 |
| 84 | +item(1) = E2, length = 4 |
0 commit comments