php修改文本内指定内容 php内容修改
在使用PHP DOMDocument和XPath对文本节点进行多次修改(如批量特定批量)时,因DOM结构变化可能导致splitText()方法报错,尤其是在正向匹配项时。论文将深入分析此问题,并提供核心解决方案:正确解解析preg_match_all结果,并采用逆序遍历匹配项的策略,以保证每次修改都不会影响后续操作的偏移量,从而实现对所有目标文本的精确包围。问题背景:DOM节点多次修改的挑战在网页内容处理中,我们经常需要识别并修改特定的文本片段,例如将所有出现的品牌名称用lt;spangt;标签包裹起来。PHP的DOMDocument和XPath提供了强大的能力来实现这一点。然而,当一个文本节点中存在多个匹配项时,并并且修改我们尝试使用DOMText::splitText()方法逐一修改它们时,一个常见的陷阱是,第一次会改变文本节点的内部结构,从而导致后续匹配项的偏移量失效,导致splitText()调用失败并引发致命 错误:未捕获的错误:调用成员函数 splitText() on bool错误。
具体来说,DOMText::splitText(offset)方法会在指定偏移量处将当前文本节点一分为二,返回一个新的文本节点(从偏移量开始的部分)。如果我们在一个文本节点上连续进行多次splitText()操作,并且每次操作都基于原始文本的偏移量,那么第一次在splitT ext()之后,原始文本节点的长度和内容都已改变,后续的偏移量将不再准确,甚至可能指向一个不再存在的文本部分,导致splitText()返回false,重新引发错误。解决方案:逆序遍历与正确的匹配结果处理
要解决这个问题,我们需要采取两种关键策略:正确解析preg_match_all的结果: preg_match_all函数返回的$matches架构包含多个维度。为了我们关心的完整匹配字符串及其偏移量,通常位于$matches[0]中。不正确的遍历(例如,foreach ($matches as $group))可能会导致处理或处理不正确的数据。逆序处理匹配项: 这是解决偏移量问题的核心。如果我们从文本节点的终点向指针处理匹配项,每次修改(例如,将文本节点拆分并插入lt;spangt;)只影响修改当前点“之前”的DOM结构。这意味着,对于尚未处理的、位于当前修改点“之后”的匹配项,它们的偏移量仍然相对于原始文本节点是准确的。详细步骤与示例代码
以下是基于原始问题的代码,并应用上述解决方案后的修改版本。我们将注意力集中于foreach ($text as $node)循环内部的修改逻辑。
立即学习“PHP免费学习笔记(深入)”;/** * 自动将各种形式的CCJM包装在一个类中以用于品牌推广 * * @param string $content * @return string */function ccjm_branding_filter(string $content): string { if (! (is_admin() amp;amp; ! wp_doing_ajax()) amp;amp; $content) { $DOM = new DOMDocument(); /** * 使用内部错误绕过HTML5警告 */ libxml_use_internal_errors(true); /** * 加载内容,使用正确的编码和解析所需的`lt;htmlgt;`包装器 */ $DOM-gt;loadHTML(quot;lt;?xml encoding='utf-8' ?gt;lt;htmlgt;{$content}lt;/htmlgt;quot;, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); /** * 清除错误以绕过 HTML5 警告 */ libxml_clear_errors(); /** * 初始化 XPath */ $XPath = new DOMXPath($DOM); /** * 检索所有文本节点,脚本中的节点除外 */ $text = $XPath-gt;query(quot;//text()[not(parent::script)]quot;); foreach ($text as $node) { /** * 查找所有匹配项,包括偏移量 * PREG_OFFSET_CAPTURE 捕获匹配项的偏移量 */ preg_match_all(quot;/(C\.? ?C\.?(?:JM| Johnson (?:amp;|amp;|amp;|and) Malhotra)(?: Engineers, LTD\.?|, P\.?C\.?)?)/iquot;, $node-gt;textContent, $matches, PREG_OFFSET_CAPTURE);
/** * 确定有匹配项才进行处理 * 逆序遍历匹配项,苏格兰DOM导致的偏移匹配量失效问题 * $matches[0] 包含所有完整的字符串及其偏移量 */ $group = array_reverse($matches[0]); // 关键:逆序遍历 $matches[0] foreach ($group as $match) { /** * 确定匹配的偏移量和长度 */ $offset = $match[1]; $length = strlen($match[0]); /** * 隔离匹配项和其后的内容 * splitText(offset) 分割当前节点在 offset 处分割, * 并返回从 offset 开始的新节点。 * 原节点会保留 offset 的部分。 */ $word = $node-gt;splitText($offset); /** * 再次调用 splitText,从 $word 节点的索引之前(即原匹配项的索引) * 分割出匹配项本身。 * $after节点将包含匹配项之后的部分。
*/ $after = $word-gt;splitText($length); /** * 创建包装 span */ $span = $DOM-gt;createElement(quot;spanquot;); $span-gt;setAttribute(quot;classquot;, quot;__brandquot;); /** * 将单词替换为 span, * 然后在 span 中重新插入单词 */ $word-gt;parentNode-gt;replaceChild($span, $word); $span-gt;appendChild($word); } } /** * 保存更改,删除不需要的标签 * 移除 body 内容,去除 html/body 包装 */ $content = implode(array_map([$DOM-gt;documentElement-gt;ownerDocument, quot;saveHTMLquot;], iterator_to_array($DOM-gt;documentElement-gt;childNodes))); // 如果需要更精确地获取lt;bodygt;内部的内容,可以查询body节点 // $body = $XPath-gt;query('//body')-gt;item(0); // if ($body) { // $content = ''; // foreach ($body-gt;childNodes as $child) { // $content .= $DOM-gt;saveHTML($child); // } // } } return $content;}// add_filter(quot;ccjm_final_outputquot;, quot;ccjm_branding_filterquot;); // 如果是 WordPress 环境,则需要此行登录后复制
代码修改因为要点:删除森林循环:原代码中的 foreach ($matches as $group) 是多余的,$matches 引用的第一个元素 $matches[0] 已经包含了所有完整的匹配项及其偏移量。
逆序遍历:将$matches[0]通过array_reverse()函数进行逆序处理。foreach ($group as $match)现在会从文本的结果匹配项开始处理。删除break:原代码中的break语句导致只处理了每个文本节点的第一个匹配项,现在可以安全删除,以确保所有匹配项都被处理。
结果示例
使用提供的示例内容:C.C. Johnson amp; Malhotra,P.C. (CCJM) 是 16.5 英里长的公私合作 (P3) 紫线项目大型设计团队的重要成员。东西轻轨系统从马里兰州 PG 县的新卡罗尔顿延伸至马里兰州密苏里州县的贝塞斯达,设有 21 个车站和 1 条短隧道。CCJM 担任八 (8) 座桥梁设计的记录工程师 (EOR),并对 35 座过境/公路桥梁和桥梁附近以及挖/填区域的 100 多座不同长度/类型的挡土墙进行设计审查。 CCJM 为 42,000 平方英尺的搬迁供水总管和 19,000 平方英尺的搬迁污水总管设计了公用设施结构,满足华盛顿郊区卫生委员会 (WSSC)、马里兰州交通部 (MDOT) MTA 和当地的要求标准。登录后复制
经过修改后的代码处理,结果输出将如下所示,所有匹配的模板都被正确地包裹在lt;spangt;标签中:lt;pgt;lt;span class=quot;__brand";gt;C.C. Johnson amp; Malhotra,P.C.lt;/spangt; (lt;spanclass=quot;__brandquot;gt;CCJMlt;/spangt;) 是 16.5 英里长的公私合作 (P3) 紫线项目大型设计团队的重要成员。东西轻轨系统从马里兰州 PG 县的新卡罗尔顿延伸到马里兰州密苏里县的贝塞斯达,设有 21 个车站和一条短隧道。lt;span class=quot;__brandquot;gt;CCJMlt;/spangt; 是八 (8) 座桥梁设计的记录工程师 (EOR),并为 35 座过境/公路桥梁和其他
在桥梁附近和挖/填区域有超过 100 道不同长度/类型的挡土墙。 lt;span class=quot;__brandquot;gt;CCJMlt;/spangt;为 42,000 LF 的搬迁水管和 19,000 LF 的搬迁下水道设计公用设施结构,符合华盛顿郊区卫生委员会 (WSSC)、马里兰州交通部 (MDOT) MTA 和 LocalStandards.lt;/pgt;登录后复制注意事项与总结 DOM 操作的原子性:它对DOM树的修改都可能影响其结构。当涉及到文本节点的分割和替换时,这一点极其重要。splitText()的返回类型:一定要检查splitText()返回的值。如果偏移量无效,可能返回false,导致后续对返回值的操作(如$word-gt;splitText($length))失败。性能考量:对于非常大的HTML文档和大量的文本节点,DOMDocument的操作可能会消耗分配的内存和CPU资源。在生产环境中,应进行性能测试。HTML解析: DOMDocument::loadHTML()在解析不标准或格式不佳的HTML时可能会遇到问题。使用libxml_use_internal_errors(true)和libxml_clear_errors()是一种常见的处理方式,但仍需保证输入HTML的质量。
通过采用逆序解决匹配项的策略,我们能够有效地规避PHP DOMDocument在处理单个文本节点内多个修改时所面临的偏移量失效问题,从而实现一致准确的文本转换内容。理解DOM操作的底层机制是编写高效且无错代码的关键。
以上就是解决PHP DOM操作中多次修改文本节点导致的splitText错误的详细信息,更多请关注乐哥常识网其他相关内容!