[英]DOMDocument appendXML with special characters
我正在从数据库中检索一些html字符串,我想将这些字符串解析为DOMDocument。 问题是DOMDocument会以特殊字符给出警告。
警告:DOMDocumentFragment :: appendXML()[domdocumentfragment.appendxml]:实体:第2行:解析器错误:第189行的page.php中未定义实体'nbsp'
我想知道为什么,也想知道如何解决这个问题。 这是我页面的一些代码片段。 如何解决此类警告?
$doc = new DOMDocument();
// .. create some elements first, like some divs and a h1 ..
while($row = mysql_fetch_array($result))
{
$messageEl = $doc->createDocumentFragment();
$messageEl->appendXML($row['message']); // gives it's warnings here!
$otherElement->appendChild($messageEl);
}
echo $doc->saveHTML();
我还发现了一些有关验证的信息,但是当我将其应用时,我的页面将不再加载。 我为此尝试的代码是这样的。
$implementation = new DOMImplementation();
$dtd = $implementation->createDocumentType('html','-//W3C//DTD XHTML 1.0 Transitional//EN','http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd');
$doc = $implementation->createDocument('','',$dtd);
$doc->validateOnParse = true;
$doc->formatOutput = true;
// in the same whileloop, I used the following:
$messageEl = $doc->createDocumentFragment();
$doc->validate(); // which stopped my code, but error- and warningless.
$messageEl->appendXml($row['message']);
提前致谢!
没有
在XML中。 唯一定义了实际名称 (而不是使用数字引用)的字符实体是&
, <
, >
, "
和'
。
这意味着您必须使用不间断空格的数字等效值,即 
或(以十六进制表示)  
。
如果您试图将HTML保存到XML容器中,则将其另存为文本。 HTML和XML可能看起来很相似,但是却截然不同。 appendXML()
期望格式正确的XML作为参数。 改用nodeValue
属性,它将对XML字符串进行XML编码而没有任何警告。
// document fragment is completely unnecessary
$otherElement->nodeValue = $row['message'];
这是一个棘手的问题,因为实际上一个问题是多个。
就像Tomalak指出的那样,没有
在XML中。 因此,您指定了DOMImplementation的操作正确,因为在XHTML中存在
。 但是,要让DOM知道该文档是XHTML,您必须针对DTD进行加载和验证。 DTD位于
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
但是由于每天有对该页面的数百万个请求,因此W3C决定阻止对该页面的访问 ,除非请求中发送了UserAgent。 要提供UserAgent,您必须创建一个自定义流上下文。
在代码中:
// make sure DOM passes a User Agent when it fetches the DTD
libxml_set_streams_context(
stream_context_create(
array(
'http' => array(
'user_agent' => 'PHP libxml agent',
)
)
)
);
// specify the implementation
$imp = new DOMImplementation;
// create a DTD (here: for XHTML)
$dtd = $imp->createDocumentType(
'html',
'-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
);
// then create a DOMDocument with the configured DTD
$dom = $imp->createDocument(NULL, "html", $dtd);
$dom->encoding = 'UTF-8';
$dom->validate();
$fragment = $dom->createDocumentFragment();
$fragment->appendXML('
<head><title>XHTML test</title></head>
<body><p>Some text with a entity</p></body>
'
);
$dom->documentElement->appendChild($fragment);
$dom->formatOutput = TRUE;
echo $dom->saveXml();
这仍然需要一些时间来完成(不要问我为什么),但是最后,您会得到( 重新格式化为SO )
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>XHTML test</title>
</head>
<body>
<p>Some text with a entity</p>
</body>
</html>
我确实看到有问题的问题,并且该问题已经得到回答,但是如果我愿意的话,我想提出我过去对类似问题的看法。
可能是因为您的任务需要在结果XML中包含来自数据库的标记数据,但可能需要也可能不需要解析。 如果仅是要包含的数据,而不是XML的结构化部分,则可以将数据库中的字符串放在CDATA节中 ,从而有效地绕过了此阶段的所有验证错误。
这是另一种方法,因为我们不想降低网络请求的速度(或者根本不希望任何来自用户输入的网络请求):
<?php
$document = new \DOMDocument();
$document->loadHTML('<html><body></body></html>');
$html = '<b>test </b>';
$fragment = $document->createDocumentFragment();
$html = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE document [
<!ENTITY nbsp " " >
]>
<document>'.$html.'</document>';
$newdom = new \DOMDocument();
$newdom->loadXML($html, LIBXML_HTML_NOIMPLIED | LIBXML_NOCDATA | LIBXML_NOENT | LIBXML_NONET | LIBXML_NOBLANKS);
foreach ($newdom->documentElement->childNodes as $childnode)
$fragment->appendChild($fragment->ownerDocument->importNode($childnode, TRUE));
$document->getElementsByTagName('body')[0]->appendChild($fragment);
echo $document->saveHTML();
在这里,我们包括DTD的相关部分,特别是latin1实体定义作为内部DOCTYPE定义。 然后,将HTML内容包装在document元素中,以便能够处理一系列子元素。 然后将解析的节点导入并添加到目标DOM中。
我们的实际实现使用file_get_contents从本地文件加载包含所有实体定义的DTD。
虽然聪明人可能是一个不错的选择(为什么要第14次发明方向盘?),但etranger可能有一点意义。 在某些情况下,您不想使用像完整的新包装(未经研究)的软件包那样的过大杀伤力,而更像是想要从数据库中发布一些数据,而这些数据恰好包含XML解析器有问题的html东西。
警告,以下是一个简单的解决方案,但是除非您确定可以摆脱它,否则请不要这样做! (我是在截止日期前约2小时并且没有时间学习的时候才这样做的,让诸如此类的东西成为孤独的工具...)
在将字符串粘贴到appendXML函数之前,请通过preg_replace运行它。 例如,替换所有&nbsp; [some_prefix] _nbsp的字符。 然后,在显示html的页面上,执行相反的操作。
和普雷斯托! =)
示例代码:将文本放入文档片段的代码:
// add text tag to p tag.
// print("CCMSSelTextBody::getDOMObject: strText: ".$this->m_strText."<br>\n");
$this->m_strText = preg_replace("/ /", "__nbsp__", $this->m_strText);
$domTextFragment = $domDoc->createDocumentFragment();
$domTextFragment->appendXML(utf8_encode($this->m_strText));
$p->appendChild($domTextFragment);
// $p->appendChild(new DOMText(utf8_encode($this->m_strText)));
解析字符串并编写html的代码:
// Instantiate template.
$pTemplate = new CTemplate($env, $pageID, $pUser, $strState);
// Parse tag-sets.
$pTemplate->parseTXTTags();
$pTemplate->parseCMSTags();
// present the html code.
$html = $pTemplate->getPageHTML();
$html = preg_replace("/__nbsp__/", " ", $html);
print($html);
考虑一个更强大的替代品可能是一个好主意。 (如果您坚持要做到透彻,请执行以下操作:在time()值上执行md5,并将其结果硬编码为前缀。就像在第一个代码段中一样:
$this->m_strText = preg_replace("/ /", "4597ee308cd90d78aa4655e76bf46ee0_nbsp", $this->m_strText);
在第二个中:
$html = preg_replace("/4597ee308cd90d78aa4655e76bf46ee0_nbsp/", " ", $html);
对您需要规避的任何其他标签和内容执行相同的操作。
无论如何,这是一个hack,不是好的代码。 但它挽救了我的生命,并希望与遇到此特定问题的其他人分享,并花几分钟时间。
使用上述内容的风险自负。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.