SimpleXMLElement 在 addChild 和 addAttribute 中处理文本值的基本原理
Posted
技术标签:
【中文标题】SimpleXMLElement 在 addChild 和 addAttribute 中处理文本值的基本原理【英文标题】:Rationale behind SimpleXMLElement's handling of text values in addChild and addAttribute 【发布时间】:2010-10-07 20:48:09 【问题描述】:这不是不一致的行为吗? (php 5.2.6)
<?php
$a = new SimpleXMLElement('<a/>');
$a->addAttribute('b', 'One & Two');
//$a->addChild('c', 'Three & Four'); -- results in "unterminated entity reference" warning!
$a->addChild('c', 'Three & Four');
$a->d = 'Five & Six';
print($a->asXML());
渲染:
<?xml version="1.0"?>
<a b="One & Two">
<c>Three & Four</c>
<d>Five & Six</d>
</a>
在 bugs.php.net,他们拒绝所有关于此的提交,称这是一项功能。为什么会这样?顺便说一句,文档中没有关于 SimpleXMLElement 转义文本值的差异。
谁能说服我这是最好的 API 设计决策?
【问题讨论】:
顺便说一下,如果你必须在你的 标签内放置多个为了确保我们在同一页面上,您有三种情况。
使用 addAttribute 将 & 符号插入属性
使用 addChild 将 & 符号插入元素
通过属性重载将&符号插入元素
2 和 3 之间的差异让您感到困惑。为什么 addChild 不会自动转义 & 符号,而向对象添加属性并设置其值确实会自动转义 & 符号?
基于我的直觉,并受到this bug 的鼓舞,这是一个深思熟虑的设计决定。属性重载 ($a->d = 'Five & Six';) 旨在成为“我的转义符”的做事方式。 addChild 方法的意思是“添加我告诉你添加的内容”方法。因此,无论您需要哪种行为,SimpleXML 都能满足您的需求。
假设您有一个文本数据库,其中所有 & 符号都已转义。自动转义在这里对你不起作用。那就是您要使用 addChild 的地方。或者假设您需要在文档中插入一个实体
$a = simplexml_load_string('<root></root>');
$a->b = 'This is a non-breaking space ';
$a->addChild('c','This is a non-breaking space ');
print $a->asXML();
这就是那个 bug 中的 PHP 开发人员所提倡的。当您需要在文档中插入 & 符号而不被转义时,addChild 的行为旨在提供“不那么简单、更健壮”的支持。
当然,这确实给我们留下了我提到的第一种情况,addAttribute 方法。 addAttribute 方法确实 转义和符号。因此,我们现在可以将不一致声明为
-
addAttribute 方法对 & 符号进行转义
addChild 方法不会转义 & 符号
此行为有些不一致。用户期望 SimpleXML 上的方法以一致的方式转义是合理的
这暴露了 SimpleXML api 的真正问题。这里的理想情况是
-
元素对象上的属性重载会转义 & 符号
属性对象上的属性重载会转义 & 符号
addChild 方法不会转义 & 符号
addAttribute 方法不会转义 & 符号
但这是不可能的,因为 SimpleXML 没有属性对象的概念。 addAttribute 方法是(似乎是?)添加属性的唯一方法。因此,事实证明(似乎?)SimpleXML 无法使用实体创建属性。
所有这些都揭示了 SimpleXML 的悖论。这个 API 背后的想法是提供一种与复杂事物交互的简单方式。
团队本可以添加一个 SimpleXMLAttribute 对象,但这增加了一层复杂性。如果您想要一个多对象层次结构,请使用 DomDoument。
团队本可以向 addAttribute 和 addChild 方法添加标志,但标志会使 API 更加复杂。
真正的教训在这里?也许就是这么简单就很难,而在最后期限内简单就更难了。我不知道是不是这样,但是对于 SimpleXML,似乎有人从一个简单的想法开始(使用属性重载使 XML 文档的创建变得容易),然后随着问题/功能请求的出现进行调整.
实际上,我认为这里真正的教训是只使用 JSON ;)
【讨论】:
你,先生,真棒。感谢您对我无法弄清楚的事情的高质量解释。 "addAttribute 方法是添加属性的唯一方法" - 这是不正确:$a->b['attr'] = 'value';
。使用数组访问运算符,您可以使用unset
创建、更改和删除属性。但是,它取决于键:当键是整数时,这处理子元素,但是当键是字符串时,它与属性一起使用。 (注意:我做了最少的试验,所以我可能会遗漏一两个细节!)
我在这里看到@Alan 和我来到这里是因为 Magento 错误涉及这个精确问题:Mage_Usa_Model_Shipping_Carrier_Dhl_International::_doRequest()
有相同的错误(例如,如果客户端有一个包含 &
的 companyname
请求将被破坏)【参考方案2】:
这是我的解决方案,特别是这解决了添加多个具有相同标记名的孩子
$job->addChild('industrycode')->0 = $entry1;
$job->addChild('industrycode')->0 = $entry2;
$job->addChild('industrycode')->0 = $entry3;
【讨论】:
遗憾的是,这种方法虽然可以一次性完成分配,但它失去了分配能力。例如。 $newnode = $job...... 请注意,使用此对象表示法设置值在 PHP 7.0+ 中不再起作用。相反,您可以使用 $job->addChild('industrycode')[0]。例如,请参阅3v4l.org/cliKZ。【参考方案3】:“假设您有一个文本数据库,其中所有 & 符号都已转义。”
如果你这样做,那你就错了。数据应该以最准确的形式存储,而不是针对您当前使用的任何类型的输出。如果您实际上在数据库中存储(有效)html 的 blob,情况会更糟。使用 addChild() 并再次获取数据会破坏您的 HTML;没有任何明智的图书馆表现出如此可怕的不对称性。
addChild() 不为您的文本编码是完全违反直觉的。 API 中不能保护您免受此影响的意义何在?如果您在其中一个值中使用双引号,这就像 json_encode() 吐槽。
无论如何,回答最初的问题:显然,我也认为这不是一个好的决定。我确实认为这与 PHP 的许多设计决策是一致的,即实现某人“更快”的想法,而不是正确的。
【讨论】:
【参考方案4】:转义字符 &
和 &lt;
的要求在 Character Data and Markup 部分中提供,而不是在属性值规范化部分中提供,如上一个答案所述。
To quote the XML Spec.:
“和号字符 (&) 和左尖括号 (& 和 &lt;
进行转义"
【讨论】:
【参考方案5】:Alan Storm 对这个问题有很好的描述,但是对于他描述的悖论,有一个简单的解决方案。 addChild() 方法可以有一个可选的布尔参数,用于确定是否自动转义字符。所以,我仍然坚信这只是一个(非常)糟糕的设计选择。
addChild() 方法的文档没有提供任何参考,因此问题更加复杂(尽管正在讨论中)。此外,该方法转义了一些字符(即小于号和大于号)。这会误导使用该方法的开发人员认为它通常会转义字符。
【讨论】:
【参考方案6】:我相信这是由 XML 规范要求的 Attribute-Value Normalization 引起的。
【讨论】:
以上是关于SimpleXMLElement 在 addChild 和 addAttribute 中处理文本值的基本原理的主要内容,如果未能解决你的问题,请参考以下文章
如何在 PHP 中回显此 SimpleXMLElement 中的 OK 属性? [复制]