php - SimpleXML,请不要扩展实体

标签 php xml simplexml

<分区>

我正在使用 SimpleXML 来尝试解析 large XML file<!ENTITY声明。不幸的是,SimpleXML 似乎太急于继续扩展这些实体,我宁愿它没有,因为实体符号很短,易于解析,理论上不会在文件的新版本中改变,而扩展实体是可能会改变的英语句子。有什么方法可以告诉 SimpleXML 取消它吗?

我想过“预解析”XML 文件以去除 <!ENTITY在将文件内容传递给 XML 解析器之前需要一些位,但这感觉很糟糕,而且由于它是一个巨大的文件,我宁愿尽可能少地摆弄它。

(请原谅上面的任何错误术语;我已经有一段时间没有完成这种级别的 XML 工作了。)

最佳答案

它可能看起来是这样,但事实并非如此(除非你指定了我猜你没有指定的标志,尽管你没有在代码中显示你做了什么)。只是 SimpleXML 只能在您使用 ->asXML() 时将其返回给您。方法不是通过 to-string-implementation。

让我们做一些例子来演示它是如何工作的。我从 DTD 中选择了这个简单的实体:

<!ENTITY n "noun (common) (futsuumeishi)">

所以让我们选择第一个 <pos>元素,因为它包含 &n;实体:

$xml = simplexml_load_file($file);
$pos = $xml->entry->sense->pos;

变量$pos现在是 <pos> 的 SimpleXMLElement元素节点。让我们输出它看看解析器对 &n; 做了什么实体:

echo  "SimpleXML value (string): ", $pos         , "\n"
    , "SimpleXML value (XML)   : ", $pos->asXML(), "\n";

输出是:

SimpleXML value (string): noun (common) (futsuumeishi)
SimpleXML value (XML)   : <pos>&n;</pos>

如本例所示,&n;仍然存在 ( <pos>&n;</pos> ),只是当您将它作为字符串值 ( noun (common) (futsuumeishi) ) 访问时它会被扩展。

顺便说一句,这完全没问题,XML 规范在这里说,是否扩展这些实体取决于解析器。对于 SimpleXML 的设计目的,这完全有望在读取字符串值时进行扩展。

您甚至可以通过指定 LIBXML_NOENT 来控制此行为选项:

$xml = simplexml_load_file($file, NULL, LIBXML_NOENT);

这实际上会按照您假设的那样进行,实体现在已展开,XML 输出不再不再包含该实体:

SimpleXML value (string): noun (common) (futsuumeishi)
SimpleXML value (XML)   : <pos>noun (common) (futsuumeishi)</pos>

那么现在双问号如何做你正在寻找的东西?好吧,PHP 中的一个 XML 解析器实际上有一个实体模型是 DOMDocument。它是 SimpleXML 的姊妹库,在内部共享相同的内存对象。这是同一对象的输出(更准确地说:它的唯一子节点)对于这两种模式没有和有 LIBXML_NOENT :

Mode 1:
DOMDocument Class       : DOMEntityReference
DOMDocument value(XML)  : &n;
DOMDocument ->nodeName  : n

Mode 2 (LIBXML_NOENT):
DOMDocument Class       : DOMText
DOMDocument value(XML)  : noun (common) (futsuumeishi)
DOMDocument ->nodeName  : #text

这是由以下代码创建的,它应该使给定输出背后的内容更加可见:

$node   = dom_import_simplexml($pos);
$doc    = $node->ownerDocument;
$entity = $node->firstChild;

echo  "DOMDocument Class       : ", get_class($entity)    , "\n"
    , "DOMDocument value(XML)  : ", $doc->saveXML($entity), "\n"
    , "DOMDocument ->nodeName  : ", $entity->nodeName     , "\n";

正如所写,它是一个姊妹图书馆并且dom_import_simplexml$pos进入DOMElement其中我们需要遍历它的子项,我们知道这是有问题的实体引用。

现在这开始变得很有意义了:由于 SimpleXML 不能表示实体引用,它只能提供扩展的字符串值包含实体的 XML。

否则如何区分字符串值

<pos>&n;</pos>
<pos><![CDATA[&n;]]></pos>

?所以你的要求只有有限的意义。然而,这并不意味着我们无法处理它,因此可以通过从它扩展来欺骗 SimpleXML 来做到这一点。假设每个只包含单个实体的子元素都应该返回。否则应使用标准的 SimpleXML 字符串化:

/**
 * Class EntityPreserveXML
 */
class EntityPreserveXML extends SimpleXMLElement
{
    /**
     * @return string
     */
    public function __toString()
    {
        $dom = dom_import_simplexml($this);
        if (
            !$dom instanceof DOMElement
            || $dom->childNodes->length !== 1
            || ! $dom->firstChild instanceof DOMEntityReference
        ) {
            return parent::__toString();
        }

        return $dom->ownerDocument->saveXML($dom->firstChild);
    }
}

让我们在上面的示例中运行它:

require('EntityPreserveXML.php');
$xml = simplexml_load_file($file, 'EntityPreserveXML');
$pos = $xml->entry->sense->pos;

echo  "SimpleXML value (string): ", $pos         , "\n"
    , "SimpleXML value (XML)   : ", $pos->asXML(), "\n";

SimpleXML 现在使用扩展类,然后按预期给出:

SimpleXML value (string): &n;
SimpleXML value (XML)   : <pos>&n;</pos>

&n;因为它是唯一的 child ,现在保存在 SimpleXMLElement 的字符串转换中。但仅仅因为这有效并不意味着您应该使用它,它打破了以文本形式解析的 XML 与文档模型含义中的 XML 之间的编码边界。

可能您只是在寻找 DOMDocument?这是一个包含更多细节的模型,您可以从中使用 DOMEntityReference如果有的话。

关于php - SimpleXML,请不要扩展实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19749869/

相关文章:

android - 时代广场日历XML文件错误

PHP简单XML解析

php - 从 XMLReader 打开的 simplexml 中的 CData

xml - 有没有办法为亚马逊的产品获取 XML?

php - 如何将 PHPUnit 与 VSCode 结合使用?

php - X-Cart 4.6 向数据库中的新表提交数据时出错

php - 需要从 user_items 表 LARAVEL 中获取 ID

xml - 转换输出 `>` 而不是 `&gt;`

php - 在 php 中使用 simplexml 获取 xml 中的父元素?

php mysql_insert_id 在多行上?