SimpleXML плохо работает со смешанными дочерними узлами, но в DOM это не сложно, просто немного многословно.Имейте в виду, что в DOM все является узлом, а не только элементами.Итак, вы пытаетесь заменить один текстовый узел тремя новыми узлами - текстовым узлом, новым элементом элемента и другим текстовым узлом.
$xmlns = [
'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
];
$xml = <<<'XML'
<text:p
text:style-name="P1"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">
the quick brown fox jumps over the lazy dog
</text:p>
XML;
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
$searchFor = 'fox jumps over';
// iterate over text nodes containing the search string
$expression = '//text:p//text()[contains(., "'.$searchFor.'")]';
foreach ($xpath->evaluate($expression) as $textNode) {
// split the text content at the search string and capture any part
$parts = preg_split(
'(('.preg_quote($searchFor).'))',
$textNode->textContent,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
// here should be at least two parts
if (count($parts) < 2) {
continue;
}
// fragments allow to treat several nodes like one
$fragment = $document->createDocumentFragment();
foreach ($parts as $part) {
// matched the text
if ($part === $searchFor) {
// create the new span
$fragment->appendChild(
$span = $document->createElementNS($xmlns['text'], 'text:span')
);
$span->setAttributeNS($xmlns['text'], 'text:style-name', 'T1');
$span->appendChild($document->createTextNode($part));
} else {
// add the part as a new text node
$fragment->appendChild($document->createTextNode($part));
}
}
// replace the text node with the fragment
$textNode->parentNode->replaceChild($fragment, $textNode);
}
echo $document->saveXML();