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
。
答案 0 :(得分:2)
根据标准,其原因实际上与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
。现在,当您查找没有命名空间的属性时,您将找到它们。