PHP Conference Japan 2024

DOMNode::replaceChild

(PHP 5, PHP 7, PHP 8)

DOMNode::replaceChild替换子节点

描述

public DOMNode::replaceChild(DOMNode $node, DOMNode $child): DOMNode|false

此函数用传入的新节点替换子节点 child。如果 node 已经是子节点,则不会再次添加。如果替换成功,则返回旧节点。

参数

node

新节点。它必须是目标文档的成员,即由 DOMDocument->createXXX() 方法之一创建或由 DOMDocument::importNode 导入到文档中。

child

旧节点。

返回值

旧节点或false(如果发生错误)。

错误/异常

DOM_NO_MODIFICATION_ALLOWED_ERR

如果此节点为只读或正在插入的节点的上一个父节点为只读,则引发此异常。

DOM_HIERARCHY_REQUEST_ERR

如果此节点的类型不允许 node 节点类型的子节点,或者要放入的节点是此节点的祖先之一或此节点本身,则引发此异常。

DOM_WRONG_DOCUMENT_ERR

如果 node 是从与创建此节点的文档不同的文档创建的,则引发此异常。

DOM_NOT_FOUND_ERR

如果 child 不是此节点的子节点,则引发此异常。

参见

添加注释

用户贡献的注释 4 条注释

jb at jbpros dot com
19 年前
如果您尝试一次替换多个节点,则必须小心迭代 DOMNodeList。如果旧节点的名称与新节点不同,则替换后它将从列表中删除。使用逆向循环

<?php

$xml
= new DOMDocument;
$xml->load('docfile.xml');

$elements = $xml->getElementsByTagNameNS('http://www.example.com/NS/', '*');
$i = $elements->length - 1;
while (
$i > -1) {
$element = $elements->item($i);
$ignore = false;

$newelement = $xml>createTextNode('Some new node!');
$element->parentNode->replaceChild($newelement, $element);
$i--;
}

?>

循环计数器 ($i) 将始终位于列表的区间内,因为已删除元素的索引高于计数器。
aidan at php dot net
19 年前
这是一个替换节点的简单示例

让我们这样定义我们的 XML

<?php
$xml
= <<<XML
<?xml version="1.0"?>
<root>
<parent>
<child>bar</child>
<child>foo</child>
</parent>
</root>
XML;
?>

如果我们想替换整个 <parent> 节点,我们可以这样做

<?php
// 创建一个新的文档片段来保存新的 <parent> 节点
$parent = new DomDocument;
$parent_node = $parent ->createElement('parent');

// 添加一些子节点
$parent_node->appendChild($parent->createElement('child', 'somevalue'));
$parent_node->appendChild($parent->createElement('child', 'anothervalue'));

// 将 keywordset 添加到新文档中
// $parent 变量现在将新节点作为文档片段保存
$parent->appendChild($parent_node);
?>

接下来,我们需要找到旧节点

<?php
// 加载 XML
$dom = new DomDocument;
$dom->loadXML($xml);

// 定位旧的父节点
$xpath = new DOMXpath($dom);
$nodelist = $xpath->query('/root/parent');
$oldnode = $nodelist->item(0);
?>

然后我们导入并替换新节点

<?php
// 将 $parent 文档片段加载到当前文档中
$newnode = $dom->importNode($parent->documentElement, true);

// 替换
$oldnode->parentNode->replaceChild($newnode, $oldnode);

// 显示
echo $dom->saveXML();
?>

我们的新节点已成功导入

<?xml version="1.0"?>
<root>
<parent><child>somevalue</child><child>anothervalue</child></parent>
</root>
franp at free dot fr
20年前
1) 如果您的 XPath 查询返回的 NodeList 包含唯一项,或者您确定返回项的顺序,则可以使用“item(n)”语法代替“foreach”语法。
这可以大大提高代码的可读性。
正如方法名所示,replaceChild 无法替换节点本身,而只能替换节点的子节点。
技巧在于对 XPath 查询结果的父节点使用 replaceChild。

<?xml version="1.0" ?>
<action>
<actionGlobal>
<actionGlobalFR>sometext</actionGlobalFR>
<actionGlobal>
</action>

$frag = $doc->createElement("actionGlobalFR");
$fragA = $doc->createTextNode("anothertext");
$frag->appendChild($fragA);

$xpResult = $xp->query("/action/actionGlobal/actionGlobalFR");
$blipblip = $xpResult->item(0)->parentNode->replaceChild($ajout, $xpResult->item(0));

2) 同样,正如方法名所示,replaceChild 无法替换节点本身,而只能替换节点的子节点。
但是,仍然可以替换 XPath 指向的节点,而不是其子节点。
技巧在于对 XPath 查询结果的父节点使用 replaceChild。

<?xml version="1.0" ?>
<action>
<FR>French Text</FR>
</action>

<?php
$frag
= $doc->createElement("EN");
$fragA = $doc->createTextNode("English Text");
$frag->appendChild($fragA);

$xpResult = $xp->query("/action/FR");
$blipblip = $xpResult->item(0)->parentNode->replaceChild($fragA, $xpResult->item(0));
?>

好了!

这将生成
<?xml version="1.0" ?>
<action>
<EN>English Text</EN>
</action>

....................................................

3) 此外,请注意,您无法替换不存在的节点。
虽然这看起来很明显,但很容易忘记。

考虑一下
<?xml version="1.0" ?>
<action>
<EN></EN>
</action>

您无法使用 replaceChild() 将其转换为
<?xml version="1.0" ?>
<action>
<EN>Some text</EN>
</action>

原因是,由于<EN></EN>元素为空,因此它没有子节点(如果您认为<EN></EN>可以写成<EN />,则更容易理解这一点)。
您打算在<EN>和</EN>之间添加一些文本这一事实并没有改变它目前还没有文本,因此还没有子节点的事实。
处理 DOM 时,不要把您的想法当成现实。DOM 解析器并不关心您的想法。如果一个元素当前为空,无论您打算填充什么,它都没有子节点。

因此,解决这个问题的方法是使用 appendChild 而不是 replaceChild

<?php
$fragA
= $doc->createTextNode("Some Text");
$xpResult = $xp->query("/action/EN");
$blipblip = $xpResult->item(0)->appendChild($fragA);
?>

这将生成预期的结果
<?xml version="1.0" ?>
<action>
<EN>Some Text</EN>
</action>

....................................................

4) 请注意,文档中 replaceChild 的描述是错误的。参数被颠倒了。
正确的描述是
object DOMNode->replaceChild (object newnode, object oldnode)
chealer at gmail dot com
6年前
截至 5.6 版本,PHP 的行为仍然如 jb at jbpros dot com 所报道的那样。要在循环中使用 replaceChild(),可以使用以下示例中使用的更标准的模式

for ($currentNode = 0; $currentNode < $link->childNodes->length; $currentNode++) {
$child = $link->childNodes[$currentNode];

// “移除”链接,因为链接不能包含链接
if ($child instanceof DOMElement && $child->tagName == 'a') {
$replacement = $dom->createTextNode($child->textContent);
$link->replaceChild($replacement, $child);
}
}
To Top