有名称空间和没有名称空间时,SimpleXML子属性的行为不同

2022-04-13 00:00:00 xml php simplexml

SimpleXML examples page节使用属性的示例#5说明:

像访问数组的元素一样访问元素的属性。

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:前缀,就像您对元素所做的那样:

<a:child a:role="daughter"/>
没有名称空间前缀的属性与它所在的元素位于相同的名称空间,这似乎是一种常见的假设,但事实并非如此。上面的示例与您的示例不等同:

<a:child role="daughter"/>

您可能会看到的另一种情况是在默认(无前缀)命名空间中存在:

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

这里,child元素在http://example.com/foo.bar命名空间中,但role属性仍然不是!如this related question中所述,relevant section of the XML Namespaces spec包括以下语句:

无前缀属性名称的命名空间名称始终没有值。

也就是说,不带命名空间前缀的属性永远不会在任何命名空间中,无论文档的其余部分是什么样子。

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

SimpleXML的工作原理是每当您使用->children()->attributes()方法时更改"当前命名空间",并从那时起跟踪它。

所以当你写:

$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);

现在,"当前名称空间"是null。现在,当您查找没有命名空间的属性时,您将找到它们。

相关文章