【问题标题】:php/simplexml - LIBXML options ignored?php/simplexml - LIBXML 选项被忽略?
【发布时间】:2020-02-11 00:13:39
【问题描述】:

我正在尝试将 LIBXML* 常量用于SimpleXMLElementconstructor 的第二个参数,但它们根本没有改变任何东西。

$xml = '<root><empty_tag/><foo></foo></root>';
$simpleXml = new SimpleXMLElement($xml, LIBXML_NOENT|LIBXML_NOXMLDECL|LIBXML_NOEMPTYTAG);

$simpleXml->foo = 'Ņ';

echo $simpleXml->asXML();

预期:

<root><empty_tag></empty_tag><foo>Ņ</foo></root>

实际:

<?xml version="1.0"?>
<root><empty_tag/><foo>&#x145;</foo></root>

如您所见,这些标志中没有一个做任何事情 - 实体仍然被转义(即使根据https://www.w3.org/TR/xml/#syntax,XML 应该只转义 "'&amp;&gt;&lt;),XML 声明仍然存在,并且空的标签保持为空。 有没有办法使用 SimpleXML 达到预期的结果?或者至少只转义 5 个特殊字符? addChild() 不是这里的选项,我正在分配现有节点。

【问题讨论】:

标签: php simplexml libxml2


【解决方案1】:

这些常量的命名可能有点神秘。那么究竟支持什么?

LIBXML_NOENT

实体是作为实体引用添加到文档中还是展开。需要通过加载文档来指定:

<?php

$xml = '<!DOCTYPE test [<!ENTITY c "TEST">]>
<test>&c;</test>';

echo (new SimpleXMLElement($xml))->asXML(), "\n";
echo (new SimpleXMLElement($xml, LIBXML_NOENT))->asXML(), "\n";

这显示了第一个输出:

<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY c "TEST">
]>
<test>&c;</test>

实体被保留。对于第二个回声,LIBXML_NOENT:

<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY c "TEST">
]>
<test>TEST</test>

XML 借自相关问答:What does LIBXML_NOENT do (and why isn't it called LIBXML_ENT)?

顺便说一句,这与您的文档中的非 US-ASCII 字符无关。如果您需要带有它的文档,请将编码设置为 UTF-8,例如:

$xml = '<root><empty_tag/><foo></foo></root>';
$simpleXml = new SimpleXMLElement($xml);

dom_import_simplexml($simpleXml)->ownerDocument->encoding = 'UTF-8';

$simpleXml->foo = 'Ņ';

echo $simpleXml->asXML();

这里的诀窍是set the encoding in the underlying DOMDocument,这是我知道SimpleXMLElement(和DOMDocument)的唯一方法。这里的输出:

<?xml version="1.0" encoding="UTF-8"?>
<root><empty_tag/><foo>Ņ</foo></root>

您看不到更多 &amp;#x145; 实体,而只能看到 Unicode(UTF-8 编码)中的 ŅXML declaration 现在还显示编码。

根据您的问题,我认为这就是您要“寻找”的LIBXML_NOENT

LIBXML_NOXMLDECL

列表中的第二个。我从来没有让它工作,它有问题和/或有一些特定的版本要求,但老实说,我什至不知道是否/在哪里故意应用它。

您可以从输出中删除包含 XML 声明的第一行(总是“\n”终止)。

或者您可以再次关联底层DOMDocument 以输出文档元素,因此它不是完整的文档,因此没有 XML 声明:

$dom = dom_import_simplexml($simpleXml)->ownerDocument;
echo $dom->saveXML($dom->documentElement);

输出:

<root><empty_tag/><foo>Ņ</foo></root>

这基本上是在:remove xml version tag when a xml is created in php 中建议的。

LIBXML_NOMPTYTAG

列表中的第三个也是最后一个。我现在可以引用 PHP 手册,但这已在其他地方完成 on site already 但无论如何,无论常量不可用,如何使用 SimpleXMLElement 执行此操作?

一种方法是再次通过DOMDocument 提供选项:

$dom = dom_import_simplexml($simpleXml)->ownerDocument;
echo $dom->saveXML($dom->documentElement, LIBXML_NOEMPTYTAG);

输出:

<root><empty_tag></empty_tag><foo>Ņ</foo></root>

或者为了做这个“纯”的 SimpleXML,一个空的文本节点插入每个空元素:

$xml = '<?xml version="1.0" encoding="UTF-8"?><root><empty_tag/><foo></foo></root>';
$simpleXml = new SimpleXMLElement($xml);
$simpleXml->foo = 'Ņ';

foreach ($simpleXml->xpath('//*[not(*) and string() = ""]') as $empty) {
    $empty[0] = '';
}

echo $simpleXml->asXML();

foreach 中,获取每个 xpath 查询的所有空元素,然后将其文本内容设置为空字符串,如果没有(空),则将在其中插入一个文本节点一个。输出:

<?xml version="1.0" encoding="UTF-8"?>
<root><empty_tag></empty_tag><foo>Ņ</foo></root>

我希望这可以为您提供您正在寻找的选项。

【讨论】:

  • 非常好,我相信很多人会喜欢它,谢谢!
【解决方案2】:

您需要添加 XML 文档使用 UTF-8 编码的事实,因此类似于...

$xml = '<?xml version="1.0" encoding="utf-8" ?><root><empty_tag/><foo></foo></root>';

给你...

<?xml version="1.0" encoding="utf-8"?>
<root><empty_tag/><foo>Ņ</foo></root>

【讨论】:

  • 这真的一点改进都没有。
  • 当你说它忽略 LIBXML_NOENT - 这是完全不正确的,事实上你的文档没有设置为允许 utf-8 字符是问题所在,与这个标志无关!跨度>
  • 它仍然不应该对实体进行编码!当然,这应该由用户决定是否应该使用合理的默认值来做这些事情。但无论如何,显然LIBXML_NOENTLIBXML_NOXMLDECL 具有相同的值2,因此它们显然是要在不同的上下文中使用,这真的很不幸。尽管如此,即使我们跳过 NOENT 标志,其他两个也不会做任何事情。
  • 查看我的 cmets 对其他 2 个选项的问题。
  • 为了完整起见,最好将所有这些都放在一个答案中。
猜你喜欢
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 1970-01-01
  • 2012-08-01
相关资源
最近更新 更多