php – SimpleXML children的属性在有和没有命名空间的情况下表现不同

SimpleXML examples page,“示例#5使用属性”部分指出:

Access attributes of an element just as you would elements of an array.

并且SimpleXMLElement::children()中的示例#1使用$element [‘attribute’]语法来访问子节点的属性;

向该代码添加命名空间将禁用对属性的访问:

$xml = new SimpleXMLElement(
'<person xmlns:a="foo:bar">
  <a:child role="son">
    <a:child role="daughter"/>
  </a:child>
  <a:child role="daughter">
    <a:child role="son">
      <a:child role="son"/>
    </a:child>
  </a:child>
</person>');
foreach ($xml->children('a', true) as $second_gen) {
    echo ' The person begot a ' . $second_gen['role'];
    foreach ($second_gen->children('a', true) as $third_gen) {
        echo ' who begot a ' . $third_gen['role'] . ';';
        foreach ($third_gen->children('a', true) as $fourth_gen) {
            echo ' and that ' . $third_gen['role'] . ' begot a ' . $fourth_gen['role'];
        }
    }
}
// results
// The person begot a who begot a ; The person begot a who begot a ; and that begot a 
// expected
// The person begot a son who begot a daughter; The person begot a daughter who begot a son; and that son begot a son

这里有很多问题指向相同的解决方案,使用SimpleXMLElement :: attributes()函数而不是作为数组访问,但没有答案解释原因.

使用命名空间时这种不同的行为是一个错误?文档是否过时了?我们应该始终使用SimpleXMLElement :: attributes()并避免使用推荐的类似数组的语法吗?

注意:我使用的是PHP 5.5.9-1ubuntu4.9.

相关问题

> Retrieving attributes of namespaced children
> how to access this child element – attribute in php simplexml
> Get children attributes using simplexml

最佳答案 其原因实际上与SimpleXML没有任何关系,而是根据标准与XML命名空间如何工作的一些令人惊讶的细节有关.

在您的示例中,您具有使用前缀a声明的名称空间,因此要声明属性位于该名称空间中,您必须在其名称前加上:,就像使用元素一样:

<a:child a:role="daughter"/>

似乎是一种常见的假设,即没有名称空间前缀的属性与它所在的元素位于同一名称空间中,但事实并非如此.上面的示例与您的示例不同:

<a:child role="daughter"/>

您可能会看到的另一种情况是默认(未加前缀)命名空间中的位置:

<person xmlns="http://example.com/foo.bar"><child role="daughter" /></person>

这里,子元素位于http://example.com/foo.bar命名空间中,但是角色属性仍然不是!如this related question所述,relevant section of the XML Namespaces spec包括以下声明:

The namespace name for an unprefixed attribute name always has no value.

也就是说,没有名称空间前缀的属性永远不会出现在任何名称空间中,无论文档的其余部分是什么样的.

那么,这对SimpleXML有什么影响?

只要您使用 – > children()或 – > attributes()方法,并且从那时起跟踪它,SimpleXML就会根据更改“当前命名空间”而工作.

所以当你写:

$children = $xml->children('a', true);

要么:

$children = $xml->children('http://example.com/foo.bar');

“当前命名空间”是foo:bar.随后使用 – > childElement或[‘attribute’]语法将在此命名空间中查找 – 您不需要每次都再次调用children() – 但是在那里找不到您的未加前缀的属性,因为它们没有命名空间.

当你后来写:

$attributes = $children->attributes();

这解释方式与:

$attributes = $children->attributes(null);

所以现在,“当前命名空间”为空.现在,当您查找没有命名空间的属性时,您将找到它们.

点赞