
在使用php的domdocument和domxpath处理html内容,尤其是需要在文本节点中查找特定短语并用<span>标签包裹它们时,开发者常会遇到一个棘手的问题。当对一个文本节点进行首次修改(例如使用splittext()方法)后,该节点的结构会发生变化。如果后续的修改仍然基于原始的偏移量,则会导致错误,常见的如fatal error: uncaught error: call to a member function splittext() on bool,因为splittext()可能返回false,表示无法在指定位置分割文本。
问题的核心在于DOMText::splitText(int $offset)方法。它将一个文本节点分割成两个,$offset之前的文本保留在原节点中,$offset及之后的文本则创建为一个新的DOMText节点并返回。一旦这个操作完成,原始文本节点的长度和内容都已改变,任何基于其原始状态计算出的后续偏移量都将失效。
解决此问题的关键在于两点:
以下是结合了上述解决方案的PHP函数示例:
<?php
/**
* 自动将特定短语包裹在具有指定class的<span>标签中,用于品牌目的。
*
* @param string $content 待处理的HTML内容。
* @return string 处理后的HTML内容。
*/
function ccjm_branding_filter(string $content): string {
// 仅在非管理后台且非AJAX请求时处理,且内容不为空
if (! (is_admin() && ! wp_doing_ajax()) && $content) {
$DOM = new DOMDocument();
// 使用内部错误处理来避免HTML5警告
libxml_use_internal_errors(true);
// 加载内容,确保正确编码并添加HTML包装以供解析
// LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 用于避免添加不必要的<html><body>标签
$DOM->loadHTML("<?xml encoding='utf-8' ?><html>{$content}</html>", LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// 清除加载HTML时可能产生的错误
libxml_clear_errors();
// 初始化XPath
$XPath = new DOMXPath($DOM);
// 检索所有文本节点,排除<script>标签内的文本
$textNodes = $XPath->query("//text()[not(parent::script)]");
foreach ($textNodes as $node) {
// 查找所有匹配项,包括偏移量
// 正则表达式匹配 'C.C. Johnson & Malhotra, P.C.' 或 'CCJM' 的各种形式
preg_match_all("/(C\.? ?C\.?(?:JM| Johnson (?:&|&|&|and) Malhotra)(?: Engineers, LTD\.?|, P\.?C\.?)?)/i", $node->textContent, $matches, PREG_OFFSET_CAPTURE);
// 检查是否有匹配项
if (empty($matches[0])) {
continue; // 没有匹配项,跳过当前节点
}
/**
* 关键步骤:倒序处理匹配项。
* array_reverse($matches[0]) 确保我们从文本末尾的匹配项开始处理。
* 这样,每次DOM修改都不会影响到尚未处理的、位于当前匹配项之前的(在原始文本中)匹配项的偏移量。
*/
$reversedMatches = array_reverse($matches[0]);
foreach ($reversedMatches as $match) {
// 确定匹配项的偏移量和长度
$offset = $match[1];
$length = strlen($match[0]);
/**
* 隔离匹配项及其之后的部分。
* 1. $node->splitText($offset) 将文本节点在$offset处分割。
* 原节点保留$offset之前的文本,返回的新节点($word)包含$offset及之后的文本。
* 2. $word->splitText($length) 将$word节点在$length处再次分割。
* $word保留匹配文本,返回的新节点($after)包含匹配文本之后的部分。
*/
$word = $node->splitText($offset); // $word 现在是包含匹配项和其后内容的DOMText节点
$after = $word->splitText($length); // $word 现在只包含匹配项的DOMText节点,$after是匹配项之后的内容
// 创建包裹用的<span>标签
$span = $DOM->createElement("span");
$span->setAttribute("class", "__brand");
// 替换匹配文本节点为<span>,然后将匹配文本节点重新插入到<span>中
$word->parentNode->replaceChild($span, $word);
$span->appendChild($word);
}
}
/**
* 保存更改,并移除不必要的<html>标签。
* 由于我们加载时使用了<html>包装,现在需要提取其子节点的内容。
*/
$content = '';
foreach ($DOM->documentElement->childNodes as $childNode) {
$content .= $DOM->saveHTML($childNode);
}
}
return $content;
}
// 示例用法(WordPress环境下的filter钩子)
// add_filter("ccjm_final_output", "ccjm_branding_filter");
// 示例内容
$sampleContent = <<<HTML
<p>C.C. Johnson & Malhotra, P.C. (CCJM) was an integral member of a large Design Team for a 16.5-mile-long Public-Private Partnership (P3) Purple Line Project. The east-west light rail system extends from New Carrollton in PG County, MD to Bethesda in MO County, MD with 21 stations and one short tunnel. CCJM was Engineer of Record (EOR) for the design of eight (8) Bridges and design reviews for 35 transit/highway bridges and over 100 retaining walls of different lengths/types adjacent to bridges and in areas of cut/fill. CCJM designed utility structures for 42,000 LF of relocated water mains and 19,000 LF of relocated sewer mains meeting Washington Suburban Sanitary Commission (WSSC), Md Dept of Transportation (MDOT) MTA, and Local Standards.</p>
HTML;
// 模拟WordPress环境下的函数
if (!function_exists('is_admin')) {
function is_admin() { return false; }
}
if (!function_exists('wp_doing_ajax')) {
function wp_doing_ajax() { return false; }
}
$processedContent = ccjm_branding_filter($sampleContent);
echo $processedContent;
?>在PHP中使用DOMDocument和DOMXPath进行文本节点内容的批量修改和包裹是一个常见的任务。通过理解DOMText::splitText()的工作原理及其对节点结构的影响,并结合倒序处理匹配项的策略,可以有效地避免因DOM修改导致的偏移量失效问题。这种方法不仅提高了代码的健壮性,也确保了所有目标短语都能被准确无误地处理。
立即学习“PHP免费学习笔记(深入)”;
以上就是PHP DOM操作:在文本节点中安全地批量替换和包裹内容的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号