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