如何防止 DOMDocument 转换 转为统一码

Col*_*man 3 php domdocument

我正在尝试获取DOMElementPHP 中 a 的内部 HTML。标记示例:

<div>...</div>
<div id="target"><p>Here's some &nbsp; <em>funny</em> &nbsp; text</p></div>
<div>...</div>
<div>...</div>
Run Code Online (Sandbox Code Playgroud)

将上面的字符串输入变量中$html,我正在做:

$doc = new DOMDocument();
@$doc->loadHTML("<html><body>$html</body></html>");
$node = $doc->getElementById('target')
$markup = '';
foreach ($node->childNodes as $child) {
  $markup .= $child->ownerDocument->saveXML($child);
}
Run Code Online (Sandbox Code Playgroud)

生成的$markup字符串如下所示(转换为 JSON 以显示不可见字符):

"<p>Here's some \u00a0 <em>funny<\/em> \u00a0 text<\/p>"
Run Code Online (Sandbox Code Playgroud)

所有&nbsp;字符都已转换为 Unicode 不间断空格,这会破坏我的应用程序。

在我的理想世界中,有一种方法可以按原样检索目标 div 内的原始 HTML 字符串,而无需DomDocument对其执行任何操作。这似乎不可能,所以下一个最好的办法是以某种方式关闭此字符转换。到目前为止我已经尝试过:

  • 设置$doc->substituteEntities = false;没有结果。更改为true也没有帮助。
  • 切换$doc->preserveWhiteSpace方式均无变化
  • 更改saveXMLsaveHTML. 没有什么区别。

最后我求助于这个 hack,它有效,但感觉不是正确的解决方案。

$markup = str_replace("\xc2\xa0", '&nbsp;', $markup);
Run Code Online (Sandbox Code Playgroud)

当然有更好的方法吗?

mik*_*n32 6

您可以使用非常神秘的函数mb_encode_numericentity()来转换可见 ASCII 范围之外的这些字符,因此它不会影响您的标记等:

<?php
$html = <<< HTML
<div>...</div>
<div id="target"><p>Here's some &nbsp; <em>funny </em> &nbsp; text</p></div>
<div>...</div>
<div>...</div>
HTML;

$doc = new DOMDocument();
libxml_use_internal_errors();
$doc->loadHTML("<html><head><meta charset=UTF-8></head><body>$html</body></html>");
$node = $doc->getElementById('target');
$markup = '';
foreach ($node->childNodes as $child) {
  $markup .= $child->ownerDocument->saveHTML($child);
}

$convmap = [
    0x00, 0x1f, 0, 0xff,
    0x7f, 0x10ffff, 0, 0xffffff,
];

$markup = mb_encode_numericentity($markup, $convmap, "UTF-8");

echo $markup;
Run Code Online (Sandbox Code Playgroud)

输出:

<p>Here's some &#160; <em>funny &#128514;</em> &#160; text</p>
Run Code Online (Sandbox Code Playgroud)

超出了原始问题的范围,但我也在字符串中添加了表情符号。要对多字节字符进行编码,<meta charset="UTF-8">将强制 PHP 将内容视为 Unicode,而不是默认的 ISO-8859-1。