Атрибуты childrenXXXXXXXX отличаются друг от друга с пространством имен и без него

Страница примеров SimpleXML , раздел «Пример # 5 Использование атрибутов»:

Доступ к атрибутам элемента так же, как и к элементам массива.

И пример # 1 в SimpleXMLElement::children() работает с синтаксисом $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 .


Связанные вопросы

  • Получение атрибутов детей с именами
  • как получить доступ к этому дочернему элементу – атрибут в php simplexml
  • Получить атрибуты children с помощью 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 прежнему отсутствует ! Как обсуждалось в этом связанном вопросе , соответствующий раздел спецификации XML Namespaces включает в себя следующее утверждение:

Имя пространства имен для неподписанного имени атрибута всегда не имеет значения.

То есть атрибут без префикса пространства имен никогда не находится в каком-либо пространстве имен , независимо от того, как выглядит остальная часть документа.

Итак, какое влияние это оказывает на 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 . Теперь, когда вы ищете атрибуты, которые не имеют пространства имен, вы найдете их.