SimpleXMLElement在addChild和addAttribute中处理文本值的基本原理

时间:2009-02-16 11:04:24

标签: php xml

这不是一种不一致的行为吗? (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 &amp; Four');
$a->d = 'Five & Six';

print($a->asXML());

渲染:

<?xml version="1.0"?>
<a b="One &amp; Two">
    <c>Three &amp; Four</c>
    <d>Five &amp; Six</d>
</a>

在bugs.php.net,他们拒绝所有关于此的提交,称这是一个功能。为什么会这样?顺便说一句,文档中没有任何关于SimpleXMLElement转义文本值的差异。

有人能说服我这是最好的API设计决策吗?

6 个答案:

答案 0 :(得分:79)

为了确保我们在同一页面上,您有三种情况。

  1. 使用addAttribute

  2. 将&符插入属性中
  3. 使用addChild

  4. 将&符插入元素中
  5. 通过属性重载将&符号插入元素

  6. 这是2和3之间的差异让你陷入困境。为什么addChild不会自动转义&符号,而为对象添加属性并设置其值自动转义&符号吗?

    基于我的直觉,并受this bug的鼓舞,这是一个刻意的设计决定。属性重载($ a-&gt; d ='Five&amp; Six';)旨在成为“逃避&符号”的做事方式。 addChild方法意味着“正好添加我告诉你添加的方法”。因此,无论您需要哪种行为,SimpleXML都可以满足您的需求。

    假设您有一个文本数据库,其中所有的&符已经被转义。自动转义在这里对你不起作用。这就是你使用addChild的地方。或者假设您需要在文档中插入实体

    $a = simplexml_load_string('<root></root>');
    $a->b = 'This is a non-breaking space &nbsp;';
    $a->addChild('c','This is a non-breaking space &nbsp;');    
    print $a->asXML();
    

    这就是那个bug中的PHP Developer所倡导的。 addChild的行为旨在提供“不太简单,更强大”的支持,当您需要在文档中插入&符号而不进行转义时。

    当然,这确实让我们得到了我提到的第一种情况,即addAttribute方法。 addAttribute方法执行转义&符号。因此,我们现在可以将不一致性说明为

    1. addAttribute方法转义&符号
    2. addChild方法转义&符号
    3. 此行为有些不一致。用户希望SimpleXML上的方法以一致的方式逃避事情是合理的
    4. 这样就暴露了SimpleXML api的真正问题。这里理想的情况是

      1. 元素对象上的属性重载转义&符号
      2. 属性对象上的属性重载转义&符号
      3. addChild方法不会转义&符号
      4. addAttribute方法不会转义&符号
      5. 这是不可能的,因为SimpleXML没有属性对象的概念。 addAttribute方法是(似乎是?)添加属性的唯一方法。因此,事实证明(似乎?)SimpleXML无法用实体创建属性。

        所有这些都揭示了简单 XML的悖论。这个API背后的想法是提供一种与事物相互作用的简单方法,结果证明是复杂的。

        团队可以添加一个SimpleXMLAttribute对象,但这是一个复杂的附加层。如果需要多个对象层次结构,请使用DomDoument。

        团队可以在addAttribute和addChild方法中添加标志,但标志会使API更复杂。

        真正的教训在这里?也许这很简单很难,而且在截止日期之前简单就更难了。我不知道是否是这种情况,但是使用SimpleXML似乎有人从一个简单的想法开始(使用属性重载来简化XML文档的创建),然后在问题/功能请求进入时进行调整

        实际上,我认为这里的真正教训是只使用JSON;)

答案 1 :(得分:16)

这是我的解决方案,特别是这解决了添加几个具有相同标记名称的子项

$job->addChild('industrycode')->{0} = $entry1;
$job->addChild('industrycode')->{0} = $entry2;
$job->addChild('industrycode')->{0} = $entry3;

答案 2 :(得分:10)

“假设您有一个文本数据库,其中所有的&符号都已被转义。”

如果你这样做,你做错了。数据应以最准确的形式存储,而不是针对您当前使用的任何类型的输出。如果您实际在数据库中存储(有效)HTML的blob,情况会更糟。使用addChild()并再次获取数据将破坏您的HTML;没有明智的图书馆表现出如此可怕的不对称。

addChild()不会为您编码文本完全违反直觉。 API中有什么意义不能保护您免受此影响?如果你在一个值中使用双引号就像json_encode()barfing。

无论如何,要回答原来的问题:显然,我也认为这不是一个好的决定。我认为这与许多PHP的设计决策是一致的,这是为了满足某人对“更快”的想法,而不是正确的。

答案 3 :(得分:7)

转义字符&<的要求在Character Data and Markup部分中提供,而不是在属性值规范化部分中提供,因为先前的答案陈述

<强> To quote the XML Spec

“和号字符(&amp;)和左尖括号(&lt;)绝不能以字面形式出现,除非用作标记分隔符,或用于注释,处理指令或CDATA部分。它们在其他地方需要,它们必须使用数字字符引用或字符串分别&amp;&lt;进行转义“

答案 4 :(得分:5)

Alan Storm对这个问题有一个很好的描述,但是对于他所描述的悖论有一个简单的解决方案。 addChild()方法可以有一个可选的布尔参数,用于确定是否自动转义字符。所以,我仍然相信它只是一个(非常)糟糕的设计选择。

由于addChild()方法的文档没有任何引用所以问题(尽管在讨论中),这使得混淆更加复杂。此外,该方法转义一些字符(即小于和大于符号)。这将误导使用该方法的开发人员相信它一般会逃脱字符。

答案 5 :(得分:0)

我认为这是由XML规范要求的Attribute-Value Normalization引起的。