That's somewhat tricky, but you could do it this way:
$html = <<< HTML
<div><p>The CEO of the Dexia bank <em>has</em> just decided to retire.</p></div>
HTML;
I've added an emphasis element just to illustrate that it works with inline elements too.
Setup
$dom = new DOMDocument;
$dom->formatOutput = TRUE;
$dom->loadXML($html);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//text()[contains(., "Dexia")]');
The interesting thing above is the XPath of course. It queries the loaded DOM for all DOMText
nodes containing the needle "Dexia". The result is DOMNodeList
(as usual).
The replacement
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);
The found $node
will contain the string The CEO of the Dexia bank for wholeText
, despite it being inside the P
element. That is because the $node
has a sibling DOMElement
with the emphasis after bank. I am creating the link as a string instead of a node and replace all occurences of "Dexia" (regardless of word boundary - that would be a good call for Regex) in the wholeText
with it. Then I create a DocumentFragment
from the resulting string and replace the DOMText
node with it.
W3C vs PHP
Using DocumentFragement::applyXML()
is a non-standard approach, because the method is not part of the W3C DOM Specs.
If you would want to do the replacement with the standard API, you'd first have to create the A
Element as a new DOMElement
. Then you'd have to find the offset of "Dexia" in the nodeValue
of the DOMText
and split the DOMText
Node into two nodes at that position. Remove Dexia from the returned sibling and insert the Link Element, before the second one. Repeat this procedure with the sibling node until no more Dexia strings are found in the node. Here is how to do it for one occurence of 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);
}
And finally the output
<div>
<p>The CEO of the <a href="info.php?tag=dexia">Dexia</a> bank <em>has</em> just decided to retire.</p>
</div>