我在PHP中使用XPATH处理XHTML的XML兼容输入,如下所示:
$xml=new DOMDocument();
$xml->loadXML(utf8_encode($temp));
[...]
$temp=utf8_decode($xml->saveXML());
出现的问题是,根据HTML5规范,节点可能无法自行关闭,例如
<textarea id="something"></textarea>
或div 以通过 JS 利用
<div id="someDiv" class="whaever"></div>
回来作为
<textarea id="something" />
和
<div id="someDiv" class="whaever" />
我目前通过使用 str_replace
来解决这个问题,但这是胡说八道,因为我需要匹配个别情况。我该如何解决这个问题?
同时XPATH坚持推出
xmlns:default="http://www.w3.org/1999/xhtml
在新创建的各个节点上,它会放置类似 <default:p>
.我如何在不诉诸愚蠢的搜索和替换的情况下停止它,如下所示:
$temp=str_replace(' xmlns:default="http://www.w3.org/1999/xhtml" '," ",$temp);
$temp=str_replace(' xmlns:default="http://www.w3.org/1999/xhtml"'," ",$temp);
$temp=str_replace('<default:',"<",$temp);
$temp=str_replace('</default:',"</",$temp);
?
编辑:我真的遇到了愚蠢的搜索和替换问题,我不打算用正则表达式攻击输出XHTML。请考虑以下示例:
<div id="videoPlayer0" class="videoPlayerPlacement" data-xml="video/cp_IV_a_1.xml"/>
显然,自闭合div 是非法的(至少在我无法输出为 mime application/xhtml+xml 但被迫使用 mime text/html 的上下文中(,并且在所有其他情况下,它们肯定不会验证。
可以使用技巧规范化"非空"标签。这不是官方解决方案,但它有效。
function export_html(DOMDocument $dom)
{
$voids = [
'area',
'base',
'br',
'col',
'colgroup',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
];
// Every empty node;
// there is no reason to match nodes with content inside.
$query = '//*[not(node())]';
$nodes = (new DOMXPath($dom))->query($query);
foreach ($nodes as $node) {
if (in_array($node->nodeName, $voids)) {
// A void tag.
continue;
}
// Not a void tag. We inject a placeholder content.
$node->appendChild(new DOMComment('NOT_VOID'));
}
// We remove the placeholders.
return str_replace('<!--NOT_VOID-->', '', $dom->saveXML());
}
在您的示例中
$dom = new DOMDocument();
$dom->loadXML(<<<XML
<html>
<textarea id="something"></textarea>
<div id="someDiv" class="whaever"></div>
</html>
XML
);
echo export_html($dom);
将产生
<?xml version="1.0"?>
<html>
<textarea id="something"></textarea>
<div id="someDiv" class="whaever"></div>
</html>
- 创建 DOMDocument 的实例并将文档类型设置为 XHTML:
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->xmlStandalone = true;
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
$dom->loadXML('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">');
- 创建元素并将其附加到文档:
$html = $dom->createElement('html');
$dom->appendChild($html);
$body = $dom->createElement('body');
$html->appendChild($body);
$div = $dom->createElement('div');
$body->appendChild($div);
$span = $dom->createElement('span', 'This is an example');
$div->appendChild($span);
- 输出 XHTML 标记:
echo $dom->saveXML();
生成的输出将是有效的 XHTML,其中包含空元素的显式结束标记,例如 <div></div>
而不是 <div/>
。
如果你不知道HTML5可以作为XML编写和提供,看看这个:">对很多人来说似乎不是很清楚。因此,让我们澄清事实。HTML 5可以用HTML和XML编写。
接下来,将任何PHP示例作为XML实际提供,请设置相应的标头:
header("content-type: application/xhtml+xml; charset=UTF-8");
在实际的 XML 文档中,不能在没有右斜杠的情况下编写任何自结束标记。没有<br>
而不是</br>
等。有了这个前奏,让我们继续...
我们发现在 中使用 LIBXML_NOEMPTYTAG 选项
$xml=new DOMDocument();
$xml->loadXML(utf8_encode($temp));
// do stuff with the DOM
$temp=utf8_decode($xml->saveXML(NULL, LIBXML_NOEMPTYTAG));
不是"解决"问题,而是扭转它。HTML5规范命名了许多"void元素"。它们是: area, base, br, col, embed, hr, img, input, keygen, link, meta, param, source, track, wbr
并引用规范:"void 元素不能有任何内容(因为没有结束标签,所以不能在开始标签和结束标签之间放置任何内容(。
由于它们定义了缺乏内容,因此可以通过简单的正则表达式(缺乏实际解决方案(来实现这一目标:
$temp = preg_replace('#></(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)>#si', '/>', $temp);
之后,我们可以继续我在问题中遇到的其他愚蠢的修复:
$temp=str_replace(' xmlns:default="http://www.w3.org/1999/xhtml"','',$temp);
$temp=str_replace('<default:',"<",$temp);
$temp=str_replace('</default:',"</",$temp);