1--TEST-- 2GH-13863 (Removal during NodeList iteration breaks loop) 3--EXTENSIONS-- 4dom 5--FILE-- 6<?php 7 8enum DomType { 9 case Legacy; 10 case Modern; 11} 12 13function createTestDoc(DomType $type) { 14 $xml = <<<EOXML 15 <x> 16 <item1 /> 17 <item2 /> 18 <item3 /> 19 <item4 /> 20 <item5 /> 21 </x> 22 EOXML; 23 24 if ($type === DomType::Legacy) { 25 $doc = new DOMDocument(); 26 $doc->preserveWhiteSpace = false; 27 $doc->loadXML($xml); 28 } else { 29 $doc = Dom\XMLDocument::createFromString($xml, LIBXML_NOBLANKS); 30 } 31 32 return $doc; 33} 34 35foreach ([DomType::Legacy, DomType::Modern] as $case) { 36 $name = match ($case) { 37 DomType::Legacy => 'Legacy', 38 DomType::Modern => 'Modern', 39 }; 40 41 echo "--- $name test remove index 2 at index 2 ---\n"; 42 43 $doc = createTestDoc($case); 44 45 foreach ($doc->documentElement->childNodes as $i => $node) { 46 var_dump($doc->documentElement->childNodes->item($i) === $node); 47 var_dump($i, $node->localName); 48 if ($i === 2) { 49 $node->remove(); 50 } 51 } 52 53 echo $doc->saveXML(), "\n"; 54 55 echo "--- $name test remove index 1 at index 2 ---\n"; 56 57 $doc = createTestDoc($case); 58 59 foreach ($doc->documentElement->childNodes as $i => $node) { 60 var_dump($doc->documentElement->childNodes->item($i) === $node); 61 var_dump($i, $node->localName); 62 if ($i === 2) { 63 $doc->documentElement->childNodes[1]->remove(); 64 } 65 } 66 67 echo $doc->saveXML(), "\n"; 68 69 echo "--- $name test remove all ---\n"; 70 71 $doc = createTestDoc($case); 72 73 foreach ($doc->documentElement->childNodes as $i => $node) { 74 var_dump($doc->documentElement->childNodes->item($i) === $node); 75 var_dump($i, $node->localName); 76 $node->remove(); 77 } 78 79 echo $doc->saveXML(), "\n"; 80} 81 82?> 83--EXPECT-- 84--- Legacy test remove index 2 at index 2 --- 85bool(true) 86int(0) 87string(5) "item1" 88bool(true) 89int(1) 90string(5) "item2" 91bool(true) 92int(2) 93string(5) "item3" 94<?xml version="1.0"?> 95<x><item1/><item2/><item4/><item5/></x> 96 97--- Legacy test remove index 1 at index 2 --- 98bool(true) 99int(0) 100string(5) "item1" 101bool(true) 102int(1) 103string(5) "item2" 104bool(true) 105int(2) 106string(5) "item3" 107bool(false) 108int(3) 109string(5) "item4" 110bool(false) 111int(4) 112string(5) "item5" 113<?xml version="1.0"?> 114<x><item1/><item3/><item4/><item5/></x> 115 116--- Legacy test remove all --- 117bool(true) 118int(0) 119string(5) "item1" 120<?xml version="1.0"?> 121<x><item2/><item3/><item4/><item5/></x> 122 123--- Modern test remove index 2 at index 2 --- 124bool(true) 125int(0) 126string(5) "item1" 127bool(true) 128int(1) 129string(5) "item2" 130bool(true) 131int(2) 132string(5) "item3" 133bool(true) 134int(3) 135string(5) "item5" 136<?xml version="1.0" encoding="UTF-8"?> 137<x><item1/><item2/><item4/><item5/></x> 138--- Modern test remove index 1 at index 2 --- 139bool(true) 140int(0) 141string(5) "item1" 142bool(true) 143int(1) 144string(5) "item2" 145bool(true) 146int(2) 147string(5) "item3" 148bool(true) 149int(3) 150string(5) "item5" 151<?xml version="1.0" encoding="UTF-8"?> 152<x><item1/><item3/><item4/><item5/></x> 153--- Modern test remove all --- 154bool(true) 155int(0) 156string(5) "item1" 157bool(true) 158int(1) 159string(5) "item3" 160bool(true) 161int(2) 162string(5) "item5" 163<?xml version="1.0" encoding="UTF-8"?> 164<x><item2/><item4/></x> 165