Проблема Weird SimpleXML – не может ссылаться на узлы по имени?

Я пытаюсь проанализировать удаленный файл XML, который действителен:

$xml = simplexml_load_file('http://feeds.feedburner.com/HammersInTheHeart?format=xml'); 

Корневой элемент – это feed , и я пытаюсь его захватить с помощью:

 $nodes = $xml->xpath('/feed'); //also tried 'feed', without slash 

Кроме того, он не находит никаких узлов.

 print_r($nodes); //empty array 

Или любые узлы любого типа, если я ищу их по имени тега, на самом деле:

 $nodes = $xml->xpath('//entry'); print_r($nodes); //empty array 

Однако он находит узлы, если я использую подстановочные знаки, например

 $nodes = $xml->xpath('/*/*[4]'); print_r($nodes); //node found 

Что происходит?

В отличие от DOM, SimpleXML не имеет понятия объекта документа, а только элементов. Поэтому, если вы загружаете XML, вы всегда получаете элемент документа.

 $feed = simplexml_load_file($xmlFile); var_dump($feed->getName()); 

Вывод:

 string(4) "feed" 

Это означает, что все выражения Xpath должны относиться к этому элементу или абсолютному. Простая feed не будет работать, поскольку контекст уже является элементом feed .

Но вот еще одна причина. URL-адрес является фидом Atom. Итак, элементы XML в пространстве имен http://www.w3.org/2005/Atom . Магический синтаксис SimpleXMLs распознает пространство имен по умолчанию для некоторых вызовов, но Xpath этого не делает. Здесь нет пространства имен по умолчанию в Xpath. Вам нужно будет зарегистрировать их с префиксом и использовать этот префикс в выражениях Xpath.

 $feed = simplexml_load_file($xmlFile); $feed->registerXpathNamespace('a', 'http://www.w3.org/2005/Atom'); foreach ($feed->xpath('/a:feed/a:entry[position() < 3]') as $entry) { var_dump((string)$entry->title); } 

Вывод:

 string(24) "Sharing the goals around" string(34) "Kouyate inspires Hammers' comeback" 

Однако в SimpleXML регистрация должна выполняться для каждого объекта, на который вы вызываете метод xpath() .

Использование Xpath с DOM немного отличается, но намного более мощным.

 $document = new DOMDocument(); $document->load($xmlFile); $xpath = new DOMXpath($document); $xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom'); foreach ($xpath->evaluate('/a:feed/a:entry[position() < 3]') as $entry) { var_dump($xpath->evaluate('string(a:title)', $entry)); } 

Вывод:

 string(24) "Sharing the goals around" string(34) "Kouyate inspires Hammers' comeback" 

Выражение Xpath с использованием DOMXpath::evaluate() может возвращать скалярные значения.