DomDocument::DOMNodeList, DomDocument::removeChild или почему так не работает?

3м Буду!

Новичок
DomDocument::DOMNodeList, DomDocument::removeChild или почему так не работает?

Привет.
есть:
1. xml
PHP:
....
<root>
  <items>
    <item>item 1</item>
    .............
    <itemN>item N</itemN>
  </items>
  <someOtherNode1>
    ...............
  </someOtherNode1>
  .........................
  <someOtherNodeN>
    ...............
  </someOtherNodeN>

  <items>
    <item>item 1</item>
    .............
    <itemN>item N</itemN>
  </items>
</root>
2. Пых-код удаления всех нод с именем items, который так работает:
PHP:
  $node_list = $dom->getElementsByTagName('items');
  $childs = array();
  foreach ($node_list as $node) {
    $childs[] = $node;
  }
  
  foreach ($childs as $child) {
    $parent = $child->parentNode;
    $parent->removeChild($child);
  }
а так нет:
PHP:
  $node_list = $dom->getElementsByTagName('items');
  
  foreach ($node_list as $child) {
    $parent = $child->parentNode;
    $parent->removeChild($child);
  }
вопрос: почему последний экзампл не работает с колекцией DOMNodeList?

ведь конструкция $parent->removeChild($child); удаляет из дерева объекта $dom, но не из этой коллекции, и по идее, итератору башню сносить не должно.

кроме того, если переписать последний пример так:
PHP:
  $node_list = $dom->getElementsByTagName('items');
  $items = $node_list->length;
  for ($i = 0; $i < $items; $i++) {
    $child = $node_list->item($i);
    $parent = $child->parentNode;
    
    $parent->removeChild($child);
  }
то есть без всяких итераторов, заданные ноды, из результирующего дерева, удалены не будут.

в общем: как осуществить удаление заданных нод используя ИСКЛЮЧИТЕЛЬНО коллекцию DOMNodeList, без создания временных массивов.

Большое спасибо
 

slach

Новичок
а с чего ты взял что итератору не должно сносить башню???

объекты в PHP это ссылки
вполне возможно что внутри оно ссылается на один и тот же ресурс от libxml2
потому и сносит

натыкай
var_dump($node_list)
var_dump($child)
 

3м Буду!

Новичок
проблему решил, slach, спасибо за коменты.
В общем, если кому интересно, фишка в том, что дерево DomDocument перестраивается в памяти моментально, после любого изменения, и поскольку в DOMNodeList у нас лежат ссылки, то и итератор перестраивается автоматом.
Решение, которое работает, например у меня выглядит так:

PHP:
//$ node_list - объект класса DOMNodeList
$items_count = $node_list->length;

for ($i = 0; $i < $items_count; $i++) {
  $current = $node_list->item(0);   // суть именно в индексе - 0
  $parent = $current->parentNode;

  $parent->removeChild($current);
}
 

tashkentchi

Новичок
PHP:
while ($node_list->length) {
    $current = $node_list->item(0);
    $parent = $current->parentNode;
    $parent->removeChild($current);
}
 

3м Буду!

Новичок
приветствую! мерси всем принявшим участие в дискуссии... колективный разум родил ещё один канонический сниппет удаления нод;)
Господа, заметил одну странную особенность, заключающуюся в непонятных интерналс-особенностях формирования коллекции DOMNodeList, различными методами, поясню на примерах:
PHP:
//файл xpathtest.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
<elem11>
	<elemToDelete>I'am the first ElemToDelete, child of elem11</elemToDelete>
	<elem111>Elem111</elem111>
	<elemToDelete>I'am the second ElemToDelete, child of elem11</elemToDelete>
	<elemToDelete>I'am the third ElemToDelete, child of elem11</elemToDelete>
	<elem112>Elem112</elem112>
</elem11>
<elem12>
	<elem121>Elem121</elem121>
	<elemToDelete>I'am the first ElemToDelete, child of elem12</elemToDelete>
	<elem122>Elem122</elem122>
	<elemToDelete>I'am the second ElemToDelete, child of elem12</elemToDelete>
</elem12>
</root>
скрипт:
PHP:
function removeNodes(DOMNodeList $nodes) {
	$node_count = $nodes->length;
	while ($nodes->length) {
		$child = $nodes->item(0);
		$parent = $child->parentNode;
	
		print "Current Node: [{$child->nodeName}],\n its value: [{$child->nodeValue}] its parent: [{$parent->nodeName}]...".
			"node type: [".get_class($child)."], parent class: [".get_class($parent)."]\n";
		$parent->removeChild($child);
		print " REMOVED!!!\n\n";
	}

	return $node_count;
}

function removeNodes2(DOMNodeList $nodes) {
	$node_count = $nodes->length;
	//while ($nodes->length) {
	for ($i = 0; $i < $node_count; $i++) {	
		$child = $nodes->item($i);
		$parent = $child->parentNode;

		print "Current Node: [{$child->nodeName}],\n its value: [{$child->nodeValue}] its parent: [{$parent->nodeName}]...".
			"node type: [".get_class($child)."], parent class: [".get_class($parent)."]\n";
		$parent->removeChild($child);
		print " REMOVED!!!\n\n";
	}

	return $node_count;

/////////////// MAIN CODE ENTRY
$dom = new DOMDocument('1.0');
$dom->load('xpathtest.xml');

$xpath = new DOMXPath($dom);
$node_set = $xpath->evaluate('//elemToDelete');
$nodes_removed = removeNodes($node_set);
}
собственно, в чём суть: если колекция DOMNodeList была получена через метод getElementsByTagName , то как и следовало ожидать , совершенно корректно отрабатывает ф-ция removeNodes , однако, если же колекция DOMNodeList была получена в результате выполнения выражения XPath (как в примере), то работает ТОЛЬКО removeNodes2, что как-бы совершенно неправильно (дерево DOM должно перестраиваться моментально, после удаления НОД).
То есть, есть объект коллекции DOMNodeList, элементы которой - сылки на реальные ноды в дереве DOM, ОДНАКО, различные способы получения этой колекции getElementsByTagName/XPath, возвращают разные наборы нод,- в первом случае, это действительно ссылки, во втором же, элементы колекции DOMNodeList ими не являются.

Вопрос: почему это происходит, как можно сделать, что бы была одна ф-ция для удаления из дерева DOM, ноды, имеющиеся в колекции DOMNodeList, без особенностей к способу получения членов этой колекции (getElementsByTagName/XPath)

В общем, кто прояснит ситуацию, буду весьма признателен ;)

-~{}~ 16.03.09 14:35:

зы: что бы не "раздувать" пост, я не прикрепил отладочную инфу... ну думаю это излишне, и так всё понятно, если коротко, то суть вот в чём:
при удалении ноды из колекции полученной через выражение XPath, способом:
PHP:
while ($nodes->length) {
        $child = $nodes->item(0);
удалённая нода так и останется на своём месте с индексом 0, однако, после удаления (то есть после инструкции $parent->removeChild($child); ), её парент будет null.
То есть, колекция $nodes не меняется в процессе удаления (если она, колекция, получене выражением XPath)

в случае использования getElementsByTagName, с точностью на оборот (то есть, как и предполагается, согласно мануалу)...
 

slach

Новичок
грамотно заданный вопрос содержит половину ответа =)
скорее всего коллекция которая получается из XPath поскольку она может содержать НЕ ТОЛЬКО NodeList а возвращает вообще что угодно (фрагмент текста например), соответсвенно наверняка получается КОПИРОВАНИЕ
 
Сверху