这有点棘手,但你可以这样做:
$html = <<< HTML
<div><p>The CEO of the Dexia bank <em>has</em> just decided to retire.</p></div>
HTML;
我添加了一个强调元素,只是为了说明它也适用于行内元素。
设置
$dom = new DOMDocument;
$dom->formatOutput = TRUE;
$dom->loadXML($html);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//text()[contains(., "Dexia")]');
上面有趣的事情当然是XPath。它查询已加载的DOM中所有包含“Dexia”关键字的DOMText
节点,结果通常是一个DOMNodeList
。
更换内容
foreach($nodes as $node) {
$link = '<a href="info.php?tag=dexia">Dexia</a>';
$replaced = str_replace('Dexia', $link, $node->wholeText);
$newNode = $dom->createDocumentFragment();
$newNode->appendXML($replaced);
$node->parentNode->replaceChild($newNode, $node);
}
echo $dom->saveXML($dom->documentElement);
找到的$node
将包含字符串德克萨斯银行的CEO的wholeText
,尽管它在P
元素中。这是因为$node
有一个强调标记银行后面的兄弟DOMElement
。我将链接创建为字符串而不是节点,并用它替换wholeText
中所有出现的“Dexia”(不考虑单词边界-这对于正则表达式来说是个好选择)。然后,我从结果字符串创建一个DocumentFragment
并用它替换DOMText
节点。
W3C vs PHP
使用DocumentFragement::applyXML()
是非标准的方法,因为该方法不是W3C DOM规范的一部分。
如果您想使用标准的API进行替换,您需要首先将A
元素创建为新的DOMElement
。然后,您需要查找DOMText
的nodeValue
中“Dexia”的偏移量,并在该位置将DOMText
节点拆分为两个节点。从返回的兄弟节点中删除Dexia并在第二个节点之前插入链接元素。重复此过程,直到在节点中找不到更多的Dexia字符串。以下是如何处理一个Dexia出现的方法:
foreach($nodes as $node) {
$link = $dom->createElement('a', 'Dexia');
$link->setAttribute('href', 'info.php?tag=dexia');
$offset = strpos($node->nodeValue, 'Dexia');
$newNode = $node->splitText($offset);
$newNode->deleteData(0, strlen('Dexia'));
$node->parentNode->insertBefore($link, $newNode);
}
最后的输出
<div>
<p>The CEO of the <a href="info.php?tag=dexia">Dexia</a> bank <em>has</em> just decided to retire.</p>
</div>