PHP中DOMNode->replaceChild()仅替换首个<br>标签,如何替换全部?
问题:DOMDocument替换所有
<br>标签为换行符仅生效一次 参考相关方案封装了以下PHP函数,目的是将HTML中的<br>标签替换为换行符,但执行后仅第一个<br>被替换,第二个未被处理。
原函数代码
/* preserve all newlines by switching out <br>, which will be lost if you $node->textContent */ function newDOMDocumentBetter ($htmlstr) { $doc = new DOMDocument(); $doc->loadHTML(mb_convert_encoding($htmlstr, 'HTML-ENTITIES', 'UTF-8')); $brs = $doc->getElementsByTagName('br'); foreach ($brs as $node) { $node->parentNode->replaceChild($doc->createTextNode("\r\n"), $node); } echo $doc->C14N(); return $doc; }
测试场景
- 输入HTML:
<code><span style="color: #000000"><span style="color: #0000BB"><?php<br />$first </span><span style="color: #007700">? </span><span style="color: #0000BB">$second </span><span style="color: #007700">: </span><span style="color: #0000BB">$third<br />?></span></span></code>
- 执行后输出(仅第一个
<br>被替换):
<html><body><code><span style="color: #000000"><span style="color: #0000BB"><?php
 </span><span style="color: #007700">? </span><span style="color: #0000BB"> </span><span style="color: #007700">: </span><span style="color: #0000BB"><br></br>?></span></span></code></body></html>
解决方案
问题根源:getElementsByTagName返回的是动态DOM节点列表,当你替换一个节点后,列表的内部索引会发生偏移,导致后续节点被跳过。
解决方法:将动态节点列表转为静态数组,遍历数组进行替换,避免节点变化影响遍历过程。
修改后的函数:
/* preserve all newlines by switching out <br>, which will be lost if you $node->textContent */ function newDOMDocumentBetter ($htmlstr) { $doc = new DOMDocument(); $doc->loadHTML(mb_convert_encoding($htmlstr, 'HTML-ENTITIES', 'UTF-8')); $brs = $doc->getElementsByTagName('br'); // 将动态节点列表转为静态数组 $brNodes = iterator_to_array($brs); foreach ($brNodes as $node) { $node->parentNode->replaceChild($doc->createTextNode("\r\n"), $node); } echo $doc->C14N(); return $doc; }
原理:iterator_to_array会把动态的DOMNodeList转为普通PHP数组,遍历数组时操作的是节点的副本引用,不会因为原DOM结构变化而影响遍历顺序,从而确保所有<br>节点都被替换。
内容的提问来源于stack exchange,提问作者forgodsakehold
相关产品推荐
相关产品推荐

