如何让DOMDocument加载含未知XML实体的XML且不破坏内容?
问题
为API响应实现XML签名时遇到以下问题:
- XML包含未知XML实体(如
ä等转换后的HTML字符),无法正常转换为DOMDocument,加载后documentElement未被设置,同时抛出实体相关警告 - 尝试以HTML格式解析,即便传入
LIBXML_HTML_NOIMPLIED标志,仍会被添加重复的<xml>和<p>标签 - 使用
LIBXML_DTDLOAD标志虽能正确解析,但会将XML实体文件内容直接输出到响应中,导致返回内容长度大幅增加
希望找到方法,让DOMDocument加载XML字符串时:
- 不因未知实体解析失败
- 不修改原XML字符串内容
- 或在不生成独立XML的情况下解析DTD
当前实现代码:
// 压缩后的XML字符串 $output = '<?xml version="1.0" standalone="no"?> <!DOCTYPE api_result [ <!ELEMENT api_result (#PCDATA)> <!ENTITY % xhtml-all SYSTEM "http://server.local/entities/xhtml-all.ent"> %xhtml-all; ]> <api_result>ä</api_result>'; $xml = new DOMDocument(); // 必须保留空白字符 $xml->preserveWhiteSpace = true; $xml->formatOutput = false; $xml->strictErrorChecking = false; $xml->loadXML($output); // 规范化内容(排他性,不含注释) if (!$xml->documentElement) { // 此处无法获取内容 throw new XmlSignerException('Undefined document element'); }
解决方案
核心思路
让DOMDocument加载DTD以识别实体定义,但不解析外部实体的实际内容,同时抑制错误警告,确保documentElement正常初始化,且保存XML时完全保留原实体与结构。
修改后的代码
// 压缩后的XML字符串 $output = '<?xml version="1.0" standalone="no"?> <!DOCTYPE api_result [ <!ELEMENT api_result (#PCDATA)> <!ENTITY % xhtml-all SYSTEM "http://server.local/entities/xhtml-all.ent"> %xhtml-all; ]> <api_result>ä</api_result>'; $xml = new DOMDocument(); // 保留原格式与空白字符 $xml->preserveWhiteSpace = true; $xml->formatOutput = false; $xml->strictErrorChecking = false; // 关键配置:禁用外部实体内容解析,仅加载DTD结构 $xml->resolveExternals = false; // 加载XML时启用DTD加载,并抑制错误警告 $loaded = $xml->loadXML($output, LIBXML_DTDLOAD | LIBXML_NOERROR | LIBXML_NOWARNING); if (!$loaded || !$xml->documentElement) { throw new XmlSignerException('Undefined document element'); } // 后续签名操作... // 保存XML时会保留原实体与DOCTYPE结构,不会内联外部实体内容 // $signedXml = $xml->saveXML();
关键参数说明
LIBXML_DTDLOAD:加载文档中的DTD,让DOMDocument识别实体定义,避免因未知实体导致解析失败LIBXML_NOERROR | LIBXML_NOWARNING:抑制实体相关的警告与错误,防止解析流程中断$xml->resolveExternals = false:禁止DOMDocument加载外部实体文件的实际内容,避免响应中混入实体文件内容,同时保留原实体引用
额外建议
如果外部DTD访问存在性能或稳定性问题,建议将xhtml-all.ent下载到本地服务器,修改DOCTYPE中的SYSTEM路径为本地文件路径,进一步优化加载效率。
内容的提问来源于stack exchange,提问作者Randommm
相关产品推荐
相关产品推荐

