فهرست منبع

Zend_Feed_Reader: Improved Atom version detection using DOMNode::isDefaultNamespace() and DOMNode::lookupPrefix() to be far more robust and reliable.
Fixes a subtle bug where Atom 0.3 namespace was registered correctly, but only because of the feed type set. In isolation, this would break Atom 0.3 entry parsing.


git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@19291 44c647ce-9c0f-0410-b52a-842ac1e357ba

padraic 16 سال پیش
والد
کامیت
e3f838f38b

+ 16 - 4
library/Zend/Feed/Reader.php

@@ -62,6 +62,7 @@ class Zend_Feed_Reader
     const TYPE_ANY              = 'any';
     const TYPE_ATOM_03          = 'atom-03';
     const TYPE_ATOM_10          = 'atom-10';
+    const TYPE_ATOM_10_ENTRY    = 'atom-10-entry';
     const TYPE_ATOM_ANY         = 'atom';
     const TYPE_RSS_090          = 'rss-090';
     const TYPE_RSS_091          = 'rss-091';
@@ -341,6 +342,8 @@ class Zend_Feed_Reader
 
         if (substr($type, 0, 3) == 'rss') {
             $reader = new Zend_Feed_Reader_Feed_Rss($dom, $type);
+        } elseif (substr($type, 8, 5) == 'entry') {
+            $reader = new Zend_Feed_Reader_Entry_Atom($dom->documentElement, 0, Zend_Feed_Reader::TYPE_ATOM_10);
         } elseif (substr($type, 0, 4) == 'atom') {
             $reader = new Zend_Feed_Reader_Feed_Atom($dom, $type);
         } else {
@@ -412,14 +415,14 @@ class Zend_Feed_Reader
     /**
      * Detect the feed type of the provided feed
      *
-     * @param  Zend_Feed_Abstract $feed A fully instantiated Zend_Feed object
+     * @param  Zend_Feed_Abstract|DOMDocument|string $feed
      * @return string
      */
-    public static function detectType($feed)
+    public static function detectType($feed, $specOnly = false)
     {
         if ($feed instanceof Zend_Feed_Reader_FeedInterface) {
             $dom = $feed->getDomDocument();
-        } elseif($feed instanceof DomDocument) {
+        } elseif($feed instanceof DOMDocument) {
             $dom = $feed;
         } elseif(is_string($feed) && !empty($feed)) {
             @ini_set('track_errors', 1);
@@ -439,7 +442,8 @@ class Zend_Feed_Reader
             }
         } else {
             require_once 'Zend/Feed/Exception.php';
-            throw new Zend_Feed_Exception('Invalid object/scalar provided: must be of type Zend_Feed_Reader_FeedInterface, DomDocument or string');
+            throw new Zend_Feed_Exception('Invalid object/scalar provided: must'
+            . ' be of type Zend_Feed_Reader_FeedInterface, DomDocument or string');
         }
         $xpath = new DOMXPath($dom);
 
@@ -504,6 +508,14 @@ class Zend_Feed_Reader
         if ($xpath->query('//atom:feed')->length) {
             return self::TYPE_ATOM_10;
         }
+        
+        if ($xpath->query('//atom:entry')->length) {
+            if ($specOnly == true) {
+                return self::TYPE_ATOM_10;
+            } else {
+                return self::TYPE_ATOM_10_ENTRY;
+            }
+        }
 
         $xpath->registerNamespace('atom', self::NAMESPACE_ATOM_03);
 

+ 3 - 0
library/Zend/Feed/Reader/EntryAbstract.php

@@ -154,6 +154,9 @@ abstract class Zend_Feed_Reader_EntryAbstract
      */
     public function getXpath()
     {
+        if (!$this->_xpath) {
+            $this->setXpath(new DOMXPath($this->getDomDocument()));
+        }
         return $this->_xpath;
     }
 

+ 39 - 45
library/Zend/Feed/Reader/Extension/Atom/Entry.php

@@ -86,13 +86,13 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['authors'];
         }
 
-        $authors = $this->_xpath->query(
+        $authors = $this->getXpath()->query(
             $this->getXpathPrefix() . '//atom:author' . '|'
                 . $this->getXpathPrefix(). '//atom:contributor'
         );
 
         if (!$authors->length) {
-            $authors = $this->_xpath->query(
+            $authors = $this->getXpath()->query(
                 '//atom:author' . '|' . '//atom:contributor'
             );
         }
@@ -127,7 +127,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['content'];
         }
 
-        $content = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:content)');
+        $content = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:content)');
 
         if ($content) {
             $content =  html_entity_decode($content, ENT_QUOTES, $this->getEncoding());
@@ -155,10 +155,10 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $date = null;
 
-        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
-            $dateCreated = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)');
+        if ($this->_getAtomType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $dateCreated = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)');
         } else {
-            $dateCreated = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)');
+            $dateCreated = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)');
         }
 
         if ($dateCreated) {
@@ -184,10 +184,10 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $date = null;
 
-        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
-            $dateModified = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)');
+        if ($this->_getAtomType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $dateModified = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)');
         } else {
-            $dateModified = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)');
+            $dateModified = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)');
         }
 
         if ($dateModified) {
@@ -211,7 +211,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['description'];
         }
 
-        $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:summary)');
+        $description = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:summary)');
 
         if (!$description) {
             $description = null;
@@ -237,7 +237,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $enclosure = null;
 
-        $nodeList = $this->_xpath->query($this->getXpathPrefix() . '/atom:link[@rel="enclosure"]');
+        $nodeList = $this->getXpath()->query($this->getXpathPrefix() . '/atom:link[@rel="enclosure"]');
 
         if ($nodeList->length > 0) {
             $enclosure = new stdClass();
@@ -262,7 +262,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['id'];
         }
 
-        $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)');
+        $id = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)');
 
         if (!$id) {
             if ($this->getPermalink()) {
@@ -290,12 +290,12 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['baseUrl'];
         }
 
-        $baseUrl = $this->_xpath->evaluate('string('
+        $baseUrl = $this->getXpath()->evaluate('string('
             . $this->getXpathPrefix() . '/@xml:base[1]'
         . ')');
 
         if (!$baseUrl) {
-            $baseUrl = $this->_xpath->evaluate('string(//@xml:base[1])');
+            $baseUrl = $this->getXpath()->evaluate('string(//@xml:base[1])');
         }
 
         if (!$baseUrl) {
@@ -339,7 +339,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $links = array();
 
-        $list = $this->_xpath->query(
+        $list = $this->getXpath()->query(
             $this->getXpathPrefix() . '//atom:link[@rel="alternate"]/@href' . '|' .
             $this->getXpathPrefix() . '//atom:link[not(@rel)]/@href'
         );
@@ -376,7 +376,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['title'];
         }
 
-        $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)');
+        $title = $this->getXpath()->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)');
 
         if (!$title) {
             $title = null;
@@ -402,8 +402,8 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $count = null;
 
-        $this->_xpath->registerNamespace('thread10', 'http://purl.org/syndication/thread/1.0');
-        $list = $this->_xpath->query(
+        $this->getXpath()->registerNamespace('thread10', 'http://purl.org/syndication/thread/1.0');
+        $list = $this->getXpath()->query(
             $this->getXpathPrefix() . '//atom:link[@rel="replies"]/@thread10:count'
         );
 
@@ -429,7 +429,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $link = null;
 
-        $list = $this->_xpath->query(
+        $list = $this->getXpath()->query(
             $this->getXpathPrefix() . '//atom:link[@rel="replies" and @type="text/html"]/@href'
         );
 
@@ -456,7 +456,7 @@ class Zend_Feed_Reader_Extension_Atom_Entry
 
         $link = null;
 
-        $list = $this->_xpath->query(
+        $list = $this->getXpath()->query(
             $this->getXpathPrefix() . '//atom:link[@rel="replies" and @type="application/'.$type.'+xml"]/@href'
         );
 
@@ -481,16 +481,16 @@ class Zend_Feed_Reader_Extension_Atom_Entry
             return $this->_data['categories'];
         }
 
-        if ($this->getType() == Zend_Feed_Reader::TYPE_ATOM_10) {
-            $list = $this->_xpath->query($this->getXpathPrefix() . '//atom:category');
+        if ($this->_getAtomType() == Zend_Feed_Reader::TYPE_ATOM_10) {
+            $list = $this->getXpath()->query($this->getXpathPrefix() . '//atom:category');
         } else {
             /**
              * Since Atom 0.3 did not support categories, it would have used the
              * Dublin Core extension. However there is a small possibility Atom 0.3
              * may have been retrofittied to use Atom 1.0 instead.
              */
-            $this->_xpath->registerNamespace('atom10', Zend_Feed_Reader::NAMESPACE_ATOM_10);
-            $list = $this->_xpath->query($this->getXpathPrefix() . '//atom10:category');
+            $this->getXpath()->registerNamespace('atom10', Zend_Feed_Reader::NAMESPACE_ATOM_10);
+            $list = $this->getXpath()->query($this->getXpathPrefix() . '//atom10:category');
         }
 
         if ($list->length) {
@@ -523,8 +523,9 @@ class Zend_Feed_Reader_Extension_Atom_Entry
         }
         
         $source = null;
+        // TODO: Investigate why _getAtomType() fails here. Is it even needed?
         if ($this->getType() == Zend_Feed_Reader::TYPE_ATOM_10) {
-            $list = $this->_xpath->query($this->getXpathPrefix() . '/atom:source[1]');
+            $list = $this->getXpath()->query($this->getXpathPrefix() . '/atom:source[1]');
             if ($list->length) {
                 $element = $list->item(0);
                 $source = new Zend_Feed_Reader_Feed_Atom_Source($element, $this->getXpathPrefix());
@@ -596,18 +597,12 @@ class Zend_Feed_Reader_Extension_Atom_Entry
      */
     protected function _registerNamespaces()
     {
-        if ($this->getType() == Zend_Feed_Reader::TYPE_ATOM_10
-            || $this->getType() == Zend_Feed_Reader::TYPE_ATOM_03
-        ) {
-            return; // pre-registered at Feed level
-        }
-        $atomDetected = $this->_getAtomType();
-        switch ($atomDetected) {
+        switch ($this->_getAtomType()) {
             case Zend_Feed_Reader::TYPE_ATOM_03:
-                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_03);
+                $this->getXpath()->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_03);
                 break;
             default:
-                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_10);
+                $this->getXpath()->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_10);
                 break;
         }
     }
@@ -617,17 +612,16 @@ class Zend_Feed_Reader_Extension_Atom_Entry
      */
     protected function _getAtomType()
     {
-        $nslist = $this->getDomDocument()->documentElement->attributes;
-        if (!$nslist->length) {
-            return null;
-        }
-        foreach ($nslist as $ns) {
-            if ($ns->value == Zend_Feed_Reader::NAMESPACE_ATOM_10) {
-                return Zend_Feed_Reader::TYPE_ATOM_10;
-            }
-            if ($ns->value == Zend_Feed_Reader::NAMESPACE_ATOM_03) {
-                return Zend_Feed_Reader::TYPE_ATOM_03;
-            }
+        $dom = $this->getDomDocument();
+        $prefixAtom03 = $dom->lookupPrefix(Zend_Feed_Reader::NAMESPACE_ATOM_03);
+        $prefixAtom10 = $dom->lookupPrefix(Zend_Feed_Reader::NAMESPACE_ATOM_10);
+        if ($dom->isDefaultNamespace(Zend_Feed_Reader::NAMESPACE_ATOM_10)
+        || !empty($prefixAtom10)) {
+            return Zend_Feed_Reader::TYPE_ATOM_10;
+        }
+        if ($dom->isDefaultNamespace(Zend_Feed_Reader::NAMESPACE_ATOM_03)
+        || !empty($prefixAtom03)) {
+            return Zend_Feed_Reader::TYPE_ATOM_03;
         }
     }
 }

+ 10 - 11
library/Zend/Feed/Reader/Extension/Atom/Feed.php

@@ -541,17 +541,16 @@ class Zend_Feed_Reader_Extension_Atom_Feed
      */
     protected function _getAtomType()
     {
-        $nslist = $this->getDomDocument()->documentElement->attributes;
-        if (!$nslist->length) {
-            return null;
-        }
-        foreach ($nslist as $ns) {
-            if ($ns->value == Zend_Feed_Reader::NAMESPACE_ATOM_10) {
-                return Zend_Feed_Reader::TYPE_ATOM_10;
-            }
-            if ($ns->value == Zend_Feed_Reader::NAMESPACE_ATOM_03) {
-                return Zend_Feed_Reader::TYPE_ATOM_03;
-            }
+        $dom = $this->getDomDocument();
+        $prefixAtom03 = $dom->lookupPrefix(Zend_Feed_Reader::NAMESPACE_ATOM_03);
+        $prefixAtom10 = $dom->lookupPrefix(Zend_Feed_Reader::NAMESPACE_ATOM_10);
+        if ($dom->isDefaultNamespace(Zend_Feed_Reader::NAMESPACE_ATOM_10)
+        || !empty($prefixAtom10)) {
+            return Zend_Feed_Reader::TYPE_ATOM_10;
+        }
+        if ($dom->isDefaultNamespace(Zend_Feed_Reader::NAMESPACE_ATOM_03)
+        || !empty($prefixAtom03)) {
+            return Zend_Feed_Reader::TYPE_ATOM_03;
         }
     }
 }

+ 4 - 1
library/Zend/Feed/Reader/Extension/EntryAbstract.php

@@ -86,7 +86,7 @@ abstract class Zend_Feed_Reader_Extension_EntryAbstract
         if (!is_null($type)) {
             $this->_data['type'] = $type;
         } else {
-            $this->_data['type'] = Zend_Feed_Reader::detectType($feed);
+            $this->_data['type'] = Zend_Feed_Reader::detectType($entry->ownerDocument, true);
         }
         // set the XPath query prefix for the entry being queried
         if ($this->getType() == Zend_Feed_Reader::TYPE_RSS_10
@@ -153,6 +153,9 @@ abstract class Zend_Feed_Reader_Extension_EntryAbstract
      */
     public function getXpath()
     {
+        if (!$this->_xpath) {
+            $this->setXpath(new DOMXPath($this->getDomDocument()));
+        }
         return $this->_xpath;
     }
 

+ 3 - 0
tests/Zend/Feed/AllTests.php

@@ -54,6 +54,7 @@ require_once 'Zend/Feed/Reader/Feed/CommonTest.php';
 require_once 'Zend/Feed/Reader/Entry/CommonTest.php';
 
 require_once 'Zend/Feed/Reader/Feed/AtomSourceTest.php';
+require_once 'Zend/Feed/Reader/Entry/AtomStandaloneEntryTest.php';
 
 require_once 'Zend/Feed/Reader/Integration/WordpressRss2DcAtomTest.php';
 require_once 'Zend/Feed/Reader/Integration/WordpressAtom10Test.php';
@@ -105,6 +106,8 @@ class Zend_Feed_AllTests
         $suite->addTestSuite('Zend_Feed_Reader_Entry_CommonTest');
         // ATOM - Entry Level (Source Feed Metadata)
         $suite->addTestSuite('Zend_Feed_Reader_Feed_AtomSourceTest');
+        // ATOM - Entry Level (Standalone Entry Documents)
+        $suite->addTestSuite('Zend_Feed_Reader_Entry_AtomStandaloneEntryTest');
         /**
          * Real World Feed Tests
          */

+ 2 - 0
tests/Zend/Feed/Reader/Entry/AtomTest.php

@@ -43,6 +43,7 @@ class Zend_Feed_Reader_Entry_AtomTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);
@@ -93,6 +94,7 @@ class Zend_Feed_Reader_Entry_AtomTest extends PHPUnit_Framework_TestCase
 
     /**
      * Get Id (Unencoded Text)
+     * @group ZFR003
      */
     public function testGetsIdFromAtom03()
     {

+ 1 - 0
tests/Zend/Feed/Reader/Entry/CommonTest.php

@@ -39,6 +39,7 @@ class Zend_Feed_Reader_Entry_CommonTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);

+ 1 - 0
tests/Zend/Feed/Reader/Entry/RssTest.php

@@ -45,6 +45,7 @@ class Zend_Feed_Reader_Entry_RssTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);

+ 1 - 1
tests/Zend/Feed/Reader/Entry/_files/Atom/id/plain/atom03.xml

@@ -3,4 +3,4 @@
     <entry>
         <id>1</id>
     </entry>
-</feed>
+</feed>

+ 1 - 0
tests/Zend/Feed/Reader/Feed/AtomSourceTest.php

@@ -45,6 +45,7 @@ class Zend_Feed_Reader_Feed_AtomSourceTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);

+ 1 - 0
tests/Zend/Feed/Reader/Feed/AtomTest.php

@@ -45,6 +45,7 @@ class Zend_Feed_Reader_Feed_AtomTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);

+ 1 - 0
tests/Zend/Feed/Reader/Feed/CommonTest.php

@@ -40,6 +40,7 @@ class Zend_Feed_Reader_Feed_CommonTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);

+ 1 - 0
tests/Zend/Feed/Reader/Feed/RssTest.php

@@ -46,6 +46,7 @@ class Zend_Feed_Reader_Feed_RssTest extends PHPUnit_Framework_TestCase
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         if (Zend_Registry::isRegistered('Zend_Locale')) {
             $registry = Zend_Registry::getInstance();
             unset($registry['Zend_Locale']);

+ 1 - 0
tests/Zend/Feed/Reader/Integration/H-OnlineComAtom10Test.php

@@ -39,6 +39,7 @@ class Zend_Feed_Reader_Integration_HOnlineComAtom10Test extends PHPUnit_Framewor
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         $this->_feedSamplePath = dirname(__FILE__) . '/_files/h-online.com-atom10.xml';
         $this->_options = Zend_Date::setOptions();
         foreach($this->_options as $k=>$v) {

+ 1 - 0
tests/Zend/Feed/Reader/Integration/LautDeRdfTest.php

@@ -39,6 +39,7 @@ class Zend_Feed_Reader_Integration_LautDeRdfTest extends PHPUnit_Framework_TestC
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         $this->_feedSamplePath = dirname(__FILE__) . '/_files/laut.de-rdf.xml';
         $this->_options = Zend_Date::setOptions();
         foreach($this->_options as $k=>$v) {

+ 1 - 0
tests/Zend/Feed/Reader/Integration/PodcastRss2Test.php

@@ -40,6 +40,7 @@ class Zend_Feed_Reader_Integration_PodcastRss2Test extends PHPUnit_Framework_Tes
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         $this->_feedSamplePath = dirname(__FILE__) . '/_files/podcast.xml';
         $this->_options = Zend_Date::setOptions();
         foreach($this->_options as $k=>$v) {

+ 1 - 0
tests/Zend/Feed/Reader/Integration/WordpressAtom10Test.php

@@ -39,6 +39,7 @@ class Zend_Feed_Reader_Integration_WordpressAtom10Test extends PHPUnit_Framework
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         $this->_feedSamplePath = dirname(__FILE__) . '/_files/wordpress-atom10.xml';
         $this->_options = Zend_Date::setOptions();
         foreach($this->_options as $k=>$v) {

+ 1 - 0
tests/Zend/Feed/Reader/Integration/WordpressRss2DcAtomTest.php

@@ -39,6 +39,7 @@ class Zend_Feed_Reader_Integration_WordpressRss2DcAtomTest extends PHPUnit_Frame
 
     public function setup()
     {
+        Zend_Feed_Reader::reset();
         $this->_feedSamplePath = dirname(__FILE__) . '/_files/wordpress-rss2-dc-atom.xml';
     }