Browse Source

[REVIEW] Promoted Zend_Feed_Reader to trunk

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@16718 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew 16 years ago
parent
commit
6289bc49a8
100 changed files with 10622 additions and 0 deletions
  1. 1 0
      documentation/manual/en/manual.xml.in
  2. 1321 0
      documentation/manual/en/module_specs/Zend_Feed_Reader.xml
  3. 690 0
      library/Zend/Feed/Reader.php
  4. 215 0
      library/Zend/Feed/Reader/Entry/Abstract.php
  5. 355 0
      library/Zend/Feed/Reader/Entry/Atom.php
  6. 139 0
      library/Zend/Feed/Reader/Entry/Interface.php
  7. 625 0
      library/Zend/Feed/Reader/Entry/Rss.php
  8. 479 0
      library/Zend/Feed/Reader/Extension/Atom/Entry.php
  9. 441 0
      library/Zend/Feed/Reader/Extension/Atom/Feed.php
  10. 64 0
      library/Zend/Feed/Reader/Extension/Content/Entry.php
  11. 97 0
      library/Zend/Feed/Reader/Extension/CreativeCommons/Entry.php
  12. 89 0
      library/Zend/Feed/Reader/Extension/CreativeCommons/Feed.php
  13. 232 0
      library/Zend/Feed/Reader/Extension/DublinCore/Entry.php
  14. 265 0
      library/Zend/Feed/Reader/Extension/DublinCore/Feed.php
  15. 197 0
      library/Zend/Feed/Reader/Extension/EntryAbstract.php
  16. 166 0
      library/Zend/Feed/Reader/Extension/FeedAbstract.php
  17. 144 0
      library/Zend/Feed/Reader/Extension/Slash/Entry.php
  18. 168 0
      library/Zend/Feed/Reader/Extension/Syndication/Feed.php
  19. 91 0
      library/Zend/Feed/Reader/Extension/Thread/Entry.php
  20. 73 0
      library/Zend/Feed/Reader/Extension/WellFormedWeb/Entry.php
  21. 260 0
      library/Zend/Feed/Reader/Feed/Abstract.php
  22. 337 0
      library/Zend/Feed/Reader/Feed/Atom.php
  23. 115 0
      library/Zend/Feed/Reader/Feed/Interface.php
  24. 544 0
      library/Zend/Feed/Reader/Feed/Rss.php
  25. 29 0
      tests/Zend/Feed/AllTests.php
  26. 226 0
      tests/Zend/Feed/Reader/Entry/AtomTest.php
  27. 2563 0
      tests/Zend/Feed/Reader/Entry/RssTest.php
  28. 45 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/author/plain/atom03.xml
  29. 45 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/author/plain/atom10.xml
  30. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/content/plain/atom03.xml
  31. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/content/plain/atom10.xml
  32. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/datecreated/plain/atom03.xml
  33. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/datecreated/plain/atom10.xml
  34. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/datemodified/plain/atom03.xml
  35. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/datemodified/plain/atom10.xml
  36. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/description/plain/atom03.xml
  37. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/description/plain/atom10.xml
  38. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/id/plain/atom03.xml
  39. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/id/plain/atom10.xml
  40. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/link/plain/atom03.xml
  41. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/link/plain/atom10.xml
  42. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/links/plain/atom03.xml
  43. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/links/plain/atom10.xml
  44. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/title/plain/atom03.xml
  45. 6 0
      tests/Zend/Feed/Reader/Entry/_files/Atom/title/plain/atom10.xml
  46. 12 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss090.xml
  47. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss091.xml
  48. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss092.xml
  49. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss093.xml
  50. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss094.xml
  51. 12 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss10.xml
  52. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss20.xml
  53. 12 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss090.xml
  54. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss091.xml
  55. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss092.xml
  56. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss093.xml
  57. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss094.xml
  58. 12 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss10.xml
  59. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss20.xml
  60. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss090.xml
  61. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss091.xml
  62. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss092.xml
  63. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss093.xml
  64. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss094.xml
  65. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss10.xml
  66. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss20.xml
  67. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss090.xml
  68. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss091.xml
  69. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss092.xml
  70. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss093.xml
  71. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss094.xml
  72. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss10.xml
  73. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss20.xml
  74. 12 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss090.xml
  75. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss091.xml
  76. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss092.xml
  77. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss093.xml
  78. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss094.xml
  79. 12 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss10.xml
  80. 10 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss20.xml
  81. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss090.xml
  82. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss091.xml
  83. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss092.xml
  84. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss093.xml
  85. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss094.xml
  86. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss10.xml
  87. 7 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss20.xml
  88. 11 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss090.xml
  89. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss091.xml
  90. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss092.xml
  91. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss093.xml
  92. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss094.xml
  93. 11 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss10.xml
  94. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss20.xml
  95. 11 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss090.xml
  96. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss091.xml
  97. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss092.xml
  98. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss093.xml
  99. 9 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss094.xml
  100. 11 0
      tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss10.xml

+ 1 - 0
documentation/manual/en/manual.xml.in

@@ -193,6 +193,7 @@
         <xi:include href="module_specs/Zend_Feed-ConsumingAtomSingle.xml" />
         <xi:include href="module_specs/Zend_Feed-ModifyingFeed.xml" />
         <xi:include href="module_specs/Zend_Feed-CustomFeed.xml" />
+        <xi:include href="module_specs/Zend_Feed_Reader.xml" />
     </chapter>
 
     <chapter id="zend.file">

+ 1321 - 0
documentation/manual/en/module_specs/Zend_Feed_Reader.xml

@@ -0,0 +1,1321 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<sect1 id="zend.feed.reader">
+    <title>Zend_Feed_Reader</title>
+
+    <sect2 id="zend.feed.reader.introduction">
+        <title>Introduction</title>
+
+        <para>
+            <classname>Zend_Feed_Reader</classname> is a component used to
+            consume RSS and Atom feeds of any version, including RDF/RSS 1.0,
+            RSS 2.0 and Atom 0.3/1.0. The API for retrieving feed data is
+            deliberately simple since <classname>Zend_Feed_Reader</classname> is
+            capable of searching any feed of any type for the information
+            requested through the API. If the typical elements containing this
+            information are not present, it will adapt and fall back on a
+            variety of alternative elements instead. This ability to choose from
+            alternatives removes the need for users to create their own
+            abstraction layer on top of the component to make it useful or have
+            any in-depth knowledge of the underlying standards, current
+            alternatives, and namespaced extensions.
+        </para>
+
+        <para>
+            Internally, <classname>Zend_Feed_Reader</classname> works almost
+            entirely on the basis of making XPath queries against the feed XML's
+            Document Object Model. The DOM is not exposed though a chained
+            property API like <classname>Zend_Feed</classname> though the
+            underlying <classname>DOMDocument</classname>,
+            <classname>DOMElement</classname> and
+            <classname>DOMXPath</classname> objects are exposed for external
+            manipulation. This singular approach to parsing is consistent and
+            the component offers a plugin system to add to the Feed and Entry
+            level API by writing Extensions on a similar basis.
+        </para>
+
+        <para>
+            Performance is assisted in three ways. First of all,
+            <classname>Zend_Feed_Reader</classname> supports caching using
+            <classname>Zend_Cache</classname> to maintain a copy of the original
+            feed XML. This allows you to skip network requests for a feed URI if
+            the cache is valid. Second, the Feed and Entry level API is backed
+            by an internal cache (non-persistant) so repeat API calls for the
+            same feed will avoid additional DOM/XPath use. Thirdly, importing
+            feeds from a URI can take advantage of HTTP Conditional GET requests
+            which allow servers to issue an empty 304 response when the
+            requested feed has not changed since the last time you requested it.
+            In the final case, an instance of <classname>Zend_Cache</classname>
+            will hold the last received feed along with the ETag and
+            Last-Modified header values sent in the HTTP response.
+        </para>
+
+        <para>
+            In relation to <classname>Zend_Feed</classname>,
+            <classname>Zend_Feed_Reader</classname> was formulated as a free
+            standing replacement for <classname>Zend_Feed</classname> but it is
+            not backwards compatible with <classname>Zend_Feed</classname>.
+            Rather it is an alternative following a different ideology focused
+            on being simple to use, flexible, consistent and extendable through
+            the plugin system. <classname>Zend_Feed_Reader</classname> is also
+            not capable of constructing feeds through this will be addressed at
+            a future date.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.feed.reader.import">
+        <title>Importing Feeds</title>
+
+        <para>
+            Importing a feed with <classname>Zend_Feed_Reader</classname> is not
+            that much different to <classname>Zend_Feed</classname>. Feeds can
+            be imported from a string, file, URI or an instance of type
+            <classname>Zend_Feed_Abstract</classname>. Importing from a URI can
+            additionally utilise a HTTP Conditional GET request. If importing
+            fails, an exception will be raised. The end result will be an object
+            of type <classname>Zend_Feed_Reader_FeedInterface</classname>, the
+            core implementations of which are
+            <classname>Zend_Feed_Reader_Feed_Rss</classname> and
+            <classname>Zend_Feed_Reader_Feed_Atom</classname>
+            (<classname>Zend_Feed</classname> took all the short names!). Both
+            objects support multiple (all existing) versions of these broad feed
+            types.
+        </para>
+
+        <para>
+            In the following example, we import an RDF/RSS 1.0 feed and extract
+            some basic information that can be saved to a database or
+            elsewhere.
+        </para>
+
+        <programlisting role="php"><![CDATA[
+$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
+$data = array(
+    'title'        => $feed->getTitle(),
+    'link'         => $feed->getLink(),
+    'dateModified' => $feed->getDateModified(),
+    'description'  => $feed->getDescription(),
+    'language'     => $feed->getLanguage(),
+    'entries'      => array(),
+);
+
+foreach ($feed as $entry) {
+    $edata = array(
+        'title'        => $entry->getTitle(),
+        'description'  => $entry->getDescription(),
+        'dateModified' => $entry->getDateModified(),
+        'author'       => $entry->getAuthor(),
+        'link'         => $entry->getLink(),
+        'content'      => $entry->getContent()
+    );
+    $data['entries'][] = $edata;
+}
+]]></programlisting>
+
+        <para>
+            The example above demonstrates
+            <classname>Zend_Feed_Reader</classname>'s API, and it also
+            demonstrates some of it's internal operation. In reality, the RDF
+            feed selected does not have any native date or author elements,
+            however it does utilise the Dublin Core 1.1 module which offers
+            namespaced creator and date elements.
+            <classname>Zend_Feed_Reader</classname> falls back on these and
+            similar options if no relevant native elements exist. If it
+            absolutely cannot find an alternative it will return null,
+            indicating the information could not be found in the feed. You
+            should note that classes implementing
+            <classname>Zend_Feed_Reader_FeedInterface</classname> also implement
+            the SPL <classname>Iterator</classname> and
+            <classname>Countable</classname> interfaces.
+        </para>
+
+        <para>
+            Feeds can also be imported from strings, files, and even objects of
+            type <classname>Zend_Feed_Abstract</classname>.
+        </para>
+
+        <programlisting role="php"><![CDATA[
+// from a URI
+$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
+
+// from a String
+$feed = Zend_Feed_Reader::importString($feedXmlString);
+
+// from a file
+$feed = Zend_Feed_Reader::importFile('./feed.xml');
+
+// from a Zend_Feed_Abstract object
+$zfeed = Zend_Feed::import('http://www.planet-php.net/atom/');
+$feed  = Zend_Feed_Reader::importFeed($zfeed);
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="zend.feed.reader.sources">
+        <title>Retrieving Underlying Feed and Entry Sources</title>
+
+        <para>
+            <classname>Zend_Feed_Reader</classname> does it's best not to stick
+            you in a narrow confine. If you need to work on a feed outside of
+            <classname>Zend_Feed_Reader</classname>, you can extract the base
+            <classname>DOMDocument</classname> or
+            <classname>DOMElement</classname> objects from any class, or even an
+            XML string containing these. Also provided are methods to extract
+            the current <classname>DOMXPath</classname> object (with all core
+            and Extension namespaces registered) and the correct prefix used in
+            all XPath queries for the current Feed or Entry. The basic methods
+            to use (on any object) are <methodname>saveXml()</methodname>,
+            <methodname>getDomDocument()</methodname>,
+            <methodname>getElement()</methodname>,
+            <methodname>getXpath()</methodname> and
+            <methodname>getXpathPrefix()</methodname>. These will let you break
+            free of <classname>Zend_Feed_Reader</classname> and do whatever else
+            you want.
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <methodname>saveXml()</methodname> returns an XML string
+                    containing only the element representing the current object.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <methodname>getDomDocument()</methodname> returns the
+                    <classname>DOMDocument</classname> object representing the
+                    entire feed (even if called from an Entry object).
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <methodname>getElement()</methodname> returns the
+                    <classname>DOMElement</classname> of the current object
+                    (i.e.  the Feed or current Entry).
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <methodname>getXpath()</methodname> returns the
+                    <classname>DOMXPath</classname> object for the current feed
+                    (even if called from an Entry object) with the namespaces of
+                    the current feed type and all loaded Extensions
+                    pre-registered.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <methodname>getXpathPrefix()</methodname> returns the query
+                    prefix for the current object (i.e. the Feed or current
+                    Entry) which includes the correct XPath query path for that
+                    specific Feed or Entry.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            Here's an example where a feed might include an RSS Extension not
+            supported by <classname>Zend_Feed_Reader</classname> out of the box.
+            Notably, you could write and register an Extension (covered later)
+            to do this, but that's not always warranted for a quick check. You
+            must register any new namespaces on the
+            <classname>DOMXPath</classname> object before use unless they are
+            registered by <classname>Zend_Feed_Reader</classname> or an
+            Extension beforehand.
+        </para>
+
+        <programlisting role="php"><![CDATA[
+$feed        = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
+$xpathPrefix = $feed->getXpathPrefix();
+$xpath       = $feed->getXpath();
+$xpath->registerNamespace('admin', 'http://webns.net/mvcb/');
+$reportErrorsTo = $xpath->evaluate('string(' . $xpathPrefix . '/admin:errorReportsTo)');
+]]></programlisting>
+
+        <warning>
+            <para>
+                If you register an already registered namespace with a different
+                prefix name to that used internally by
+                <classname>Zend_Feed_Reader</classname>, it will break the
+                internal operation of this component.
+            </para>
+        </warning>
+    </sect2>
+
+    <sect2 id="zend.feed.reader.cache-request">
+        <title>Cache Support and Intelligent Requests</title>
+
+        <sect3 id="zend.feed.reader.cache-request.cache">
+            <title>Adding Cache Support to Zend_Feed_Reader</title>
+
+            <para>
+                <classname>Zend_Feed_Reader</classname> supports using an
+                instance of <classname>Zend_Cache</classname> to cache feeds (as
+                XML) to avoid unnecessary network requests. Adding a cache is as
+                simple here as it is for other Zend Framework components, create
+                and configure your cache and then tell
+                <classname>Zend_Feed_Reader</classname> to use it! The cache key
+                used is "Zend_Feed_Reader_" followed by the MD5 hash of the
+                feed's URI.
+            </para>
+
+            <programlisting role="php"><![CDATA[
+$frontendOptions = array(
+   'lifetime' => 7200,
+   'automatic_serialization' => true
+);
+$backendOptions = array('cache_dir' => './tmp/');
+$cache = Zend_Cache::factory(
+    'Core', 'File', $frontendOptions, $backendOptions
+);
+
+Zend_Feed_Reader::setCache($cache);
+]]></programlisting>
+
+            <note>
+                <para>
+                    While it's a little off track, you should also consider
+                    adding a cache to
+                    <classname>Zend_Loader_PluginLoader</classname> which is
+                    used by <classname>Zend_Feed_Reader</classname> to load
+                    Extensions.
+                </para>
+            </note>
+        </sect3>
+
+        <sect3 id="zend.feed.reader.cache-request.http-conditional-get">
+            <title>HTTP Conditional GET Support</title>
+
+            <para>
+                The big question often asked when importing a feed frequently, is
+                if it has even changed. With a cache enabled, you can add HTTP
+                Conditional GET support to your arsenal to answer that question.
+            </para>
+
+            <para>
+                Using this method, you can request feeds from URIs and include
+                their last known ETag and Last-Modified response header values
+                with the request (using the If-None-Match and If-Modified-Since
+                headers). If the feed on the server remains unchanged, you
+                should receive a 304 response which tells
+                <classname>Zend_Feed_Reader</classname> to use the cached
+                version. If a full feed is sent in a response with a status code
+                of 200, this means the feed has changed and
+                <classname>Zend_Feed_Reader</classname> will parse the new
+                version and save it to the cache. It will also cache the new
+                ETag and Last-Modified header values for future use.
+            </para>
+
+            <para>
+                These "conditional" requests are not guaranteed to be supported
+                by the server you request a URI of, but can be attempted
+                regardless. Most common feed sources like blogs should however
+                have this supported. To enable conditional requests, you will
+                need to provide a cache to <classname>Zend_Feed_Reader</classname>.
+            </para>
+
+            <programlisting role="php"><![CDATA[
+$frontendOptions = array(
+   'lifetime' => 86400,
+   'automatic_serialization' => true
+);
+$backendOptions = array('cache_dir' => './tmp/');
+$cache = Zend_Cache::factory(
+    'Core', 'File', $frontendOptions, $backendOptions
+);
+
+Zend_Feed_Reader::setCache($cache);
+Zend_Feed_Reader::useHttpConditionalGet();
+
+$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
+]]></programlisting>
+
+            <para>
+                In the example above, with HTTP Conditional GET requests enabled,
+                the response header values for ETag and Last-Modified will be cached
+                along with the feed. For the next 24hrs (the cache lifetime), feeds will
+                only be updated on the cache if a non-304 response is received
+                containing a valid RSS or Atom XML document.
+            </para>
+
+            <para>
+                If you intend on managing request headers from outside
+                <classname>Zend_Feed_Reader</classname>, you can set the
+                relevant If-None-Matches and If-Modified-Since request headers
+                via the URI import method.
+            </para>
+
+            <programlisting role="php"><![CDATA[
+$lastEtagReceived = '5e6cefe7df5a7e95c8b1ba1a2ccaff3d';
+$lastModifiedDateReceived = 'Wed, 08 Jul 2009 13:37:22 GMT';
+$feed = Zend_Feed_Reader::import(
+    $uri, $lastEtagReceived, $lastModifiedDateReceived
+);
+]]></programlisting>
+        </sect3>
+    </sect2>
+
+    <sect2 id="zend.feed.reader.locate">
+        <title>Locating Feed URIs from Websites</title>
+
+        <para>
+            These days, many websites are aware that the location of their XML
+            feeds is not always obvious. A small RDF, RSS or Atom graphic helps
+            when the user is reading the page, but what about when a machine
+            visits trying to identify where your feeds are located? To assist in
+            this, websites may point to their feeds using &lt;link&gt; tags in
+            the &lt;head&gt; section of their HTML. To take advantage of this,
+            you can use <classname>Zend_Feed_Reader</classname> to locate these
+            feeds using the static <methodname>findFeedLinks()</methodname>
+            method.
+        </para>
+
+        <para>
+            This method calls any URI and searches for the location of RSS, RDF
+            and Atom feeds assuming the wlebsite's HTML contains the relevant links.
+            It then returns a value object where you can check for the existence of a
+            RSS, RDF or Atom feed URI.
+        </para>
+
+        <programlisting role="php"><![CDATA[
+$links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');
+
+if(isset($links->rdf)) {
+    echo $links->rdf, "\n"; // http://www.planet-php.org/rdf/
+}
+if(isset($links->rss)) {
+    echo $links->rss, "\n"; // http://www.planet-php.org/rss/
+}
+if(isset($links->atom)) {
+    echo $links->atom, "\n"; // http://www.planet-php.org/atom/
+}
+]]></programlisting>
+
+        <para>
+            Based on these links, you can then import from whichever source you
+            wish in the usual manner.
+        </para>
+  </sect2>
+
+  <sect2 id="zend.feed.reader.retrieve-info">
+        <title>Retrieving Feed Information</title>
+
+        <para>
+            Retrieving information from a feed (we'll cover entries/items in the
+            next section though they follow identical principals) uses a clearly
+            defined API which is exactly the same regardless of whether the feed
+            in question is RSS/RDF/Atom. The same goes for sub-versions of these
+            standards and we've tested every single RSS and Atom version. While
+            the underlying feed XML can differ substantially in terms of the
+            tags and elements they present, they nonetheless are all trying to
+            convey similar information and to reflect this all the differences
+            and wrangling over alternative tags are handled internally by
+            <classname>Zend_Feed_Reader</classname> presenting you with an
+            identical interface for each. Ideally, you should not have to care
+            whether a feed is RSS or Atom so long as you can extract the
+            information you want.
+        </para>
+
+        <para>
+            Of course, we don't live in an ideal world so there may be times the
+            API just does not cover what you're looking for. To assist you,
+            <classname>Zend_Feed_Reader</classname> offers a plugin system which
+            allows you to write Extensions to expand the core API and cover any
+            additional data you are trying to extract from feeds. If writing
+            another Extension is too much trouble, you can simply grab the
+            underlying DOM or XPath objects and do it by hand in your
+            application. Of course, we really do encourage writing an Extension
+            simply to make it more portable and reusable.
+        </para>
+
+        <para>
+            Here's a summary of the Core API for Feeds. You should note it
+            comprises not only the basic RSS and Atom standards, but also
+            accounts for a number of included Extensions bundled with
+            <classname>Zend_Feed_Reader</classname>. The naming of these
+            Extension sourced methods remain fairly generic - all Extension
+            methods operate at the same level as the Core API though we do allow
+            you to retrieve any specific Extension object separately if
+            required.
+        </para>
+
+        <table>
+            <title>Feed Level API Methods</title>
+
+            <tgroup cols="2">
+                <tbody>
+                    <row>
+                        <entry><methodname>getId()</methodname></entry>
+
+                        <entry>Returns a unique ID associated with this feed</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getTitle()</methodname></entry>
+
+                        <entry>Returns the title of the feed</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getDescription()</methodname></entry>
+
+                        <entry>Returns the text description of the feed</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getLink()</methodname></entry>
+
+                        <entry>
+                            Returns a URI to the HTML website containing the same or
+                            similar information as this feed (i.e. if the feed is from a blog,
+                            it should provide the blog's URI where the HTML version of the
+                            entries can be read).
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getFeedLink()</methodname></entry>
+
+                        <entry>
+                            Returns the URI of this feed, which should be the same as
+                            the URI used to import the feed
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getAuthors()</methodname></entry>
+
+                        <entry>
+                            Returns an array of all authors associated with this feed
+                            including email address in the author string if available
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getAuthor(integer $index = 0)</methodname></entry>
+
+                        <entry>
+                            Returns either the first author known, or with the
+                            optional <code>$index</code> parameter any specific
+                            index on the array of Authors (returning null if an
+                            invalid index).
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getDateCreated()</methodname></entry>
+
+                        <entry>
+                            Returns the date on which this feed was created. Generally
+                            only applicable to Atom where it represents the date the resource
+                            described by an Atom 1.0 document was created.
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getDateModified()</methodname></entry>
+
+                        <entry>
+                            Returns the date on which this feed was last modified
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getLanguage()</methodname></entry>
+
+                        <entry>
+                            Returns the language of the feed (if defined) or simply the
+                            language noted in the XML document
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getGenerator()</methodname></entry>
+
+                        <entry>
+                            Returns the generator of the feed, e.g. the software which
+                            generated it. This may differ between RSS and Atom since Atom
+                            defines a different notation.
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getCopyright()</methodname></entry>
+
+                        <entry>
+                            Returns any copyright notice associated with the feed
+                        </entry>
+                    </row>
+        </tbody>
+      </tgroup>
+    </table>
+
+        <para>
+            Given the variety of feeds in the wild, some of these methods will
+            undoubtedly return NULL indicating the relevant information couldn't
+            be located. Where possible, <classname>Zend_Feed_Reader</classname>
+            will fall back on alternative elements during its search. For
+            example, searching an RSS feed for a modification date is more
+            complicated than it looks. RSS 2.0 feeds should include a
+            <code>&lt;lastBuildDate&gt;</code> tag and/or a
+            <code>&lt;pubDate&gt;</code> element. But what if it doesn't, maybe
+            this is an RSS 1.0 feed? Perhaps it instead has an
+            <code>&lt;atom:updated&gt;</code> element with identical information
+            (Atom may be used to supplement RSS's syntax)? Failing that, we
+            could simply look at the entries, pick the most recent, and use its
+            <code>&lt;pubDate&gt;</code> element.  Assuming it exists... Many
+            feeds also use Dublin Core 1.0/1.1 <code>&lt;dc:date&gt;</code>
+            elements for feeds/entries. Or we could find Atom lurking again.
+        </para>
+
+        <para>
+            The point is, <classname>Zend_Feed_Reader</classname> was designed
+            to know this. When you ask for the modification date (or anything
+            else), it will run off and search for all these alternatives until
+            it either gives up and returns <code>NULL</code>, or finds an
+            alternative that should have the right answer.
+        </para>
+
+        <para>
+            In addition to the above methods, all Feed objects implement methods
+            for retrieving the DOM and XPath objects for the current feeds as
+            described earlier. Feed objects also implement the SPL Iterator and
+            Countable interfaces. The extended API is summarised below.
+        </para>
+
+        <table>
+            <title>Extended Feed Level API Methods</title>
+
+            <tgroup cols="2">
+                <tbody>
+                    <row>
+                        <entry><methodname>getDomDocument()</methodname></entry>
+
+                        <entry>
+                            Returns the parent
+                            <classname>DOMDocument</classname> object for the
+                            entire source XML document
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getElement()</methodname></entry>
+
+                        <entry>
+                            Returns the current feed level
+                            <classname>DOMElement</classname> object
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>saveXml()</methodname></entry>
+
+                        <entry>
+                            Returns a string containing an XML document of the
+                            entire feed element (this is not the original
+                            document but a rebuilt version)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getXpath()</methodname></entry>
+
+                        <entry>
+                            Returns the <classname>DOMXPath</classname> object
+                            used internally to run queries on the
+                            <classname>DOMDocument</classname> object (this
+                            includes core and Extension namespaces
+                            pre-registered)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getXpathPrefix()</methodname></entry>
+
+                        <entry>
+                            Returns the valid DOM path prefix prepended to all XPath
+                            queries matching the feed being queried
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getEncoding()</methodname></entry>
+
+                        <entry>
+                            Returns the encoding of the source XML document
+                            (note: this cannot account for errors such as the
+                            server sending documents in a different encoding)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>count()</methodname></entry>
+
+                        <entry>
+                            Returns a count of the entries or items this feed contains
+                            (implements SPL <classname>Countable</classname> interface)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>current()</methodname></entry>
+
+                        <entry>
+                            Returns either the current entry (using the current index
+                            from <methodname>key()</methodname>)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>key()</methodname></entry>
+
+                        <entry>Returns the current entry index</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>next()</methodname></entry>
+
+                        <entry>Increments the entry index value by one</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>rewind()</methodname></entry>
+
+                        <entry>Resets the entry index to 0</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>valid()</methodname></entry>
+
+                        <entry>
+                            Checks that the current entry index is valid, i.e.
+                            it does fall below 0 and does not exceed the number
+                            of entries existing.
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getExtensions()</methodname></entry>
+
+                        <entry>
+                            Returns an array of non-Core Extension objects loaded for
+                            the current feed (note: both feed-level and entry-level Extensions
+                            exist, and only feed-level Extensions are returned here).
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getType()</methodname></entry>
+
+                        <entry>
+                            Returns a static class constant (e.g.
+                            <varname>Zend_Feed_Reader::TYPE_ATOM_03</varname>,
+                            i.e. Atom 0.3) indicating exactly what kind of feed
+                            is being consumed.
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+    </sect2>
+
+    <sect2 id="zend.feed.reader.entry">
+        <title>Retrieving Entry/Item Information</title>
+
+        <para>
+            Retrieving information for specific entries or items (depending on
+            whether you speak Atom or RSS) is identical to feed level data.
+            Accessing entries is simply a matter of iterating over a Feed object
+            or using the SPL <classname>Iterator</classname> interface Feed
+            objects implement and calling the appropriate method on each.
+        </para>
+
+        <table>
+            <title>Entry Level API Methods</title>
+
+            <tgroup cols="2">
+                <tbody>
+                    <row>
+                        <entry><methodname>getId()</methodname></entry>
+
+                        <entry>Returns a unique ID for the current entry</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getTitle()</methodname></entry>
+
+                        <entry>Returns the title of the current entry</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getDescription()</methodname></entry>
+
+                        <entry>Returns a description of the current entry</entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getLink()</methodname></entry>
+
+                        <entry>
+                            Returns a URI to the HTML version of the current
+                            entry
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getPermaLink()</methodname></entry>
+
+                        <entry>
+                            Returns the permanent link to the current entry
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getAuthors()</methodname></entry>
+
+                        <entry>
+                            Returns an array of all authors associated with this entry
+                            including email address in the author string if available
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getAuthor($index = 0)</methodname></entry>
+
+                        <entry>
+                            Returns either the first author known, or with the
+                            optional <code>$index</code> parameter any specific
+                            index on the array of Authors (returning null if an
+                            invalid index).
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getDateCreated()</methodname></entry>
+
+                        <entry>
+                            Returns the date on which the current entry was
+                            created.  Generally only applicable to Atom where it
+                            represents the date the resource described by an
+                            Atom 1.0 document was created.
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getDateModified()</methodname></entry>
+
+                        <entry>
+                            Returns the date on which the current entry was last
+                            modified
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getContent()</methodname></entry>
+
+                        <entry>
+                            Returns the content of the current entry (this has any
+                            entities reversed if possible assuming the content type is HTML).
+                            The description is returned if a separate content element does not
+                            exist.
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getCommentCount()</methodname></entry>
+
+                        <entry>
+                            Returns the number of comments made on this entry at the
+                            time the feed was last generated
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getCommentLink()</methodname></entry>
+
+                        <entry>
+                            Returns a URI pointing to the HTML page where comments can
+                            be made on this entry
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry>
+                            <methodname>getCommentFeedLink(string $type =
+                                'atom'|'rss')</methodname>
+                        </entry>
+
+            <entry>Returns a URI pointing to a feed of the provided type
+            containing all comments for this entry (type defaults to Atom/RSS
+            depending on current feed type).</entry>
+          </row>
+        </tbody>
+      </tgroup>
+    </table>
+
+        <para>
+            The extended API for entries is identical to that for feeds with the
+            exception of the Iterator methods which are not needed here.
+        </para>
+
+        <caution>
+            <para>
+                There is often confusion over the concepts of modified and
+                created dates. In Atom, these are two clearly defined concepts
+                (so knock yourself out) but in RSS they are vague. RSS 2.0
+                defines a single <emphasis>&lt;pubDate&gt;</emphasis> element
+                which typically refers to the date this entry was published,
+                i.e. a creation date of sorts. This is not always the case, and
+                it may change with updates or not. As a result, if you really
+                want to check whether an entry has changed, don't rely on the
+                results of <methodname>getDateModified()</methodname>. Instead,
+                consider tracking the MD5 hash of three other elements
+                concatenated, e.g. using <methodname>getTitle()</methodname>,
+                <methodname>getDescription()</methodname> and
+                <methodname>getContent()</methodname>. If the entry was trully
+                updated, this hash computation will give a different result than
+                previously saved hashes for the same entry. Further muddying the
+                waters, dates in feeds may follow different standards. Atom and
+                Dublin Core dates should follow ISO 8601, and RSS dates should
+                follow RFC 822 or RFC 2822 which is also common. Date methods
+                will throw an exception if <classname>Zend_Date</classname>
+                cannot load the date string using one of the above standards.
+            </para>
+        </caution>
+
+        <warning>
+            <para>
+                The values returned from these methods are not validated. This
+                means users must perform validation on all retrieved data
+                including the filtering of any HTML such as from
+                <methodname>getContent()</methodname> before it is output from
+                your application. Remember that most feeds come from external
+                sources, and therefore the default assumption should be that
+                they cannot be trusted.
+            </para>
+        </warning>
+
+        <table>
+            <title>Extended Entry Level API Methods</title>
+
+            <tgroup cols="2">
+                <tbody>
+                    <row>
+                        <entry><methodname>getDomDocument()</methodname></entry>
+
+                        <entry>
+                            Returns the parent
+                            <classname>DOMDocument</classname> object for the
+                            entire feed (not just the current entry)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getElement()</methodname></entry>
+
+                        <entry>
+                            Returns the current entry level
+                            <classname>DOMElement</classname> object
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getXpath()</methodname></entry>
+
+                        <entry>
+                            Returns the <classname>DOMXPath</classname> object
+                            used internally to run queries on the
+                            <classname>DOMDocument</classname> object (this
+                            includes core and Extension namespaces
+                            pre-registered)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getXpathPrefix()</methodname></entry>
+
+                        <entry>
+                            Returns the valid DOM path prefix prepended to all XPath
+                            queries matching the entry being queried
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getEncoding()</methodname></entry>
+
+                        <entry>
+                            Returns the encoding of the source XML document (note: this
+                            cannot account for errors such as the server sending documents in
+                            a different encoding)
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getExtensions()</methodname></entry>
+
+                        <entry>
+                            Returns an array of non-Core Extension objects loaded for
+                            the current entry (note: both feed-level and entry-level
+                            Extensions exist, and only entry-level Extensions are returned
+                            here).
+                        </entry>
+                    </row>
+
+                    <row>
+                        <entry><methodname>getType()</methodname></entry>
+
+                        <entry>
+                            Returns a static class constant (e.g.
+                            <varname>Zend_Feed_Reader::TYPE_ATOM_03</varname>,
+                            i.e. Atom 0.3) indicating exactly what kind
+                            of feed is being consumed.
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+    </sect2>
+
+    <sect2 id="zend.feed.reader.extending">
+        <title>Extending Feed and Entry APIs</title>
+
+        <para>
+            Extending <classname>Zend_Feed_Reader</classname> allows you to add
+            methods at both the feed and entry level which cover the retrieval
+            of information not already supported by
+            <classname>Zend_Feed_Reader</classname>. Given the number of RSS and
+            Atom extensions that exist, this is a good thing since
+            <classname>Zend_Feed_Reader</classname> couldn't possibly add
+            everything.
+        </para>
+
+        <para>
+            There are two types of Extensions possible, those which retrieve
+            information from elements which are immediate children of the root
+            element (e.g. <code>&lt;channel&gt;</code> for RSS or
+            <code>&lt;feed&gt;</code> for Atom) and those who retrieve
+            information from child elements of an entry (e.g.
+            <code>&lt;item&gt;</code> for RSS or <code>&lt;entry&gt;</code> for
+            Atom). On the filesystem these are grouped as classes within
+            a namespace based on the extension standard's name. For
+            example, internally we have <classname>Zend_Feed_Reader_Extension_DublinCore_Feed</classname>
+            and <classname>Zend_Feed_Reader_Extension_DublinCore_Entry</classname>
+            classes which are two Extensions implementing Dublin Core
+            1.0/1.1 support.
+        </para>
+
+        <para>
+            Extensions are loaded into <classname>Zend_Feed_Reader</classname>
+            using Zend_Loader_PluginLoader, so their operation will be familiar
+            from other Zend Framework components.
+            <classname>Zend_Feed_Reader</classname> already bundles a number of
+            these Extensions, however those which are not used internally and
+            registered by default (so called Core Extensions) must be registered
+            to <classname>Zend_Feed_Reader</classname> before they are used. The
+            bundled Extensions include:
+        </para>
+
+        <table>
+            <title>Core Extensions (pre-registered)</title>
+
+            <tgroup cols="2">
+                <tbody>
+                    <row>
+                        <entry>DublinCore (Feed and Entry)</entry>
+
+                        <entry>Implements support for both Dublin Core 1.0 and 1.1</entry>
+                    </row>
+
+                    <row>
+                        <entry>Content (Entry only)</entry>
+
+                        <entry>Implements support for Content 1.0</entry>
+                    </row>
+
+                    <row>
+                        <entry>Atom (Feed and Entry)</entry>
+
+                        <entry>Implements support for Atom 0.3 and Atom 1.0</entry>
+                    </row>
+
+                    <row>
+                        <entry>Slash</entry>
+
+                        <entry>todo</entry>
+                    </row>
+
+                    <row>
+                        <entry>WellFormedWeb</entry>
+
+                        <entry>todo</entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+
+        <para>
+            The Core Extensions are somewhat special since they are extremely
+            common and multi-faceted. For example, we have a Core Extension for Atom.
+            Atom is implemented as an Extension (not just a base class) because it
+            doubles as a valid RSS Extension - you can insert Atom elements into RSS
+            feeds. I've even seen RDF feeds which use a lot of Atom in place of more
+            common Extensions like Dublin Core.
+        </para>
+
+        <table>
+            <title>Non-Core Extensions (must register manually)</title>
+
+            <tgroup cols="1">
+                <tbody>
+                    <row>
+                        <entry>At present, no non-Core Extensions are distributed.</entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+
+        <para>
+            The additional non-Core Extensions are offered but not registered to
+            <classname>Zend_Feed_Reader</classname> by default. If you want to
+            use them, you'll need to tell
+            <classname>Zend_Feed_Reader</classname> to load them in advance of
+            importing a feed.
+        </para>
+
+        <para>
+            Registering an Extension with
+            <classname>Zend_Feed_Reader</classname>, so it is loaded and its API
+            is available to Feed and Entry objects, is a simple affair using the
+            <classname>Zend_Loader_PluginLoader</classname>. Here we register
+            the optional Slash Extension, and discover that it can be directly
+            called from the Entry level API without any effort. Note that
+            Extension names are case sensitive and use camel casing for multiple
+            terms.
+        </para>
+
+        <programlisting><![CDATA[
+Zend_Feed_Reader::registerExtension('Slash');
+$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
+$commentsForLastEntry = $feed->current()->getCommentCount();
+]]></programlisting>
+
+        <para>
+            In the simple example above, we checked how many comments were made
+            on an entry using the <methodname>getCommentCount()</methodname>
+            method. Since it's not part of
+            <classname>Zend_Feed_Reader</classname>'s core API, it could only be
+            a method supported by the newly registered Slash Extension (indeed
+            Slash was invented at Slashdot as an RDF Site Summary module and is
+            now fairly popular).
+        </para>
+
+        <sect3 id="zend.feed.reader.extending.feed">
+            <title>Writing <classname>Zend_Feed_Reader</classname> Extensions</title>
+
+            <para>
+                Inevitably, there will be times when the
+                <classname>Zend_Feed_Reader</classname> API is just not capable
+                of getting something you need from a feed or entry. You can use
+                the underlying source objects, like
+                <classname>DOMDocument</classname>, to get these by hand however
+                there is a more reusable method available by writing Extensions
+                supporting these new queries.
+            </para>
+
+            <para>
+                As an example, let's take the case of a purely fictitious
+                corporation named Jungle Books. Jungle Books have been
+                publishing a lot of reviews on books they sell (from external
+                sources and customers), which are distributed as an RSS 2.0
+                feed. Their marketing department realises that web applications
+                using this feed cannot currently figure out exactly what book is
+                being reviewed. To make life easier for everyone, they determine
+                that the geek department needs to extend RSS 2.0 to include a
+                new element per entry supplying the ISBN-10 or ISBN-13 number of
+                the publication the entry concerns. They define the new
+                <code>&lt;isbn&gt;</code> element quite simply with a standard
+                name and namespace URI:
+            </para>
+
+            <literallayout>
+JungleBooks 1.0:
+http://example.com/junglebooks/rss/module/1.0/
+            </literallayout>
+
+            <para>
+                A snippet of RSS containing this extension in practice could be
+                something similar to:
+            </para>
+
+            <literallayout>
+&lt;?xml version="1.0" encoding="utf-8" ?&gt;
+&lt;rss version="2.0"
+   xmlns:content="http://purl.org/rss/1.0/modules/content/"
+   xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/"&gt;
+&lt;channel&gt;
+    &lt;title&gt;Jungle Books Customer Reviews&lt;/title&gt;
+    &lt;link&gt;http://example.com/junglebooks&lt;/link&gt;
+    &lt;description&gt;Many book reviews!&lt;/description&gt;
+    &lt;pubDate&gt;Fri, 26 Jun 2009 19:15:10 GMT&lt;/pubDate&gt;
+    &lt;jungle:dayPopular&gt;http://example.com/junglebooks/book/938&lt;/jungle:dayPopular&gt;
+    &lt;item&gt;
+        &lt;title&gt;Review Of Flatland: A Romance of Many Dimensions&lt;/title&gt;
+        &lt;link&gt;http://example.com/junglebooks/review/987&lt;/link&gt;
+        &lt;author&gt;Confused Physics Student&lt;/author&gt;
+        &lt;content:encoded&gt;
+        A romantic square?!
+        &lt;/content:encoded&gt;
+        &lt;pubDate&gt;Thu, 25 Jun 2009 20:03:28 -0700&lt;/pubDate&gt;
+        &lt;jungle:isbn&gt;048627263X&lt;/jungle:isbn&gt;
+    &lt;/item&gt;
+&lt;/channel&gt;
+&lt;/rss&gt;
+            </literallayout>
+
+            <para>
+                Implementing this new ISBN element as a simple entry level
+                extension would require the following class (using your own class
+                namespace outside of Zend).
+            </para>
+
+            <programlisting role="php"><![CDATA[
+class My_FeedReader_Extension_JungleBooks_Entry
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+    public function getIsbn()
+    {
+        if (isset($this->_data['isbn'])) {
+            return $this->_data['isbn'];
+        }
+        $isbn = $this->_xpath->evaluate(
+            'string(' . $this->getXpathPrefix() . '/jungle:isbn)'
+        );
+        if (!$isbn) {
+            $isbn = null;
+        }
+        $this->_data['isbn'] = $title;
+        return $this->_data['isbn'];
+    }
+
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace(
+            'jungle', 'http://example.com/junglebooks/rss/module/1.0/'
+        );
+    }
+}
+]]></programlisting>
+
+            <para>
+                This extension is easy enough to follow. It creates a new method
+                <methodname>getIsbn()</methodname> which runs an XPath query on
+                the current entry to extract the ISBN number enclosed by the
+                <code>&lt;jungle:isbn&gt;</code> element. It can optionally
+                store this to the internal non-persistent cache (no need to keep
+                querying the DOM if it's called again on the same entry). The
+                value is returned to the caller. At the end we have a protected
+                method (it's abstract so it must exist) which registers the
+                Jungle Books namespace for their custom RSS module. While we
+                call this an RSS module, there's nothing to prevent the same
+                element being used in Atom feeds - and all Extensions which use
+                the prefix provided by <methodname>getXpathPrefix()</methodname>
+                are actually neutral and work on RSS or Atom feeds with no extra
+                code.
+            </para>
+
+            <para>
+                Since this Extension is stored outside of Zend Framework, you'll
+                need to register the path prefix for your Extensions so
+                <classname>Zend_Loader_PluginLoader</classname> can find them.
+                After that, it's merely a matter of registering the Extension,
+                if it's not already loaded, and using it in practice.
+            </para>
+
+            <programlisting role="php"><![CDATA[
+if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
+    Zend_Feed_Reader::addPrefixPath(
+        '/path/to/My/FeedReader/Extension', 'My_FeedReader_Extension'
+    );
+    Zend_Feed_Reader::registerExtension('JungleBooks');
+}
+$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
+
+// ISBN for whatever book the first entry in the feed was concerned with
+$firstIsbn = $feed->current()->getIsbn();
+]]></programlisting>
+
+            <para>
+                Writing a feed level Extension is not much different. The
+                example feed from earlier included an unmentioned
+                <code>&lt;jungle:dayPopular&gt;</code> element which Jungle
+                Books have added to their standard to include a link to the
+                day's most popular book (in terms of visitor traffic).  Here's
+                an Extension which adds a
+                <methodname>getDaysPopularBookLink()</methodname> method to the
+                feel level API.
+            </para>
+
+            <programlisting role="php"><![CDATA[
+class My_FeedReader_Extension_JungleBooks_Feed
+    extends Zend_Feed_Reader_Extension_FeedAbstract
+{
+    public function getDaysPopularBookLink()
+    {
+        if (isset($this->_data['dayPopular'])) {
+            return $this->_data['dayPopular'];
+        }
+        $dayPopular = $this->_xpath->evaluate(
+            'string(' . $this->getXpathPrefix() . '/jungle:dayPopular)'
+        );
+        if (!$dayPopular) {
+            $dayPopular = null;
+        }
+        $this->_data['dayPopular'] = $dayPopular;
+        return $this->_data['dayPopular'];
+    }
+
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace(
+            'jungle', 'http://example.com/junglebooks/rss/module/1.0/'
+        );
+    }
+}]]></programlisting>
+
+            <para>
+                Let's repeat the last example using a custom Extension to show the
+                method being used.
+            </para>
+
+            <programlisting role="php"><![CDATA[
+if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
+    Zend_Feed_Reader::addPrefixPath(
+        '/path/to/My/FeedReader/Extension', 'My_FeedReader_Extension'
+    );
+    Zend_Feed_Reader::registerExtension('JungleBooks');
+}
+$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
+
+// URI to the information page of the day's most popular book with visitors
+$daysPopularBookLink = $feed->getDaysPopularBookLink();
+
+// ISBN for whatever book the first entry in the feed was concerned with
+$firstIsbn = $feed->current()->getIsbn();
+]]></programlisting>
+
+            <para>
+                Going through these examples, you'll note that we don't register
+                feed and entry Extensions separately. Extensions within the same
+                standard may or may not include both a feed and entry class, so
+                <classname>Zend_Feed_Reader</classname> only requires you to
+                register the overall parent name, e.g. JungleBooks, DublinCore,
+                Slash. Internally, it can check at what level Extensions exist
+                and load them up if found. In our case, we have a full set of
+                Extensions now: <classname>JungleBooks_Feed</classname> and
+                <classname>JungleBooks_Entry</classname>.
+            </para>
+        </sect3>
+  </sect2>
+</sect1>

+ 690 - 0
library/Zend/Feed/Reader.php

@@ -0,0 +1,690 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed
+ */
+require_once 'Zend/Feed.php';
+
+/**
+ * @see Zend_Feed_Reader_Feed_Rss
+ */
+require_once 'Zend/Feed/Reader/Feed/Rss.php';
+
+/**
+ * @see Zend_Feed_Reader_Feed_Atom
+ */
+require_once 'Zend/Feed/Reader/Feed/Atom.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader
+{
+	/**
+	 * Namespace constants
+	 */
+	const NAMESPACE_ATOM_03  = 'http://purl.org/atom/ns#';
+    const NAMESPACE_ATOM_10  = 'http://www.w3.org/2005/Atom';
+    const NAMESPACE_RDF      = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+    const NAMESPACE_RSS_090  = 'http://my.netscape.com/rdf/simple/0.9/';
+    const NAMESPACE_RSS_10   = 'http://purl.org/rss/1.0/';
+
+    /**
+	 * Feed type constants
+	 */
+	const TYPE_ANY              = 'any';
+	const TYPE_ATOM_03          = 'atom-03';
+    const TYPE_ATOM_10          = 'atom-10';
+    const TYPE_ATOM_ANY         = 'atom';
+    const TYPE_RSS_090          = 'rss-090';
+    const TYPE_RSS_091          = 'rss-091';
+    const TYPE_RSS_091_NETSCAPE = 'rss-091n';
+    const TYPE_RSS_091_USERLAND = 'rss-091u';
+    const TYPE_RSS_092          = 'rss-092';
+    const TYPE_RSS_093          = 'rss-093';
+    const TYPE_RSS_094          = 'rss-094';
+    const TYPE_RSS_10           = 'rss-10';
+    const TYPE_RSS_20           = 'rss-20';
+    const TYPE_RSS_ANY          = 'rss';
+
+    /**
+     * Cache instance
+     *
+     * @var Zend_Cache_Core
+     */
+    protected static $_cache = null;
+
+    /**
+     * HTTP client object to use for retrieving feeds
+     *
+     * @var Zend_Http_Client
+     */
+    protected static $_httpClient = null;
+
+    /**
+     * Override HTTP PUT and DELETE request methods?
+     *
+     * @var boolean
+     */
+    protected static $_httpMethodOverride = false;
+
+    protected static $_httpConditionalGet = false;
+
+    protected static $_pluginLoader = null;
+
+    protected static $_prefixPaths = array();
+
+    protected static $_extensions = array(
+        'feed' => array(
+            'DublinCore_Feed',
+            'Atom_Feed'
+        ),
+        'entry' => array(
+            'Content_Entry',
+            'DublinCore_Entry',
+            'Atom_Entry'
+        ),
+        'core' => array(
+            'DublinCore_Feed',
+            'Atom_Feed',
+            'Content_Entry',
+            'DublinCore_Entry',
+            'Atom_Entry'
+        )
+    );
+
+    /**
+     * Get the Feed cache
+     *
+     * @return Zend_Cache_Core
+     */
+    public static function getCache()
+    {
+        return self::$_cache;
+    }
+
+    /**
+     * Set the feed cache
+     *
+     * @param Zend_Cache_Core $cache
+     * @return void
+     */
+    public static function setCache(Zend_Cache_Core $cache)
+    {
+        self::$_cache = $cache;
+    }
+
+    /**
+     * Set the HTTP client instance
+     *
+     * Sets the HTTP client object to use for retrieving the feeds.
+     *
+     * @param  Zend_Http_Client $httpClient
+     * @return void
+     */
+    public static function setHttpClient(Zend_Http_Client $httpClient)
+    {
+        self::$_httpClient = $httpClient;
+    }
+
+
+    /**
+     * Gets the HTTP client object. If none is set, a new Zend_Http_Client will be used.
+     *
+     * @return Zend_Http_Client_Abstract
+     */
+    public static function getHttpClient()
+    {
+        if (!self::$_httpClient instanceof Zend_Http_Client) {
+            /**
+             * @see Zend_Http_Client
+             */
+            require_once 'Zend/Http/Client.php';
+            self::$_httpClient = new Zend_Http_Client();
+        }
+
+        return self::$_httpClient;
+    }
+
+    /**
+     * Toggle using POST instead of PUT and DELETE HTTP methods
+     *
+     * Some feed implementations do not accept PUT and DELETE HTTP
+     * methods, or they can't be used because of proxies or other
+     * measures. This allows turning on using POST where PUT and
+     * DELETE would normally be used; in addition, an
+     * X-Method-Override header will be sent with a value of PUT or
+     * DELETE as appropriate.
+     *
+     * @param  boolean $override Whether to override PUT and DELETE.
+     * @return void
+     */
+    public static function setHttpMethodOverride($override = true)
+    {
+        self::$_httpMethodOverride = $override;
+    }
+
+    /**
+     * Get the HTTP override state
+     *
+     * @return boolean
+     */
+    public static function getHttpMethodOverride()
+    {
+        return self::$_httpMethodOverride;
+    }
+
+    /**
+     * Set the flag indicating whether or not to use HTTP conditional GET
+     * 
+     * @param  bool $bool 
+     * @return void
+     */
+    public static function useHttpConditionalGet($bool = true)
+    {
+        self::$_httpConditionalGet = $bool;
+    }
+
+    /**
+	 * Import a feed by providing a URL
+	 *
+	 * @param  string $url The URL to the feed
+     * @param  string $etag OPTIONAL Last received ETag for this resource
+     * @param  string $lastModified OPTIONAL Last-Modified value for this resource
+	 * @return Zend_Feed_Reader_Feed_Interface
+	 */
+    public static function import($uri, $etag = null, $lastModified = null)
+    {
+        $cache       = self::getCache();
+        $feed        = null;
+        $responseXml = '';
+        $client      = self::getHttpClient();
+        $client->resetParameters();
+        $client->setHeaders('If-None-Match', null);
+        $client->setHeaders('If-Modified-Since', null);
+        $client->setUri($uri);
+        $cacheId = 'Zend_Feed_Reader_' . md5($uri);
+
+        if (self::$_httpConditionalGet && $cache) {
+            $data = $cache->load($cacheId);
+            if ($data) {
+                if (is_null($etag)) {
+                    $etag = $cache->load($cacheId.'_etag');
+                }
+                if (is_null($lastModified)) {
+                    $lastModified = $cache->load($cacheId.'_lastmodified');;
+                }
+                if ($etag) {
+                    $client->setHeaders('If-None-Match', $etag);
+                }
+                if ($lastModified) {
+                    $client->setHeaders('If-Modified-Since', $lastModified);
+                }
+            }
+            $response = $client->request('GET');
+            if ($response->getStatus() !== 200 || $response->getStatus() !== 304) {
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
+            }
+            if ($response->getStatus() == 304) {
+                $responseXml = $data;
+            } else {
+                $responseXml = $response->getBody();
+                $cache->save($responseXml, $cacheId);
+                if ($response->getHeader('ETag')) {
+                    $cache->save($response->getHeader('ETag'), $cacheId.'_etag');
+                }
+                if ($response->getHeader('Last-Modified')) {
+                    $cache->save($response->getHeader('Last-Modified'), $cacheId.'_lastmodified');
+                }
+            }
+            return self::importString($responseXml);
+        } elseif ($cache) {
+            $data = $cache->load($cacheId);
+            if ($data !== false) {
+                return self::importString($data);
+            }
+            $response = $client->request('GET');
+            if ($response->getStatus() !== 200) {
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
+            }
+            $responseXml = $response->getBody();
+            $cache->save($responseXml, $cacheId);
+            return self::importString($responseXml);
+        } else {
+            $response = $client->request('GET');
+            if ($response->getStatus() !== 200) {
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
+            }
+            return self::importString($response->getBody());
+        }
+    }
+
+    /**
+     * Import a feed by providing a Zend_Feed_Abstract object
+     *
+     * @param  Zend_Feed_Abstract $feed A fully instantiated Zend_Feed object
+     * @return Zend_Feed_Reader_Feed_Interface
+	 */
+    public static function importFeed(Zend_Feed_Abstract $feed)
+    {
+        $dom  = $feed->getDOM()->ownerDocument;
+        $type = self::detectType($dom);
+        self::_registerCoreExtensions();
+        if (substr($type, 0, 3) == 'rss') {
+            $reader = new Zend_Feed_Reader_Feed_Rss($dom, $type);
+        } else {
+        	$reader = new Zend_Feed_Reader_Feed_Atom($dom, $type);
+        }
+
+        return $reader;
+    }
+
+    /**
+     * Import a feed froma string
+     *
+     * @param  string $string
+     * @return Zend_Feed_Reader_Feed_Interface
+     */
+    public static function importString($string)
+    {
+        @ini_set('track_errors', 1);
+        $dom = new DOMDocument;
+        $status = @$dom->loadXML($string);
+        @ini_restore('track_errors');
+
+        if (!$status) {
+            if (!isset($php_errormsg)) {
+                if (function_exists('xdebug_is_enabled')) {
+                    $php_errormsg = '(error message not available, when XDebug is running)';
+                } else {
+                    $php_errormsg = '(error message not available)';
+                }
+            }
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
+        }
+
+        $type = self::detectType($dom);
+
+        self::_registerCoreExtensions();
+
+        if (substr($type, 0, 3) == 'rss') {
+            $reader = new Zend_Feed_Reader_Feed_Rss($dom, $type);
+        } else {
+        	$reader = new Zend_Feed_Reader_Feed_Atom($dom, $type);
+        }
+        return $reader;
+    }
+
+    /**
+     * Imports a feed from a file located at $filename.
+     *
+     * @param  string $filename
+     * @throws Zend_Feed_Exception
+     * @return Zend_Feed_Reader_FeedInterface
+     */
+    public static function importFile($filename)
+    {
+        @ini_set('track_errors', 1);
+        $feed = @file_get_contents($filename);
+        @ini_restore('track_errors');
+        if ($feed === false) {
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("File could not be loaded: $php_errormsg");
+        }
+        return self::importString($feed);
+    }
+
+    public static function findFeedLinks($uri)
+    {
+        // Get the HTTP response from $uri and save the contents
+        $client = self::getHttpClient();
+        $client->setUri($uri);
+        $response = $client->request();
+        if ($response->getStatus() !== 200) {
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("Failed to access $uri, got response code " . $response->getStatus());
+        }
+        $responseHtml = $response->getBody();
+        @ini_set('track_errors', 1);
+        $dom = new DOMDocument;
+        $status = @$dom->loadHTML($responseHtml);
+        @ini_restore('track_errors');
+        if (!$status) {
+            if (!isset($php_errormsg)) {
+                if (function_exists('xdebug_is_enabled')) {
+                    $php_errormsg = '(error message not available, when XDebug is running)';
+                } else {
+                    $php_errormsg = '(error message not available)';
+                }
+            }
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
+        }
+        $feedLinks = new stdClass;
+        $links = $dom->getElementsByTagName('link');
+        foreach ($links as $link) {
+            if (strtolower($link->getAttribute('rel')) !== 'alternate'
+                || !$link->getAttribute('type') || !$link->getAttribute('href')) {
+                continue;
+            }
+            if (!isset($feedLinks->rss) && $link->getAttribute('type') == 'application/rss+xml') {
+                $feedLinks->rss = $link->getAttribute('href');
+            } elseif(!isset($feedLinks->atom) && $link->getAttribute('type') == 'application/atom+xml') {
+                $feedLinks->atom = $link->getAttribute('href');
+            } elseif(!isset($feedLinks->rdf) && $link->getAttribute('type') == 'application/rdf+xml') {
+                $feedLinks->rdf = $link->getAttribute('href');
+            }
+            if (isset($feedLinks->rss) && isset($feedLinks->atom) && isset($feedLinks->rdf)) {
+                break;
+            }
+        }
+        return $feedLinks;
+    }
+
+    /**
+     * Detect the feed type of the provided feed
+     *
+     * @param  Zend_Feed_Abstract $feed A fully instantiated Zend_Feed object
+     * @return string
+     */
+    public static function detectType($feed)
+    {
+        if ($feed instanceof Zend_Feed_Reader_Feed_Interface) {
+            $dom = $feed->getDomDocument();
+        } elseif($feed instanceof DomDocument) {
+            $dom = $feed;
+        } elseif(is_string($feed) && !empty($feed)) {
+            @ini_set('track_errors', 1);
+            $dom = new DOMDocument;
+            $status = @$doc->loadXML($string);
+            @ini_restore('track_errors');
+            if (!$status) {
+                if (!isset($php_errormsg)) {
+                    if (function_exists('xdebug_is_enabled')) {
+                        $php_errormsg = '(error message not available, when XDebug is running)';
+                    } else {
+                        $php_errormsg = '(error message not available)';
+                    }
+                }
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
+            }
+        } else {
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('Invalid object/scalar provided: must be of type Zend_Feed_Reader_Feed_Interface, DomDocument or string');
+        }
+        $xpath = new DOMXPath($dom);
+
+        if ($xpath->query('/rss')->length) {
+            $type = self::TYPE_RSS_ANY;
+            $version = $xpath->evaluate('string(/rss/@version)');
+
+            if (strlen($version) > 0) {
+                switch($version) {
+                    case '2.0':
+                        $type = self::TYPE_RSS_20;
+                        break;
+
+                    case '0.94':
+                        $type = self::TYPE_RSS_094;
+                        break;
+
+                    case '0.93':
+                        $type = self::TYPE_RSS_093;
+                        break;
+
+                    case '0.92':
+                        $type = self::TYPE_RSS_092;
+                        break;
+
+                    case '0.91':
+                        $type = self::TYPE_RSS_091;
+                        break;
+                }
+            }
+
+            return $type;
+        }
+
+        $xpath->registerNamespace('rdf', self::NAMESPACE_RDF);
+
+        if ($xpath->query('/rdf:RDF')->length) {
+            $xpath->registerNamespace('rss', self::NAMESPACE_RSS_10);
+
+            if ($xpath->query('/rdf:RDF/rss:channel')->length
+                || $xpath->query('/rdf:RDF/rss:image')->length
+                || $xpath->query('/rdf:RDF/rss:item')->length
+                || $xpath->query('/rdf:RDF/rss:textinput')->length
+            ) {
+                return self::TYPE_RSS_10;
+            }
+
+            $xpath->registerNamespace('rss', self::NAMESPACE_RSS_090);
+
+            if ($xpath->query('/rdf:RDF/rss:channel')->length
+                || $xpath->query('/rdf:RDF/rss:image')->length
+                || $xpath->query('/rdf:RDF/rss:item')->length
+                || $xpath->query('/rdf:RDF/rss:textinput')->length
+            ) {
+                return self::TYPE_RSS_090;
+            }
+        }
+
+        $type = self::TYPE_ATOM_ANY;
+        $xpath->registerNamespace('atom', self::NAMESPACE_ATOM_10);
+
+        if ($xpath->query('//atom:feed')->length) {
+            return self::TYPE_ATOM_10;
+        }
+
+        $xpath->registerNamespace('atom', self::NAMESPACE_ATOM_03);
+
+        if ($xpath->query('//atom:feed')->length) {
+            return self::TYPE_ATOM_03;
+        }
+
+        return self::TYPE_ANY;
+    }
+
+    /**
+     * Set plugin loader for use with Extensions
+     *
+     * @param  Zend_Loader_PluginLoader_Interface $loader
+     */
+    public static function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader)
+    {
+        self::$_pluginLoader = $loader;
+    }
+
+    /**
+     * Get plugin loader for use with Extensions
+     *
+     * @return  Zend_Loader_PluginLoader_Interface $loader
+     */
+    public static function getPluginLoader()
+    {
+        if (!isset(self::$_pluginLoader)) {
+            require_once 'Zend/Loader/PluginLoader.php';
+            self::$_pluginLoader = new Zend_Loader_PluginLoader(array(
+                'Zend_Feed_Reader_Extension_' => 'Zend/Feed/Reader/Extension/',
+            ));
+        }
+        return self::$_pluginLoader;
+    }
+
+    /**
+     * Add prefix path for loading Extensions
+     * 
+     * @param  string $prefix 
+     * @param  string $path 
+     * @return void
+     */
+    public static function addPrefixPath($prefix, $path)
+    {
+        $prefix = rtrim($prefix, '_');
+        $path   = rtrim($path, DIRECTORY_SEPARATOR);
+        self::$_pluginLoader->addPrefixPath($prefix, $path);
+    }
+
+    /**
+     * Add multiple Extension prefix paths at once
+     * 
+     * @param  array $spec 
+     * @return void
+     */
+    public static function addPrefixPaths(array $spec)
+    {
+        if (isset($spec['prefix']) && isset($spec['path'])) {
+            self::addPrefixPath($spec['prefix'], $spec['path']);
+        }
+        foreach ($spec as $prefixPath) {
+            if (isset($prefixPath['prefix']) && isset($prefixPath['path'])) {
+                self::addPrefixPath($prefixPath['prefix'], $prefixPath['path']);
+            }
+        }
+    }
+
+    /**
+     * Register an Extension by name
+     * 
+     * @param  string $name 
+     * @return void
+     * @throws Zend_Feed_Exception if unable to resolve Extension class
+     */
+    public static function registerExtension($name)
+    {
+        $feedName  = $name . '_Feed';
+        $entryName = $name . '_Entry';
+        if (self::isRegistered($name)) {
+            if (self::getPluginLoader()->isLoaded($feedName) ||
+                self::getPluginLoader()->isLoaded($entryName)) {
+                return;
+            }
+        }
+        try {
+            self::getPluginLoader()->load($feedName);
+            self::$_extensions['feed'][] = $feedName;
+        } catch (Zend_Loader_PluginLoader_Exception $e) {
+        }
+        try {
+            self::getPluginLoader()->load($entryName);
+            self::$_extensions['entry'][] = $entryName;
+        } catch (Zend_Loader_PluginLoader_Exception $e) {
+        }
+        if (!self::getPluginLoader()->isLoaded($feedName)
+            && !self::getPluginLoader()->isLoaded($entryName)
+        ) {
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('Could not load extension: ' . $name
+                . 'using Plugin Loader. Check prefix paths are configured and extension exists.');
+        }
+    }
+
+    /**
+     * Is a given named Extension registered?
+     * 
+     * @param  string $extensionName 
+     * @return boolean
+     */
+    public static function isRegistered($extensionName)
+    {
+        $feedName  = $extensionName . '_Feed';
+        $entryName = $extensionName . '_Entry';
+        if (in_array($feedName, self::$_extensions['feed'])
+            || in_array($entryName, self::$_extensions['entry'])
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get a list of extensions
+     * 
+     * @return array
+     */
+    public static function getExtensions()
+    {
+        return self::$_extensions;
+    }
+
+    /**
+     * Reset class state to defaults
+     * 
+     * @return void
+     */
+    public static function reset()
+    {
+        self::$_cache              = null;
+        self::$_httpClient         = null;
+        self::$_httpMethodOverride = false;
+        self::$_httpConditionalGet = false;
+        self::$_pluginLoader       = null;
+        self::$_prefixPaths        = array();
+        self::$_extensions         = array(
+            'feed' => array(
+                'DublinCore_Feed',
+                'Atom_Feed'
+            ),
+            'entry' => array(
+                'Content_Entry',
+                'DublinCore_Entry',
+                'Atom_Entry'
+            ),
+            'core' => array(
+                'DublinCore_Feed',
+                'Atom_Feed',
+                'Content_Entry',
+                'DublinCore_Entry',
+                'Atom_Entry'
+            )
+        );
+    }
+
+    /**
+     * Register core (default) extensions
+     * 
+     * @return void
+     */
+    protected static function _registerCoreExtensions()
+    {
+        self::registerExtension('DublinCore');
+        self::registerExtension('Content');
+        self::registerExtension('Atom');
+        self::registerExtension('Slash');
+        self::registerExtension('WellFormedWeb');
+        self::registerExtension('Thread');
+    }
+}

+ 215 - 0
library/Zend/Feed/Reader/Entry/Abstract.php

@@ -0,0 +1,215 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Feed_Reader_Entry_Abstract
+{
+    /**
+     * Feed entry data
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * DOM document object
+     *
+     * @var DOMDocument
+     */
+    protected $_domDocument = null;
+
+    /**
+     * Entry instance
+     *
+     * @var Zend_Feed_Entry_Interface
+     */
+    protected $_entry = null;
+
+    /**
+     * Pointer to the current entry
+     *
+     * @var int
+     */
+    protected $_entryKey = 0;
+
+    /**
+     * XPath object
+     *
+     * @var DOMXPath
+     */
+    protected $_xpath = null;
+
+    /**
+     * Registered extensions
+     * 
+     * @var array
+     */
+    protected $_extensions = array();
+
+    /**
+     * Constructor
+     * 
+     * @param  DOMElement $entry 
+     * @param  int $entryKey 
+     * @param  string $type 
+     * @return void
+     */
+    public function __construct(DOMElement $entry, $entryKey, $type = null)
+    {
+        $this->_entry       = $entry;
+        $this->_entryKey    = $entryKey;
+        $this->_domDocument = $entry->ownerDocument;
+        if ($type !== null) {
+            $this->_data['type'] = $type;
+        } else {
+            $this->_data['type'] = Zend_Feed_Reader::detectType($feed);
+        }
+        $this->_loadExtensions();
+    }
+
+    /**
+     * Get the DOM
+     *
+     * @return DOMDocument
+     */
+    public function getDomDocument()
+    {
+        return $this->_domDocument;
+    }
+
+    /**
+     * Get the entry element
+     * 
+     * @return Zend_Feed_Entry_Interface
+     */
+    public function getEntryElement()
+    {
+        return $this->_entry;
+    }
+
+    /**
+     * Get the Entry's encoding
+     *
+     * @return string
+     */
+    public function getEncoding()
+    {
+        $assumed = $this->getDomDocument()->encoding;
+        return $assumed;
+    }
+
+	/**
+     * Get the entry type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->_data['type'];
+    }
+
+    /**
+     * Get the XPath query object
+     *
+     * @return DOMXPath
+     */
+    public function getXpath()
+    {
+        return $this->_xpath;
+    }
+
+	/**
+     * Set the XPath query
+     *
+     * @param  DOMXPath $xpath
+     * @return Zend_Feed_Reader_Entry_Abstract
+     */
+    public function setXpath(DOMXPath $xpath)
+    {
+        $this->_xpath = $xpath;
+        return $this;
+    }
+
+    /**
+     * Serialize the entry to an array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Get registered extensions
+     * 
+     * @return array
+     */
+    public function getExtensions()
+    {
+        return $this->_extensions;
+    }
+
+    /**
+     * Method overloading: call given method on first extension implementing it
+     * 
+     * @param  string $method 
+     * @param  array $args 
+     * @return mixed
+     * @throws Zend_Feed_Exception if no extensions implements the method
+     */
+    public function __call($method, $args)
+    {
+        foreach ($this->_extensions as $extension) {
+            if (method_exists($extension, $method)) {
+                return call_user_func_array(array($extension, $method), $args);
+            }
+        }
+        require_once 'Zend/Feed/Exception.php';
+        throw new Zend_Feed_Exception('Method: ' . $method
+            . 'does not exist and could not be located on a registered Extension');
+    }
+
+    /**
+     * Load extensions from Zend_Feed_Reader
+     * 
+     * @return void
+     */
+    protected function _loadExtensions()
+    {
+        $all = Zend_Feed_Reader::getExtensions();
+        $feed = $all['entry'];
+        foreach ($feed as $extension) {
+            if (in_array($extension, $all['core'])) {
+                continue;
+            }
+            $className = Zend_Feed_Reader::getPluginLoader()->getClassName($extension);
+            $this->_extensions[$className] = new $className(
+                $this->getEntryElement(), $this->_entryKey, $this->_data['type']
+            );
+        }
+    }
+}

+ 355 - 0
library/Zend/Feed/Reader/Entry/Atom.php

@@ -0,0 +1,355 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Interface
+ */
+require_once 'Zend/Feed/Reader/Entry/Interface.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Abstract
+ */
+require_once 'Zend/Feed/Reader/Entry/Abstract.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_Atom_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/Atom/Entry.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Entry_Atom extends Zend_Feed_Reader_Entry_Abstract implements Zend_Feed_Reader_Entry_Interface
+{
+    /**
+     * XPath query
+     *
+     * @var string
+     */
+    protected $_xpathQuery = '';
+
+    /**
+     * Atom Extension object
+     *
+     * @var Zend_Feed_Reader_Extension_Atom_Entry
+     */
+    protected $_atom = null;
+
+    /**
+     * Thread Extension object
+     *
+     * @var Zend_Feed_Reader_Extension_Thread_Entry
+     */
+    protected $_thread = null;
+
+    /**
+     * Constructor
+     *
+     * @param  DOMElement $entry
+     * @param  int $entryKey
+     * @param  string $type
+     * @return void
+     */
+    public function __construct(DOMElement $entry, $entryKey, $type = null)
+    {
+        parent::__construct($entry, $entryKey, $type);
+
+        // Everyone by now should know XPath indices start from 1 not 0
+        $this->_xpathQuery = '//atom:entry[' . ($this->_entryKey + 1) . ']';
+        $atomClass = Zend_Feed_Reader::getPluginLoader()->getClassName('Atom_Entry');
+
+        $this->_atom = new $atomClass($entry, $entryKey, $type);
+
+        $threadClass = Zend_Feed_Reader::getPluginLoader()->getClassName('Thread_Entry');
+        $this->_thread = new $threadClass($entry, $entryKey, $type);
+    }
+
+	/**
+     * Get the specified author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $people = $this->_atom->getAuthors();
+
+        $this->_data['authors'] = $people;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the entry content
+     *
+     * @return string
+     */
+    public function getContent()
+    {
+        if (array_key_exists('content', $this->_data)) {
+            return $this->_data['content'];
+        }
+
+        $content = $this->_atom->getContent();
+
+        $this->_data['content'] = $content;
+
+        return $this->_data['content'];
+    }
+
+    /**
+     * Get the entry creation date
+     *
+     * @return string
+     */
+    public function getDateCreated()
+    {
+        if (array_key_exists('datecreated', $this->_data)) {
+            return $this->_data['datecreated'];
+        }
+
+        $dateCreated = $this->_atom->getDateCreated();
+
+        $this->_data['datecreated'] = $dateCreated;
+
+        return $this->_data['datecreated'];
+    }
+
+    /**
+     * Get the entry modification date
+     *
+     * @return string
+     */
+    public function getDateModified()
+    {
+        if (array_key_exists('datemodified', $this->_data)) {
+            return $this->_data['datemodified'];
+        }
+
+        $dateModified = $this->_atom->getDateModified();
+
+        $this->_data['datemodified'] = $dateModified;
+
+        return $this->_data['datemodified'];
+    }
+
+    /**
+     * Get the entry description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = $this->_atom->getDescription();
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the entry ID
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = $this->_atom->getId();
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get a specific link
+     *
+     * @param  int $index
+     * @return string
+     */
+    public function getLink($index = 0)
+    {
+        if (!array_key_exists('links', $this->_data)) {
+            $this->getLinks();
+        }
+
+        if (isset($this->_data['links'][$index])) {
+            return $this->_data['links'][$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get all links
+     *
+     * @return array
+     */
+    public function getLinks()
+    {
+        if (array_key_exists('links', $this->_data)) {
+            return $this->_data['links'];
+        }
+
+        $links = $this->_atom->getLinks();
+
+        $this->_data['links'] = $links;
+
+        return $this->_data['links'];
+    }
+
+    /**
+     * Get a permalink to the entry
+     *
+     * @return string
+     */
+    public function getPermalink()
+    {
+        return $this->getLink(0);
+    }
+
+    /**
+     * Get the entry title
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = $this->_atom->getTitle();
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+    /**
+     * Get the number of comments/replies for current entry
+     *
+     * @return integer
+     */
+    public function getCommentCount()
+    {
+        if (array_key_exists('commentcount', $this->_data)) {
+            return $this->_data['commentcount'];
+        }
+
+        $commentcount = $this->_thread->getCommentCount();
+
+        if (!$commentcount) {
+            $commentcount = $this->_atom->getCommentCount();
+        }
+
+        $this->_data['commentcount'] = $commentcount;
+
+        return $this->_data['commentcount'];
+    }
+
+    /**
+     * Returns a URI pointing to the HTML page where comments can be made on this entry
+     *
+     * @return string
+     */
+    public function getCommentLink()
+    {
+        if (array_key_exists('commentlink', $this->_data)) {
+            return $this->_data['commentlink'];
+        }
+
+        $commentlink = $this->_atom->getCommentLink();
+
+        $this->_data['commentlink'] = $commentlink;
+
+        return $this->_data['commentlink'];
+    }
+
+    /**
+     * Returns a URI pointing to a feed of all comments for this entry
+     *
+     * @return string
+     */
+    public function getCommentFeedLink()
+    {
+        if (array_key_exists('commentfeedlink', $this->_data)) {
+            return $this->_data['commentfeedlink'];
+        }
+
+        $commentfeedlink = $this->_atom->getCommentFeedLink();
+
+        $this->_data['commentfeedlink'] = $commentfeedlink;
+
+        return $this->_data['commentfeedlink'];
+    }
+
+    /**
+     * Set the XPath query (incl. on all Extensions)
+     *
+     * @param DOMXPath $xpath
+     */
+    public function setXpath(DOMXPath $xpath)
+    {
+        parent::setXpath($xpath);
+        $this->_atom->setXpath($this->_xpath);
+        $this->_thread->setXpath($this->_xpath);
+        foreach ($this->_extensions as $extension) {
+            $extension->setXpath($this->_xpath);
+        }
+    }
+}

+ 139 - 0
library/Zend/Feed/Reader/Entry/Interface.php

@@ -0,0 +1,139 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Feed_Reader_Entry_Interface
+{
+    /**
+     * Constructor
+     *
+     * @param  Zend_Feed_Entry_Abstract $entry
+     * @param  int $entryKey
+     * @param  string $type
+     * @return void
+     */
+    public function __construct(DOMElement $entry, $entryKey, $type = null);
+
+    /**
+     * Get the specified author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0);
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors();
+
+    /**
+     * Get the entry content
+     *
+     * @return string
+     */
+    public function getContent();
+
+    /**
+     * Get the entry creation date
+     *
+     * @return string
+     */
+    public function getDateCreated();
+
+    /**
+     * Get the entry modification date
+     *
+     * @return string
+     */
+    public function getDateModified();
+
+    /**
+     * Get the entry description
+     *
+     * @return string
+     */
+    public function getDescription();
+
+    /**
+     * Get the entry ID
+     *
+     * @return string
+     */
+    public function getId();
+
+    /**
+     * Get a specific link
+     *
+     * @param  int $index
+     * @return string
+     */
+    public function getLink($index = 0);
+
+    /**
+     * Get all links
+     *
+     * @return array
+     */
+    public function getLinks();
+
+    /**
+     * Get a permalink to the entry
+     *
+     * @return string
+     */
+    public function getPermalink();
+
+    /**
+     * Get the entry title
+     *
+     * @return string
+     */
+    public function getTitle();
+
+    /**
+     * Get the number of comments/replies for current entry
+     *
+     * @return integer
+     */
+    public function getCommentCount();
+
+    /**
+     * Returns a URI pointing to the HTML page where comments can be made on this entry
+     *
+     * @return string
+     */
+    public function getCommentLink();
+
+    /**
+     * Returns a URI pointing to a feed of all comments for this entry
+     *
+     * @return string
+     */
+    public function getCommentFeedLink();
+}

+ 625 - 0
library/Zend/Feed/Reader/Entry/Rss.php

@@ -0,0 +1,625 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Interface
+ */
+require_once 'Zend/Feed/Reader/Entry/Interface.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Abstract
+ */
+require_once 'Zend/Feed/Reader/Entry/Abstract.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_DublinCore_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/DublinCore/Entry.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_Content_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/Content/Entry.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_Atom_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/Atom/Entry.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_WellformedWeb_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/WellFormedWeb/Entry.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_Slash_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/Slash/Entry.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_Thread_Entry
+ */
+require_once 'Zend/Feed/Reader/Extension/Thread/Entry.php';
+
+/**
+ * @see Zend_Date
+ */
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Entry_Rss extends Zend_Feed_Reader_Entry_Abstract implements Zend_Feed_Reader_Entry_Interface
+{
+    /**
+     * Dublin Core object
+     *
+     * @var Zend_Feed_Reader_Extension_DublinCore_Entry
+     */
+    protected $_dc = null;
+
+    /**
+     * Content Module object
+     *
+     * @var Zend_Feed_Reader_Extension_Content_Entry
+     */
+    protected $_content = null;
+
+    /**
+     * Atom Extension object
+     *
+     * @var Zend_Feed_Reader_Extension_Atom_Entry
+     */
+    protected $_atom = null;
+
+    /**
+     * WellFormedWeb Extension object
+     *
+     * @var Zend_Feed_Reader_Extension_WellFormedWeb_Entry
+     */
+    protected $_wfw = null;
+
+    /**
+     * Slash Extension object
+     *
+     * @var Zend_Feed_Reader_Extension_Slash_Entry
+     */
+    protected $_slash = null;
+
+    /**
+     * Atom Threaded Extension object
+     *
+     * @var Zend_Feed_Reader_Extension_Thread_Entry
+     */
+    protected $_thread = null;
+
+    /**
+     * XPath query for RDF
+     *
+     * @var string
+     */
+    protected $_xpathQueryRdf = '';
+
+    /**
+     * XPath query for RSS
+     *
+     * @var string
+     */
+    protected $_xpathQueryRss = '';
+
+    /**
+     * Constructor
+     *
+     * @param  Zend_Feed_Entry_Abstract $entry
+     * @param  string $entryKey
+     * @param  string $type
+     * @return void
+     */
+    public function __construct(DOMElement $entry, $entryKey, $type = null)
+    {
+        parent::__construct($entry, $entryKey, $type);
+        $this->_xpathQueryRss = '//item[' . ($this->_entryKey+1) . ']';
+        $this->_xpathQueryRdf = '//rss:item[' . ($this->_entryKey+1) . ']';
+
+        $pluginLoader = Zend_Feed_Reader::getPluginLoader();
+
+        $dublinCoreClass = $pluginLoader->getClassName('DublinCore_Entry');
+        $this->_dc       = new $dublinCoreClass($entry, $entryKey, $type);
+
+        $contentClass   = $pluginLoader->getClassName('Content_Entry');
+        $this->_content = new $contentClass($entry, $entryKey, $type);
+
+        $atomClass   = $pluginLoader->getClassName('Atom_Entry');
+        $this->_atom = new $atomClass($entry, $entryKey, $type);
+
+        $wfwClass   = $pluginLoader->getClassName('WellFormedWeb_Entry');
+        $this->_wfw = new $wfwClass($entry, $entryKey, $type);
+
+        $slashClass   = $pluginLoader->getClassName('Slash_Entry');
+        $this->_slash = new $slashClass($entry, $entryKey, $type);
+
+        $threadClass   = $pluginLoader->getClassName('Thread_Entry');
+        $this->_thread = new $threadClass($entry, $entryKey, $type);
+    }
+
+    /**
+     * Get an author entry
+     *
+     * @param DOMElement $element
+     * @return string
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $authors = array();
+        // @todo: create a list from all potential sources rather than from alternatives
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $list = $this->_xpath->evaluate($this->_xpathQueryRss.'//author');
+        } else {
+            $list = $this->_xpath->evaluate($this->_xpathQueryRdf.'//rss:author');
+        }
+        if (!$list->length) {
+            if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+                $list = $this->_xpath->query('//author');
+            } else {
+                $list = $this->_xpath->query('//rss:author');
+            }
+        }
+
+        if ($list->length) {
+            foreach ($list as $author) {
+                if ($this->getType() == Zend_Feed_Reader::TYPE_RSS_20 
+                    && preg_match("/\(([^\)]+)\)/", $author->nodeValue, $matches, PREG_OFFSET_CAPTURE)
+                ) {
+                    // source name from RSS 2.0 <author>
+                    // format "joe@example.com (Joe Bloggs)"
+                    $authors[] = $matches[1][0];
+                } else {
+                    $authors[] = $author->nodeValue;
+                }
+            }
+
+            $authors = array_unique($authors);
+        }
+
+        if (empty($authors)) {
+            $authors = $this->_dc->getAuthors();
+        }
+
+        if (empty($authors)) {
+            $authors = $this->_atom->getAuthors();
+        }
+
+        $this->_data['authors'] = $authors;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the entry content
+     *
+     * @return string
+     */
+    public function getContent()
+    {
+        if (array_key_exists('content', $this->_data)) {
+            return $this->_data['content'];
+        }
+
+        $content = $this->_content->getContent();
+
+        if (!$content) {
+            $content = $this->getDescription();
+        }
+
+        if (empty($content)) {
+            $content = $this->_atom->getContent();
+        }
+
+        $this->_data['content'] = $content;
+
+        return $this->_data['content'];
+    }
+
+    /**
+     * Get the entry's date of creation
+     * 
+     * @return string
+     */
+    public function getDateCreated()
+    {
+        return $this->getDateModified();
+    }
+
+    /**
+     * Get the entry's date of modification
+     * 
+     * @return string
+     */
+    public function getDateModified()
+    {
+        if (array_key_exists('datemodified', $this->_data)) {
+            return $this->_data['datemodified'];
+        }
+
+        $dateModified = null;
+        $date = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 
+            && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $dateModified = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/pubDate)');
+            if ($dateModified) {
+                $date = new Zend_Date();
+                try {
+                    $date->set($dateModified, Zend_Date::RFC_822);
+                } catch (Zend_Date_Exception $e) {
+                    try {
+                        $date->set($dateModified, Zend_Date::RFC_2822);
+                    } catch (Zend_Date_Exception $e) {
+                        require_once 'Zend/Feed/Exception.php';
+                        throw new Zend_Feed_Exception(
+                            'Could not load date due to unrecognised format (should follow RFC 822 or 2822): '
+                            . $e->getMessage()
+                        );
+                    }
+                }
+            }
+        }
+
+        if (!$date) {
+            $date = $this->_dc->getDate();
+        }
+
+        if (!$date) {
+            $date = $this->_atom->getDateModified();
+        }
+
+        if (!$date) {
+            $date = null;
+        }
+
+        $this->_data['datemodified'] = $date;
+
+        return $this->_data['datemodified'];
+    }
+
+    /**
+     * Get the entry description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 
+            && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $description = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/description)');
+        } else {
+            $description = $this->_xpath->evaluate('string('.$this->_xpathQueryRdf.'/rss:description)');
+        }
+
+        if (!$description) {
+            $description = $this->_dc->getDescription();
+        }
+
+        if (empty($description)) {
+            $description = $this->_atom->getDescription();
+        }
+
+        if (!$description) {
+            $description = null;
+        } else {
+            $description = html_entity_decode($description, ENT_QUOTES, $this->getEncoding());
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the entry ID
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 
+            && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $id = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/guid)');
+        }
+
+        if (!$id) {
+            $id = $this->_dc->getId();
+        }
+
+        if (empty($id)) {
+            $id = $this->_atom->getId();
+        }
+
+        if (!$id) {
+            if ($this->getPermalink()) {
+                $id = $this->getPermalink();
+            } elseif ($this->getTitle()) {
+                $id = $this->getTitle();
+            } else {
+                $id = null;
+            }
+        }
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get a specific link
+     *
+     * @param  int $index
+     * @return string
+     */
+    public function getLink($index = 0)
+    {
+        if (!array_key_exists('links', $this->_data)) {
+            $this->getLinks();
+        }
+
+        if (isset($this->_data['links'][$index])) {
+            return $this->_data['links'][$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get all links
+     *
+     * @return array
+     */
+    public function getLinks()
+    {
+        if (array_key_exists('links', $this->_data)) {
+            return $this->_data['links'];
+        }
+
+        $links = array();
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $list = $this->_xpath->query($this->_xpathQueryRss.'//link');
+        } else {
+            $list = $this->_xpath->query($this->_xpathQueryRdf.'//rss:link');
+        }
+
+        if (!$list->length) {
+            $links = $this->_atom->getLinks();
+        } else {
+            foreach ($list as $link) {
+                $links[] = $link->nodeValue;
+            }
+        }
+
+        $this->_data['links'] = $links;
+
+        return $this->_data['links'];
+    }
+
+    /**
+     * Get a permalink to the entry
+     *
+     * @return string
+     */
+    public function getPermalink()
+    {
+        return $this->getLink(0);
+    }
+
+    /**
+     * Get the entry title
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 
+            && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $title = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/title)');
+        } else {
+            $title = $this->_xpath->evaluate('string('.$this->_xpathQueryRdf.'/rss:title)');
+        }
+
+        if (!$title) {
+            $title = $this->_dc->getTitle();
+        }
+
+        if (!$title) {
+            $title = $this->_atom->getTitle();
+        }
+
+        if (!$title) {
+            $title = null;
+        }
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+    /**
+     * Get the number of comments/replies for current entry
+     *
+     * @return string|null
+     */
+    public function getCommentCount()
+    {
+        if (array_key_exists('commentcount', $this->_data)) {
+            return $this->_data['commentcount'];
+        }
+
+        $commentcount = $this->_slash->getCommentCount();
+
+        if (!$commentcount) {
+            $commentcount = $this->_thread->getCommentCount();
+        }
+
+        if (!$commentcount) {
+            $commentcount = $this->_atom->getCommentCount();
+        }
+
+        if (!$commentcount) {
+            $commentcount = null;
+        }
+
+        $this->_data['commentcount'] = $commentcount;
+
+        return $this->_data['commentcount'];
+    }
+
+    /**
+     * Returns a URI pointing to the HTML page where comments can be made on this entry
+     *
+     * @return string
+     */
+    public function getCommentLink()
+    {
+        if (array_key_exists('commentlink', $this->_data)) {
+            return $this->_data['commentlink'];
+        }
+
+        $commentlink = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 
+            && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $commentlink = $this->_xpath->evaluate('string('.$this->_xpathQueryRss.'/comments)');
+        }
+
+        if (!$commentlink) {
+            $commentlink = $this->_atom->getCommentLink();
+        }
+
+        if (!$commentlink) {
+            $commentlink = null;
+        }
+
+        $this->_data['commentlink'] = $commentlink;
+
+        return $this->_data['commentlink'];
+    }
+
+    /**
+     * Returns a URI pointing to a feed of all comments for this entry
+     *
+     * @return string
+     */
+    public function getCommentFeedLink()
+    {
+        if (array_key_exists('commentfeedlink', $this->_data)) {
+            return $this->_data['commentfeedlink'];
+        }
+
+        $commentfeedlink = $this->_wfw->getCommentFeedLink();
+
+        if (!$commentfeedlink) {
+            $commentfeedlink = $this->_atom->getCommentFeedLink('rss');
+        }
+
+        if (!$commentfeedlink) {
+            $commentfeedlink = $this->_atom->getCommentFeedLink('rdf');
+        }
+
+        if (!$commentfeedlink) {
+            $commentfeedlink = null;
+        }
+
+        $this->_data['commentfeedlink'] = $commentfeedlink;
+
+        return $this->_data['commentfeedlink'];
+    }
+
+    /**
+     * Set the XPath query (incl. on all Extensions)
+     *
+     * @param DOMXPath $xpath
+     */
+    public function setXpath(DOMXPath $xpath)
+    {
+        parent::setXpath($xpath);
+        $this->_dc->setXpath($this->_xpath);
+        $this->_content->setXpath($this->_xpath);
+        $this->_atom->setXpath($this->_xpath);
+        $this->_slash->setXpath($this->_xpath);
+        $this->_wfw->setXpath($this->_xpath);
+        $this->_thread->setXpath($this->_xpath);
+        foreach ($this->_extensions as $extension) {
+            $extension->setXpath($this->_xpath);
+        }
+    }
+}

+ 479 - 0
library/Zend/Feed/Reader/Extension/Atom/Entry.php

@@ -0,0 +1,479 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 16514 2009-07-05 19:59:03Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_EntryAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @see Zend_Date
+ */
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_Atom_Entry 
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+	/**
+     * Get the specified author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $authors = $this->_xpath->query(
+            $this->getXpathPrefix() . '//atom:author' . '|'
+                . $this->getXpathPrefix(). '//atom:contributor'
+        );
+
+        if (!$authors->length) {
+            $authors = $this->_xpath->query(
+                '//atom:author' . '|' . '//atom:contributor'
+            );
+        }
+
+        $people = array();
+
+        if ($authors->length) {
+            foreach ($authors as $author) {
+                $author = $this->_getAuthor($author);
+
+                if (!empty($author)) {
+                    $people[] = $author;
+                }
+            }
+        }
+
+        $people = array_unique($people);
+
+        $this->_data['authors'] = $people;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the entry content
+     *
+     * @return string
+     */
+    public function getContent()
+    {
+        if (array_key_exists('content', $this->_data)) {
+            return $this->_data['content'];
+        }
+
+        $content = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:content)');
+
+        if ($content) {
+            $content =  html_entity_decode($content, ENT_QUOTES, $this->getEncoding());
+        }
+
+        if (!$content) {
+            $content = $this->getDescription();
+        }
+
+        $this->_data['content'] = $content;
+
+        return $this->_data['content'];
+    }
+
+    /**
+     * Get the entry creation date
+     *
+     * @return string
+     */
+    public function getDateCreated()
+    {
+        if (array_key_exists('datecreated', $this->_data)) {
+            return $this->_data['datecreated'];
+        }
+
+        $date = null;
+
+        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $dateCreated = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)');
+        } else {
+            $dateCreated = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)');
+        }
+
+        if ($dateCreated) {
+            $date = new Zend_Date;
+            $date->set($dateCreated, Zend_Date::ISO_8601);
+        }
+
+        $this->_data['datecreated'] = $date;
+
+        return $this->_data['datecreated'];
+    }
+
+    /**
+     * Get the entry modification date
+     *
+     * @return string
+     */
+    public function getDateModified()
+    {
+        if (array_key_exists('datemodified', $this->_data)) {
+            return $this->_data['datemodified'];
+        }
+
+        $date = null;
+
+        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $dateModified = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)');
+        } else {
+            $dateModified = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)');
+        }
+
+        if ($dateModified) {
+            $date = new Zend_Date;
+            $date->set($dateModified, Zend_Date::ISO_8601);
+        }
+
+        $this->_data['datemodified'] = $date;
+
+        return $this->_data['datemodified'];
+    }
+
+    /**
+     * Get the entry description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:summary)');
+
+        if (!$description) {
+            $description = null;
+        } else {
+            $description = html_entity_decode($description, ENT_QUOTES, $this->getEncoding());
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the entry ID
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)');
+
+        if (!$id) {
+            if ($this->getPermalink()) {
+                $id = $this->getPermalink();
+            } elseif ($this->getTitle()) {
+                $id = $this->getTitle();
+            } else {
+                $id = null;
+            }
+        }
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get a specific link
+     *
+     * @param  int $index
+     * @return string
+     */
+    public function getLink($index = 0)
+    {
+        if (!array_key_exists('links', $this->_data)) {
+            $this->getLinks();
+        }
+
+        if (isset($this->_data['links'][$index])) {
+            return $this->_data['links'][$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get all links
+     *
+     * @return array
+     */
+    public function getLinks()
+    {
+        if (array_key_exists('links', $this->_data)) {
+            return $this->_data['links'];
+        }
+
+        $links = array();
+
+        $list = $this->_xpath->query(
+            $this->getXpathPrefix() . '//atom:link[@rel="alternate"]/@href' . '|' .
+            $this->getXpathPrefix() . '//atom:link[not(@rel)]/@href'
+        );
+
+        if ($list->length) {
+            foreach ($list as $link) {
+                $links[] = $link->value;
+            }
+        }
+
+        $this->_data['links'] = $links;
+
+        return $this->_data['links'];
+    }
+
+    /**
+     * Get a permalink to the entry
+     *
+     * @return string
+     */
+    public function getPermalink()
+    {
+        return $this->getLink(0);
+    }
+
+    /**
+     * Get the entry title
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)');
+
+        if (!$title) {
+            $title = null;
+        } else {
+            $title = html_entity_decode($title, ENT_QUOTES, $this->getEncoding());
+        }
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+    /**
+     * Get the number of comments/replies for current entry
+     *
+     * @return integer
+     */
+    public function getCommentCount()
+    {
+        if (array_key_exists('commentcount', $this->_data)) {
+            return $this->_data['commentcount'];
+        }
+
+        $count = null;
+
+        $this->_xpath->registerNamespace('thread10', 'http://purl.org/syndication/thread/1.0');
+        $list = $this->_xpath->query(
+            $this->getXpathPrefix() . '//atom:link[@rel="replies"]/@thread10:count'
+        );
+
+        if ($list->length) {
+            $count = $list->item(0)->value;
+        }
+
+        $this->_data['commentcount'] = $count;
+
+        return $this->_data['commentcount'];
+    }
+
+    /**
+     * Returns a URI pointing to the HTML page where comments can be made on this entry
+     *
+     * @return string
+     */
+    public function getCommentLink()
+    {
+        if (array_key_exists('commentlink', $this->_data)) {
+            return $this->_data['commentlink'];
+        }
+
+        $link = null;
+
+        $list = $this->_xpath->query(
+            $this->getXpathPrefix() . '//atom:link[@rel="replies" and @type="text/html"]/@href'
+        );
+
+        if ($list->length) {
+            $link = $list->item(0)->value;
+        }
+
+        $this->_data['commentlink'] = $link;
+
+        return $this->_data['commentlink'];
+    }
+
+    /**
+     * Returns a URI pointing to a feed of all comments for this entry
+     *
+     * @return string
+     */
+    public function getCommentFeedLink($type = 'atom')
+    {
+        if (array_key_exists('commentfeedlink', $this->_data)) {
+            return $this->_data['commentfeedlink'];
+        }
+
+        $link = null;
+
+        $list = $this->_xpath->query(
+            $this->getXpathPrefix() . '//atom:link[@rel="replies" and @type="application/'.$type.'+xml"]/@href'
+        );
+
+        if ($list->length) {
+            $link = $list->item(0)->value;
+        }
+
+        $this->_data['commentfeedlink'] = $link;
+
+        return $this->_data['commentfeedlink'];
+    }
+
+    /**
+     * Get an author entry
+     *
+     * @param DOMElement $element
+     * @return string
+     */
+    protected function _getAuthor(DOMElement $element)
+    {
+        $email = null;
+        $name  = null;
+        $uri   = null;
+
+        $emailNode = $element->getElementsByTagName('email');
+        $nameNode  = $element->getElementsByTagName('name');
+        $uriNode   = $element->getElementsByTagName('uri');
+
+        if ($emailNode->length) {
+            $email = $emailNode->item(0)->nodeValue;
+        }
+
+        if ($nameNode->length) {
+            $name = $nameNode->item(0)->nodeValue;
+        }
+
+        if ($uriNode->length) {
+            $uri = $uriNode->item(0)->nodeValue;
+        }
+
+        if (!empty($email)) {
+            return $email . (empty($name) ? '' : ' (' . $name . ')');
+        } else if (!empty($name)) {
+            return $name;
+        } else if (!empty($uri)) {
+            return $uri;
+        }
+
+        return null;
+    }
+
+    /**
+     * Register the default namespaces for the current feed format
+     */
+    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) {
+            case Zend_Feed_Reader::TYPE_ATOM_03:
+                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_03);
+                break;
+            default:
+                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_10);
+                break;
+        }
+    }
+
+    /**
+     * Detect the presence of any Atom namespaces in use
+     */
+    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;
+            }
+        }
+    }
+}

+ 441 - 0
library/Zend/Feed/Reader/Extension/Atom/Feed.php

@@ -0,0 +1,441 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Feed.php 16514 2009-07-05 19:59:03Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader_Extension_FeedAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/FeedAbstract.php';
+
+/**
+ * @see Zend_Date
+ */
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_Atom_Feed 
+    extends Zend_Feed_Reader_Extension_FeedAbstract
+{
+    /**
+     * Get a single author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $authors = $this->_xpath->query('//atom:author');
+        $contributors = $this->_xpath->query('//atom:contributor');
+
+        $people = array();
+
+        if ($authors->length) {
+            foreach ($authors as $author) {
+                $author = $this->_getAuthor($author);
+
+                if (!empty($author)) {
+                    $people[] = $author;
+                }
+            }
+        }
+
+        if ($contributors->length) {
+            foreach ($contributors as $contributor) {
+                $contributor = $this->_getAuthor($contributor);
+
+                if (!empty($contributor)) {
+                    $people[] = $contributor;
+                }
+            }
+        }
+
+        if (empty($people)) {
+            $people = null;
+        } else {
+            $people = array_unique($people);
+        }
+
+        $this->_data['authors'] = $people;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the copyright entry
+     *
+     * @return string|null
+     */
+    public function getCopyright()
+    {
+        if (array_key_exists('copyright', $this->_data)) {
+            return $this->_data['copyright'];
+        }
+
+        $copyright = null;
+
+        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $copyright = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:copyright)');
+        } else {
+            $copyright = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:rights)');
+        }
+
+        if (!$copyright) {
+            $copyright = null;
+        }
+
+        $this->_data['copyright'] = $copyright;
+
+        return $this->_data['copyright'];
+    }
+
+    /**
+     * Get the feed creation date
+     *
+     * @return Zend_Date|null
+     */
+    public function getDateCreated()
+    {
+        if (array_key_exists('datecreated', $this->_data)) {
+            return $this->_data['datecreated'];
+        }
+
+        $date = null;
+
+        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $dateCreated = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)');
+        } else {
+            $dateCreated = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)');
+        }
+
+        if ($dateCreated) {
+            $date = new Zend_Date;
+            $date->set($dateCreated, Zend_Date::ISO_8601);
+        }
+
+        $this->_data['datecreated'] = $date;
+
+        return $this->_data['datecreated'];
+    }
+
+    /**
+     * Get the feed modification date
+     *
+     * @return Zend_Date|null
+     */
+    public function getDateModified()
+    {
+        if (array_key_exists('datemodified', $this->_data)) {
+            return $this->_data['datemodified'];
+        }
+
+        $date = null;
+
+        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $dateModified = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)');
+        } else {
+            $dateModified = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)');
+        }
+
+        if ($dateModified) {
+            $date = new Zend_Date;
+            $date->set($dateModified, Zend_Date::ISO_8601);
+        }
+
+        $this->_data['datemodified'] = $date;
+
+        return $this->_data['datemodified'];
+    }
+
+    /**
+     * Get the feed description
+     *
+     * @return string|null
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = null;
+
+        if ($this->getType() === Zend_Feed_Reader::TYPE_ATOM_03) {
+            $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:tagline)'); // TODO: Is this the same as subtitle?
+        } else {
+            $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:subtitle)');
+        }
+
+        if (!$description) {
+            $description = null;
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the feed generator entry
+     *
+     * @return string|null
+     */
+    public function getGenerator()
+    {
+        if (array_key_exists('generator', $this->_data)) {
+            return $this->_data['generator'];
+        }
+        // TODO: Add uri support
+        $generator = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:generator)');
+
+        if (!$generator) {
+            $generator = null;
+        } else {
+            $generator = html_entity_decode($generator, ENT_QUOTES, $this->getEncoding());
+        }
+
+        $this->_data['generator'] = $generator;
+
+        return $this->_data['generator'];
+    }
+
+	/**
+     * Get the feed ID
+     *
+     * @return string|null
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)');
+
+        if (!$id) {
+            if ($this->getLink()) {
+                $id = $this->getLink();
+            } elseif ($this->getTitle()) {
+                $id = $this->getTitle();
+            } else {
+                $id = null;
+            }
+        }
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get the feed language
+     *
+     * @return string|null
+     */
+    public function getLanguage()
+    {
+        if (array_key_exists('language', $this->_data)) {
+            return $this->_data['language'];
+        }
+
+        $language = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:lang)');
+
+        if (!$language) {
+            $language = $this->_xpath->evaluate('string(//@xml:lang[1])');
+        }
+
+        if (!$language) {
+            $language = null;
+        }
+
+        $this->_data['language'] = $language;
+
+        return $this->_data['language'];
+    }
+
+    /**
+     * Get a link to the source website
+     *
+     * @return string|null
+     */
+    public function getLink()
+    {
+        if (array_key_exists('link', $this->_data)) {
+            return $this->_data['link'];
+        }
+
+        $link = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:link/@href)');
+
+        if (!$link) {
+            $link = null;
+        }
+
+        $this->_data['link'] = $link;
+
+        return $this->_data['link'];
+    }
+
+    /**
+     * Get a link to the feed's XML Url
+     *
+     * @return string|null
+     */
+    public function getFeedLink()
+    {
+        if (array_key_exists('feedlink', $this->_data)) {
+            return $this->_data['feedlink'];
+        }
+
+        $link = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:link[@rel="self"]/@href)');
+
+        if (!$link) {
+            $link = null;
+        }
+
+        $this->_data['feedlink'] = $link;
+
+        return $this->_data['feedlink'];
+    }
+
+    /**
+     * Get the feed title
+     *
+     * @return string|null
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)');
+
+        if (!$title) {
+            $title = null;
+        }
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+	/**
+     * Get an author entry in RSS format
+     *
+     * @param  DOMElement $element
+     * @return string
+     */
+    protected function _getAuthor(DOMElement $element)
+    {
+        $email = null;
+        $name  = null;
+        $uri   = null;
+
+        $emailNode = $element->getElementsByTagName('email');
+        $nameNode  = $element->getElementsByTagName('name');
+        $uriNode   = $element->getElementsByTagName('uri');
+
+        if ($emailNode->length) {
+            $email = $emailNode->item(0)->nodeValue;
+        }
+
+        if ($nameNode->length) {
+            $name = $nameNode->item(0)->nodeValue;
+        }
+
+        if ($uriNode->length) {
+            $uri = $uriNode->item(0)->nodeValue;
+        }
+
+        if (!empty($email)) {
+            return $email . (empty($name) ? '' : ' (' . $name . ')');
+        } else if (!empty($name)) {
+            return $name;
+        } else if (!empty($uri)) {
+            return $uri;
+        }
+
+        return null;
+    }
+
+    /**
+     * Register the default namespaces for the current feed format
+     */
+    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) {
+            case Zend_Feed_Reader::TYPE_ATOM_03:
+                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_03);
+                break;
+            default:
+                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_10);
+                break;
+        }
+    }
+
+    /**
+     * Detect the presence of any Atom namespaces in use
+     */
+    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;
+            }
+        }
+    }
+}

+ 64 - 0
library/Zend/Feed/Reader/Extension/Content/Entry.php

@@ -0,0 +1,64 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 16506 2009-07-05 12:56:06Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Abstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_Content_Entry 
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+
+    public function getContent()
+    {
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 
+            && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $content = $this->_xpath->evaluate('string('.$this->getXpathPrefix().'/content:encoded)');
+        } else {
+            $content = $this->_xpath->evaluate('string('.$this->getXpathPrefix().'/content:encoded)');
+        }
+        if ($content) {
+            $content = html_entity_decode($content, ENT_QUOTES, $this->getEncoding());
+        }
+        return $content;
+    }
+
+    /**
+     * Register RSS Content Module namespace
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/');
+    }
+}

+ 97 - 0
library/Zend/Feed/Reader/Extension/CreativeCommons/Entry.php

@@ -0,0 +1,97 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 16514 2009-07-05 19:59:03Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader_Extension_EntryAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_CreativeCommons_Feed
+ */
+require_once 'Zend/Feed/Reader/Extension/CreativeCommons/Feed.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_CreativeCommons_Entry extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+    /**
+     * Get the entry license
+     *
+     * @return string|null
+     */
+    public function getLicense($index = 0)
+    {
+        $licenses = $this->getLicenses();
+
+        if (isset($licenses[$index])) {
+            return $licenses[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the entry licenses
+     *
+     * @return array
+     */
+    public function getLicenses()
+    {
+        $name = 'licenses';
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        }
+
+        $licenses = array();
+        $list = $this->_xpath->evaluate($this->getXpathPrefix() . '//cc:license');
+
+        if ($list->length) {
+            foreach ($list as $license) {
+                    $licenses[] = $license->nodeValue;
+            }
+
+            $licenses = array_unique($licenses);
+        } else {
+            $cc = new Zend_Feed_Reader_Extension_CreativeCommons_Feed(
+                $this->_domDocument, $this->_data['type'], $this->_xpath
+            );
+            $licenses = $cc->getLicenses();
+        }
+
+        $this->_data[$name] = $licenses;
+
+        return $this->_data[$name];
+    }
+
+    /**
+     * Register Creative Commons namespaces
+     *
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('cc', 'http://backend.userland.com/creativeCommonsRssModule');
+    }
+}

+ 89 - 0
library/Zend/Feed/Reader/Extension/CreativeCommons/Feed.php

@@ -0,0 +1,89 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Feed.php 16528 2009-07-06 12:02:17Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader_Extension_FeedAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/FeedAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_CreativeCommons_Feed 
+    extends Zend_Feed_Reader_Extension_FeedAbstract
+{
+    /**
+     * Get the entry license
+     *
+     * @return string|null
+     */
+    public function getLicense($index = 0)
+    {
+        $licenses = $this->getLicenses();
+
+        if (isset($licenses[$index])) {
+            return $licenses[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the entry licenses
+     *
+     * @return array
+     */
+    public function getLicenses()
+    {
+        $name = 'licenses';
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        }
+
+        $licenses = array();
+        $list = $this->_xpath->evaluate('channel/cc:license');
+
+        if ($list->length) {
+            foreach ($list as $license) {
+                $licenses[] = $license->nodeValue;
+            }
+
+            $licenses = array_unique($licenses);
+        }
+
+        $this->_data[$name] = $licenses;
+
+        return $this->_data[$name];
+    }
+
+    /**
+     * Register Creative Commons namespaces
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('cc', 'http://backend.userland.com/creativeCommonsRssModule');
+    }
+}

+ 232 - 0
library/Zend/Feed/Reader/Extension/DublinCore/Entry.php

@@ -0,0 +1,232 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_EntryAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @see Zend_Date
+ */
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_DublinCore_Entry 
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+    /**
+     * Get an author entry
+     *
+     * @param DOMElement $element
+     * @return string
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $authors = array();
+        $list = $this->_xpath->evaluate($this->getXpathPrefix() . '//dc11:creator');
+
+        if (!$list->length) {
+            $list = $this->_xpath->evaluate($this->getXpathPrefix() . '//dc10:creator');
+        }
+        if (!$list->length) {
+            $list = $this->_xpath->evaluate($this->getXpathPrefix() . '//dc11:publisher');
+
+            if (!$list->length) {
+                $list = $this->_xpath->evaluate($this->getXpathPrefix() . '//dc10:publisher');
+            }
+        }
+
+        if ($list->length) {
+            foreach ($list as $author) {
+                if ($this->getType() == Zend_Feed_Reader::TYPE_RSS_20 
+                    && preg_match("/\(([^\)]+)\)/", $author->nodeValue, $matches, PREG_OFFSET_CAPTURE)
+                ) {
+                    $authors[] = $matches[1][0];
+                } else {
+                    $authors[] = $author->nodeValue;
+                }
+            }
+
+            $authors = array_unique($authors);
+        }
+
+        $this->_data['authors'] = $authors;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the entry content
+     *
+     * @return string
+     */
+    public function getContent()
+    {
+        return $this->getDescription();
+    }
+
+    /**
+     * Get the entry description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = null;
+        $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:description)');
+
+        if (!$description) {
+            $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:description)');
+        }
+
+        if (!$description) {
+            $description = null;
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the entry ID
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = null;
+        $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:identifier)');
+
+        if (!$id) {
+            $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:identifier)');
+        }
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get the entry title
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = null;
+        $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:title)');
+
+        if (!$title) {
+            $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:title)');
+        }
+
+        if (!$title) {
+            $title = null;
+        }
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+    /**
+     *
+     *
+     * @return Zend_Date|null
+     */
+    public function getDate()
+    {
+        if (array_key_exists('date', $this->_data)) {
+            return $this->_data['date'];
+        }
+
+        $d    = null;
+        $date = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:date)');
+
+        if (!$date) {
+            $date = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:date)');
+        }
+
+        if ($date) {
+            $d = new Zend_Date;
+            $d->set($date, Zend_Date::ISO_8601);
+        }
+
+        $this->_data['date'] = $d;
+
+        return $this->_data['date'];
+    }
+
+    /**
+     * Register DC namespaces
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('dc10', 'http://purl.org/dc/elements/1.0/');
+        $this->_xpath->registerNamespace('dc11', 'http://purl.org/dc/elements/1.1/');
+    }
+}

+ 265 - 0
library/Zend/Feed/Reader/Extension/DublinCore/Feed.php

@@ -0,0 +1,265 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader_Extension_FeedAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/FeedAbstract.php';
+
+/**
+ * @see Zend_Date
+ */
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_DublinCore_Feed 
+    extends Zend_Feed_Reader_Extension_FeedAbstract
+{
+	/**
+     * Get a single author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $authors = array();
+        $list    = $this->_xpath->query('//dc11:creator');
+
+        if (!$list->length) {
+            $list = $this->_xpath->query('//dc10:creator');
+        }
+        if (!$list->length) {
+            $list = $this->_xpath->query('//dc11:publisher');
+
+            if (!$list->length) {
+                $list = $this->_xpath->query('//dc10:publisher');
+            }
+        }
+
+        foreach ($list as $authorObj) {
+            $authors[] = $authorObj->nodeValue;
+        }
+
+        if (!empty($authors)) {
+            $authors = array_unique($authors);
+        }
+
+        $this->_data['authors'] = $authors;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the copyright entry
+     *
+     * @return string|null
+     */
+    public function getCopyright()
+    {
+        if (array_key_exists('copyright', $this->_data)) {
+            return $this->_data['copyright'];
+        }
+
+        $copyright = null;
+        $copyright = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:rights)');
+
+        if (!$copyright) {
+            $copyright = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:rights)');
+        }
+
+        if (!$copyright) {
+            $copyright = null;
+        }
+
+        $this->_data['copyright'] = $copyright;
+
+        return $this->_data['copyright'];
+    }
+
+    /**
+     * Get the feed description
+     *
+     * @return string|null
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = null;
+        $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:description)');
+
+        if (!$description) {
+            $description = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:description)');
+        }
+
+        if (!$description) {
+            $description = null;
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the feed ID
+     *
+     * @return string|null
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = null;
+        $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:identifier)');
+
+        if (!$id) {
+            $id = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:identifier)');
+        }
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get the feed language
+     *
+     * @return string|null
+     */
+    public function getLanguage()
+    {
+        if (array_key_exists('language', $this->_data)) {
+            return $this->_data['language'];
+        }
+
+        $language = null;
+        $language = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:language)');
+
+        if (!$language) {
+            $language = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:language)');
+        }
+
+        if (!$language) {
+            $language = null;
+        }
+
+        $this->_data['language'] = $language;
+
+        return $this->_data['language'];
+    }
+
+    /**
+     * Get the feed title
+     *
+     * @return string|null
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = null;
+        $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:title)');
+
+        if (!$title) {
+            $title = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:title)');
+        }
+
+        if (!$title) {
+            $title = null;
+        }
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+    /**
+     *
+     *
+     * @return Zend_Date|null
+     */
+    public function getDate()
+    {
+        if (array_key_exists('date', $this->_data)) {
+            return $this->_data['date'];
+        }
+
+        $d = null;
+        $date = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc11:date)');
+
+        if (!$date) {
+            $date = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/dc10:date)');
+        }
+
+        if ($date) {
+            $d = new Zend_Date;
+            $d->set($date, Zend_Date::ISO_8601);
+        }
+
+        $this->_data['date'] = $d;
+
+        return $this->_data['date'];
+    }
+
+    /**
+     * Register the default namespaces for the current feed format
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('dc10', 'http://purl.org/dc/elements/1.0/');
+        $this->_xpath->registerNamespace('dc11', 'http://purl.org/dc/elements/1.1/');
+    }
+}

+ 197 - 0
library/Zend/Feed/Reader/Extension/EntryAbstract.php

@@ -0,0 +1,197 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Feed_Reader_Extension_EntryAbstract
+{
+    /**
+     * Feed entry data
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * DOM document object
+     *
+     * @var DOMDocument
+     */
+    protected $_domDocument = null;
+
+    /**
+     * Entry instance
+     *
+     * @var Zend_Feed_Entry_Abstract
+     */
+    protected $_entry = null;
+
+    /**
+     * Pointer to the current entry
+     *
+     * @var int
+     */
+    protected $_entryKey = 0;
+
+    /**
+     * XPath object
+     *
+     * @var DOMXPath
+     */
+    protected $_xpath = null;
+
+    /**
+     * XPath query
+     *
+     * @var string
+     */
+    protected $_xpathPrefix = '';
+
+    /**
+     * Constructor
+     *
+     * @param  Zend_Feed_Entry_Abstract $entry
+     * @param  int $entryKey
+     * @param  string $type
+     * @return void
+     */
+    public function __construct(DOMElement $entry, $entryKey, $type = null)
+    {
+        $this->_entry       = $entry;
+        $this->_entryKey    = $entryKey;
+        $this->_domDocument = $entry->ownerDocument;
+
+        if (!is_null($type)) {
+            $this->_data['type'] = $type;
+        } else {
+            $this->_data['type'] = Zend_Feed_Reader::detectType($feed);
+        }
+        // set the XPath query prefix for the entry being queried
+        if ($this->getType() == Zend_Feed_Reader::TYPE_RSS_10 
+            || $this->getType() == Zend_Feed_Reader::TYPE_RSS_090
+        ) {
+            $this->setXpathPrefix('//rss:item[' . ($this->_entryKey+1) . ']');
+        } elseif ($this->getType() == Zend_Feed_Reader::TYPE_ATOM_10 
+                  || $this->getType() == Zend_Feed_Reader::TYPE_ATOM_03
+        ) {
+            $this->setXpathPrefix('//atom:entry[' . ($this->_entryKey+1) . ']');
+        } else {
+            $this->setXpathPrefix('//item[' . ($this->_entryKey+1) . ']');
+        }
+    }
+
+    /**
+     * Get the DOM
+     *
+     * @return DOMDocument
+     */
+    public function getDomDocument()
+    {
+        return $this->_domDocument;
+    }
+
+    /**
+     * Get the Entry's encoding
+     *
+     * @return string
+     */
+    public function getEncoding()
+    {
+        $assumed = $this->getDomDocument()->encoding;
+        return $assumed;
+    }
+
+	/**
+     * Get the entry type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->_data['type'];
+    }
+
+    /**
+     * Set the XPath query
+     *
+     * @param  DOMXPath $xpath
+     * @return Zend_Feed_Reader_Extension_EntryAbstract
+     */
+    public function setXpath(DOMXPath $xpath)
+    {
+        $this->_xpath = $xpath;
+        $this->_registerNamespaces();
+        return $this;
+    }
+
+    /**
+     * Get the XPath query object
+     *
+     * @return DOMXPath
+     */
+    public function getXpath()
+    {
+        return $this->_xpath;
+    }
+
+    /**
+     * Serialize the entry to an array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Get the XPath prefix
+     *
+     * @return string
+     */
+    public function getXpathPrefix()
+    {
+        return $this->_xpathPrefix;
+    }
+
+    /**
+     * Set the XPath prefix
+	 *
+	 * @param  string $prefix
+     * @return Zend_Feed_Reader_Extension_EntryAbstract
+     */
+    public function setXpathPrefix($prefix)
+    {
+        $this->_xpathPrefix = $prefix;
+        return $this;
+    }
+
+    /**
+     * Register XML namespaces
+     * 
+     * @return void
+     */
+    protected abstract function _registerNamespaces();
+}

+ 166 - 0
library/Zend/Feed/Reader/Extension/FeedAbstract.php

@@ -0,0 +1,166 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Atom
+ */
+require_once 'Zend/Feed/Reader/Entry/Atom.php';
+
+
+/**
+ * @see Zend_Feed_Reader_Entry_Rss
+ */
+require_once 'Zend/Feed/Reader/Entry/Rss.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Feed_Reader_Extension_FeedAbstract
+{
+	/**
+     * Parsed feed data
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Parsed feed data in the shape of a DOMDocument
+     *
+     * @var DOMDocument
+     */
+    protected $_domDocument = null;
+
+    /**
+     * The base XPath query used to retrieve feed data
+     *
+     * @var DOMXPath
+     */
+    protected $_xpath = null;
+
+    /**
+     * The XPath prefix
+     *
+     * @var string
+     */
+    protected $_xpathPrefix = '';
+
+    /**
+     * Constructor
+     *
+     * @param  Zend_Feed_Abstract $feed The source Zend_Feed object
+     * @param  string $type Feed type
+     * @return void
+     */
+    public function __construct(DomDocument $dom, $type = null, DOMXPath $xpath = null)
+    {
+        $this->_domDocument = $dom;
+
+        if ($type !== null) {
+            $this->_data['type'] = $type;
+        } else {
+            $this->_data['type'] = Zend_Feed_Reader::detectType($dom);
+        }
+
+        if ($xpath !== null) {
+            $this->_xpath = $xpath;
+        } else {
+            $this->_xpath = new DOMXPath($this->_domDocument);
+        }
+
+        $this->_registerNamespaces();
+    }
+
+    /**
+     * Get the DOM
+     *
+     * @return DOMDocument
+     */
+    public function getDomDocument()
+    {
+        return $this->_domDocument;
+    }
+
+    /**
+     * Get the Feed's encoding
+     *
+     * @return string
+     */
+    public function getEncoding()
+    {
+        $assumed = $this->getDomDocument()->encoding;
+        return $assumed;
+    }
+
+    /**
+     * Get the feed type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->_data['type'];
+    }
+
+
+    /**
+     * Return the feed as an array
+     *
+     * @return array
+     */
+    public function toArray() // untested
+    {
+        return $this->_data;
+    }
+
+        /**
+     * Get the XPath prefix
+	 *
+	 * @return string
+     */
+    public function getXpathPrefix()
+    {
+        return $this->_xpathPrefix;
+    }
+
+    /**
+     * Set the XPath prefix
+     *
+     * @return Zend_Feed_Reader_Feed_Atom
+     */
+    public function setXpathPrefix($prefix)
+    {
+        $this->_xpathPrefix = $prefix;
+    }
+
+    /**
+     * Register the default namespaces for the current feed format
+     */
+    abstract protected function _registerNamespaces();
+}

+ 144 - 0
library/Zend/Feed/Reader/Extension/Slash/Entry.php

@@ -0,0 +1,144 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 16514 2009-07-05 19:59:03Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_EntryAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_Slash_Entry 
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+    /**
+     * Get the entry section
+     *
+     * @return string|null
+     */
+    public function getSection()
+    {
+        return $this->_getData('section');
+    }
+
+    /**
+     * Get the entry department
+     *
+     * @return string|null
+     */
+    public function getDepartment()
+    {
+        return $this->_getData('department');
+    }
+
+    /**
+     * Get the entry hit_parade
+     *
+     * @return array
+     */
+    public function getHitParade()
+    {
+        $name = 'hit_parade';
+
+        if (isset($this->_data[$name])) {
+            return $this->_data[$name];
+        }
+
+        $stringParade = $this->_getData($name);
+        $hitParade    = array();
+
+        if (!empty($stringParade)) {
+            $stringParade = explode(',', $stringParade);
+
+            foreach ($stringParade as $hit)
+                $hitParade[] = $hit + 0; //cast to integer
+        }
+
+        $this->_data[$name] = $hitParade;
+        return $hitParade;
+    }
+
+    /**
+     * Get the entry comments
+     *
+     * @return int
+     */
+    public function getCommentCount()
+    {
+        $name = 'comments';
+
+        if (isset($this->_data[$name])) {
+            return $this->_data[$name];
+        }
+
+        $comments = $this->_getData($name, 'string');
+
+        if (!$comments) {
+            $this->_data[$name] = null;
+            return $this->_data[$name];
+        }
+
+        return $comments;
+    }
+
+    /**
+     * Get the entry data specified by name
+     * @param string $name
+     * @param string $type
+     *
+     * @return mixed|null
+     */
+    protected function _getData($name, $type = 'string')
+    {
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        }
+
+        $data = $this->_xpath->evaluate($type . '(' . $this->getXpathPrefix() . '/slash10:' . $name . ')');
+
+        if (!$data) {
+            $data = null;
+        }
+
+        $this->_data[$name] = $data;
+
+        return $data;
+    }
+
+    /**
+     * Register Slash namespaces
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('slash10', 'http://purl.org/rss/1.0/modules/slash/');
+    }
+}

+ 168 - 0
library/Zend/Feed/Reader/Extension/Syndication/Feed.php

@@ -0,0 +1,168 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Feed.php 16528 2009-07-06 12:02:17Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader_Extension_FeedAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/FeedAbstract.php';
+
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_Syndication_Feed 
+    extends Zend_Feed_Reader_Extension_FeedAbstract
+{
+    /**
+     * Get update period
+     * @return string
+     */
+    public function getUpdatePeriod()
+    {
+        $name = 'updatePeriod';
+        $period = $this->_getData($name);
+
+        if ($period === null) {
+            $this->_data[$name] = 'daily';
+            return 'daily'; //Default specified by spec
+        }
+
+        switch ($period)
+        {
+            case 'hourly':
+            case 'daily':
+            case 'weekly':
+            case 'yearly':
+                return $period;
+            default:
+                throw new Zend_Feed_Exception("Feed specified invalid update period: '$period'." 
+                    .  " Must be one of hourly, daily, weekly or yearly"
+                );
+        }
+    }
+
+    /**
+     * Get update frequency
+     * @return int
+     */
+    public function getUpdateFrequency()
+    {
+        $name = 'updateFrequency';
+        $freq = $this->_getData($name, 'number');
+
+        if (!$freq || $freq < 1) {
+            $this->_data[$name] = 1;
+            return 1;
+        }
+
+        return $freq;
+    }
+
+    /**
+     * Get update frequency as ticks
+     * @return int
+     */
+    public function getUpdateFrequencyAsTicks()
+    {
+        $name = 'updateFrequency';
+        $freq = $this->_getData($name, 'number');
+
+        if (!$freq || $freq < 1) {
+            $this->_data[$name] = 1;
+            $freq = 1;
+        }
+
+        $period = $this->getUpdatePeriod();
+        $ticks = 1;
+
+        switch ($period)
+        {
+            //intentional fall through
+            case 'yearly': 
+                $ticks *= 52; //TODO: fix generalisation, how?
+            case 'weekly': 
+                $ticks *= 7;
+            case 'daily': 
+                $ticks *= 24;
+            case 'hourly': 
+                $ticks *= 3600;
+                break;
+            default: //Never arrive here, exception thrown in getPeriod()
+                break;
+        }
+
+        return $ticks / $freq;
+    }
+
+    /**
+     * Get update base
+     *
+     * @return Zend_Date|null
+     */
+    public function getUpdateBase()
+    {
+        $updateBase = $this->_getData('updateBase');
+        $date = null;
+        if ($updateBase) {
+            $date = new Zend_Date;
+            $date->set($updateBase, Zend_Date::W3C);
+        }
+        return $date;
+    }
+
+    /**
+     * Get the entry data specified by name
+     *
+     * @param string $name
+     * @param string $type
+     * @return mixed|null
+     */
+    private function _getData($name, $type = 'string')
+    {
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        }
+
+        $data = $this->_xpath->evaluate($type . '(' . $this->getXpathPrefix() . '/syn10:' . $name . ')');
+
+        if (!$data) {
+            $data = null;
+        }
+
+        $this->_data[$name] = $data;
+
+        return $data;
+    }
+
+    /**
+     * Register Syndication namespaces
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('syn10', 'http://purl.org/rss/1.0/modules/syndication/');
+    }
+}

+ 91 - 0
library/Zend/Feed/Reader/Extension/Thread/Entry.php

@@ -0,0 +1,91 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 16514 2009-07-05 19:59:03Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader_Extension_EntryAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_Thread_Entry 
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+    /**
+     * Get the "in-reply-to" value
+     * 
+     * @return string
+     */
+    public function getInReplyTo()
+    {
+        // TODO: to be implemented
+    }
+
+    // TODO: Implement "replies" and "updated" constructs from standard
+
+    /**
+     * Get the total number of threaded responses (i.e comments)
+     *
+     * @return int|null
+     */
+    public function getCommentCount()
+    {
+        return $this->_getData('total');
+    }
+
+    /**
+     * Get the entry data specified by name
+     *
+     * @param  string $name
+     * @param  string $type
+     * @return mixed|null
+     */
+    protected function _getData($name)
+    {
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        }
+
+        $data = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/thread10:' . $name . ')');
+
+        if (!$data) {
+            $data = null;
+        }
+
+        $this->_data[$name] = $data;
+
+        return $data;
+    }
+
+    /**
+     * Register Atom Thread Extension 1.0 namespace
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('thread10', 'http://purl.org/syndication/thread/1.0');
+    }
+}

+ 73 - 0
library/Zend/Feed/Reader/Extension/WellFormedWeb/Entry.php

@@ -0,0 +1,73 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 16514 2009-07-05 19:59:03Z padraic $
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_EntryAbstract
+ */
+require_once 'Zend/Feed/Reader/Extension/EntryAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Extension_WellFormedWeb_Entry 
+    extends Zend_Feed_Reader_Extension_EntryAbstract
+{
+    /**
+     * Get the entry comment Uri
+     *
+     * @return string|null
+     */
+    public function getCommentFeedLink()
+    {
+        $name = 'commentRss';
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        }
+
+        $data = $this->_xpath->evaluate('string(' . $this->getXpathPrefix() . '/wfw:' . $name . ')');
+
+        if (!$data) {
+            $data = null;
+        }
+
+        $this->_data[$name] = $data;
+
+        return $data;
+    }
+
+    /**
+     * Register Slash namespaces
+     *
+     * @return void
+     */
+    protected function _registerNamespaces()
+    {
+        $this->_xpath->registerNamespace('wfw', 'http://wellformedweb.org/CommentAPI/');
+    }
+}

+ 260 - 0
library/Zend/Feed/Reader/Feed/Abstract.php

@@ -0,0 +1,260 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader
+ */
+require_once 'Zend/Feed/Reader.php';
+
+/**
+ * @see Zend_Feed_Reader_Entry_Atom
+ */
+require_once 'Zend/Feed/Reader/Entry/Atom.php';
+
+
+/**
+ * @see Zend_Feed_Reader_Entry_Rss
+ */
+require_once 'Zend/Feed/Reader/Entry/Rss.php';
+
+/**
+ * @see Zend_feed_Reader_Feed_Interface
+ */
+require_once 'Zend/Feed/Reader/Feed/Interface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Feed_Reader_Feed_Abstract implements Zend_Feed_Reader_Feed_Interface
+{
+	/**
+     * Parsed feed data
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Parsed feed data in the shape of a DOMDocument
+     *
+     * @var DOMDocument
+     */
+    protected $_domDocument = null;
+
+    /**
+     * An array of parsed feed entries
+     *
+     * @var array
+     */
+    protected $_entries = array();
+
+    /**
+     * A pointer for the iterator to keep track of the entries array
+     *
+     * @var int
+     */
+    protected $_entriesKey = 0;
+
+    /**
+     * The base XPath query used to retrieve feed data
+     *
+     * @var DOMXPath
+     */
+    protected $_xpath = null;
+
+    protected $_extensions = array();
+
+    /**
+     * Constructor
+     *
+     * @param DomDocument The DOM object for the feed's XML
+     * @param string $type Feed type
+     */
+    public function __construct(DomDocument $domDocument, $type = null)
+    {
+        $this->_domDocument = $domDocument;
+        $this->_xpath = new DOMXPath($this->_domDocument);
+
+        if ($type !== null) {
+            $this->_data['type'] = $type;
+        } else {
+            $this->_data['type'] = Zend_Feed_Reader::detectType($this->_domDocument);
+        }
+        $this->_registerNamespaces();
+        $this->_indexEntries();
+        $this->_loadExtensions();
+    }
+
+	/**
+     * Get the number of feed entries.
+     * Required by the Iterator interface.
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_entries);
+    }
+
+	/**
+     * Return the current entry
+     *
+     * @return Zend_Feed_Reader_Entry_Interface
+     */
+    public function current()
+    {
+        if (substr($this->getType(), 0, 3) == 'rss') {
+            $reader = new Zend_Feed_Reader_Entry_Rss($this->_entries[$this->key()], $this->key(), $this->getType());
+        } else {
+            $reader = new Zend_Feed_Reader_Entry_Atom($this->_entries[$this->key()], $this->key(), $this->getType());
+        }
+
+        $reader->setXpath($this->_xpath);
+
+        return $reader;
+    }
+
+    /**
+     * Get the DOM
+     *
+     * @return DOMDocument
+     */
+    public function getDomDocument()
+    {
+        return $this->_domDocument;
+    }
+
+    /**
+     * Get the Feed's encoding
+     *
+     * @return string
+     */
+    public function getEncoding()
+    {
+        $assumed = $this->getDomDocument()->encoding;
+        return $assumed;
+    }
+
+    /**
+     * Get the feed type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->_data['type'];
+    }
+
+	/**
+     * Return the current feed key
+     *
+     * @return unknown
+     */
+    public function key()
+    {
+        return $this->_entriesKey;
+    }
+
+	/**
+     * Move the feed pointer forward
+     *
+     */
+    public function next()
+    {
+        ++$this->_entriesKey;
+    }
+
+    /**
+     * Reset the pointer in the feed object
+     *
+     */
+    public function rewind()
+    {
+        $this->_entriesKey = 0;
+    }
+
+    /**
+     * Return the feed as an array
+     *
+     * @return array
+     */
+    public function toArray() // untested
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Check to see if the iterator is still valid
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return 0 <= $this->_entriesKey && $this->_entriesKey < $this->count();
+    }
+
+    public function getExtensions()
+    {
+        return $this->_extensions;
+    }
+
+    public function __call($method, $args)
+    {
+        foreach ($this->_extensions as $extension) {
+            if (method_exists($extension, $method)) {
+                return call_user_func_array(array($extension, $method), $args);
+            }
+        }
+        require_once 'Zend/Feed/Exception.php';
+        throw new Zend_Feed_Exception('Method: ' . $method
+        . 'does not exist and could not be located on a registered Extension');
+    }
+
+    protected function _loadExtensions()
+    {
+        $all = Zend_Feed_Reader::getExtensions();
+        $feed = $all['feed'];
+        foreach ($feed as $extension) {
+            if (in_array($extension, $all['core'])) {
+                continue;
+            }
+            $className = Zend_Feed_Reader::getPluginLoader()->getClassName($extension);
+            $this->_extensions[$className] = new $className(
+                $this->getDomDocument(), $this->_data['type'], $this->_xpath
+            );
+        }
+    }
+
+    /**
+     * Read all entries to the internal entries array
+     *
+     */
+    abstract protected function _indexEntries();
+
+    /**
+     * Register the default namespaces for the current feed format
+     *
+     */
+    abstract protected function _registerNamespaces();
+}

+ 337 - 0
library/Zend/Feed/Reader/Feed/Atom.php

@@ -0,0 +1,337 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader_Feed_Abstract
+ */
+require_once 'Zend/Feed/Reader/Feed/Abstract.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_Atom_Feed
+ */
+require_once 'Zend/Feed/Reader/Extension/Atom/Feed.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Feed_Atom extends Zend_Feed_Reader_Feed_Abstract
+{
+
+    /**
+     * Atom feed
+     *
+     * @var Zend_Feed_Reader_Feed_Atom
+     */
+    protected $_atom = null;
+
+    /**
+     * Constructor
+     *
+     * @param  Zend_Feed_Abstract $feed
+     * @param  string $type
+     * @param  string $xpath
+     */
+    public function __construct(DomDocument $dom, $type = null)
+    {
+        parent::__construct($dom, $type);
+        $atomClass = Zend_Feed_Reader::getPluginLoader()->getClassName('Atom_Feed');
+        $this->_atom = new $atomClass($dom, $this->_data['type'], $this->_xpath);
+        $this->_atom->setXpathPrefix('/atom:feed');
+        foreach ($this->_extensions as $extension) {
+            $extension->setXpathPrefix('/atom:feed');
+        }
+    }
+
+    /**
+     * Get a single author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $people = $this->_atom->getAuthors();
+
+        $this->_data['authors'] = $people;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the copyright entry
+     *
+     * @return string|null
+     */
+    public function getCopyright()
+    {
+        if (array_key_exists('copyright', $this->_data)) {
+            return $this->_data['copyright'];
+        }
+
+        $copyright = $this->_atom->getCopyright();
+
+        if (!$copyright) {
+            $copyright = null;
+        }
+
+        $this->_data['copyright'] = $copyright;
+
+        return $this->_data['copyright'];
+    }
+
+    /**
+     * Get the feed creation date
+     *
+     * @return string|null
+     */
+    public function getDateCreated()
+    {
+        if (array_key_exists('datecreated', $this->_data)) {
+            return $this->_data['datecreated'];
+        }
+
+        $dateCreated = $this->_atom->getDateCreated();
+
+        if (!$dateCreated) {
+            $dateCreated = null;
+        }
+
+        $this->_data['datecreated'] = $dateCreated;
+
+        return $this->_data['datecreated'];
+    }
+
+    /**
+     * Get the feed modification date
+     *
+     * @return string|null
+     */
+    public function getDateModified()
+    {
+        if (array_key_exists('datemodified', $this->_data)) {
+            return $this->_data['datemodified'];
+        }
+
+        $dateModified = $this->_atom->getDateModified();
+
+        if (!$dateModified) {
+            $dateModified = null;
+        }
+
+        $this->_data['datemodified'] = $dateModified;
+
+        return $this->_data['datemodified'];
+    }
+
+    /**
+     * Get the feed description
+     *
+     * @return string|null
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = $this->_atom->getDescription();
+
+        if (!$description) {
+            $description = null;
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the feed generator entry
+     *
+     * @return string|null
+     */
+    public function getGenerator()
+    {
+        if (array_key_exists('generator', $this->_data)) {
+            return $this->_data['generator'];
+        }
+
+        $generator = $this->_atom->getGenerator();
+
+        $this->_data['generator'] = $generator;
+
+        return $this->_data['generator'];
+    }
+
+	/**
+     * Get the feed ID
+     *
+     * @return string|null
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = $this->_atom->getId();
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get the feed language
+     *
+     * @return string|null
+     */
+    public function getLanguage()
+    {
+        if (array_key_exists('language', $this->_data)) {
+            return $this->_data['language'];
+        }
+
+        $language = $this->_atom->getLanguage();
+
+        if (!$language) {
+            $language = $this->_xpath->evaluate('string(//@xml:lang[1])');
+        }
+
+        if (!$language) {
+            $language = null;
+        }
+
+        $this->_data['language'] = $language;
+
+        return $this->_data['language'];
+    }
+
+    /**
+     * Get a link to the source website
+     *
+     * @return string|null
+     */
+    public function getLink()
+    {
+        if (array_key_exists('link', $this->_data)) {
+            return $this->_data['link'];
+        }
+
+        $link = $this->_atom->getLink();
+
+        $this->_data['link'] = $link;
+
+        return $this->_data['link'];
+    }
+
+    /**
+     * Get a link to the feed's XML Url
+     *
+     * @return string|null
+     */
+    public function getFeedLink()
+    {
+        if (array_key_exists('feedlink', $this->_data)) {
+            return $this->_data['feedlink'];
+        }
+
+        $link = $this->_atom->getFeedLink();
+
+        $this->_data['feedlink'] = $link;
+
+        return $this->_data['feedlink'];
+    }
+
+    /**
+     * Get the feed title
+     *
+     * @return string|null
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = $this->_atom->getTitle();
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+	/**
+     * Read all entries to the internal entries array
+     *
+     */
+    protected function _indexEntries()
+    {
+        if ($this->getType() == Zend_Feed_Reader::TYPE_ATOM_10 ||
+            $this->getType() == Zend_Feed_Reader::TYPE_ATOM_03) {
+            $entries = array();
+            $entries = $this->_xpath->evaluate('//atom:entry');
+
+            foreach($entries as $index=>$entry) {
+                $this->_entries[$index] = $entry;
+            }
+        }
+    }
+
+    /**
+     * Register the default namespaces for the current feed format
+     *
+     */
+    protected function _registerNamespaces()
+    {
+        switch ($this->_data['type']) {
+            case Zend_Feed_Reader::TYPE_ATOM_03:
+                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_03);
+                break;
+            case Zend_Feed_Reader::TYPE_ATOM_10:
+            default:
+                $this->_xpath->registerNamespace('atom', Zend_Feed_Reader::NAMESPACE_ATOM_10);
+        }
+    }
+}

+ 115 - 0
library/Zend/Feed/Reader/Feed/Interface.php

@@ -0,0 +1,115 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Feed_Reader_Feed_Interface extends Iterator, Countable
+{
+    /**
+     * Get a single author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0);
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors();
+
+    /**
+     * Get the copyright entry
+     *
+     * @return string|null
+     */
+    public function getCopyright();
+
+    /**
+     * Get the feed creation date
+     *
+     * @return string|null
+     */
+    public function getDateCreated();
+
+    /**
+     * Get the feed modification date
+     *
+     * @return string|null
+     */
+    public function getDateModified();
+
+    /**
+     * Get the feed description
+     *
+     * @return string|null
+     */
+    public function getDescription();
+
+    /**
+     * Get the feed generator entry
+     *
+     * @return string|null
+     */
+    public function getGenerator();
+
+    /**
+     * Get the feed ID
+     *
+     * @return string|null
+     */
+    public function getId();
+
+    /**
+     * Get the feed language
+     *
+     * @return string|null
+     */
+    public function getLanguage();
+
+    /**
+     * Get a link to the HTML source
+     *
+     * @return string|null
+     */
+    public function getLink();
+
+    /**
+     * Get a link to the XML feed
+     *
+     * @return string|null
+     */
+    public function getFeedLink();
+
+    /**
+     * Get the feed title
+     *
+     * @return string|null
+     */
+    public function getTitle();
+
+}

+ 544 - 0
library/Zend/Feed/Reader/Feed/Rss.php

@@ -0,0 +1,544 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Feed_Reader_Feed_Abstract
+ */
+require_once 'Zend/Feed/Reader/Feed/Abstract.php';
+
+/**
+ * @see Zend_feed_Reader_Extension_Atom_Feed
+ */
+require_once 'Zend/Feed/Reader/Extension/Atom/Feed.php';
+
+/**
+ * @see Zend_Feed_Reader_Extension_DublinCore_Feed
+ */
+require_once 'Zend/Feed/Reader/Extension/DublinCore/Feed.php';
+
+/**
+ * @see Zend_Date
+ */
+require_once 'Zend/Date.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Feed_Reader
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Reader_Feed_Rss extends Zend_Feed_Reader_Feed_Abstract
+{
+    /**
+     * Atom feed
+     *
+     * @var Zend_Feed_Reader_Feed_Atom
+     */
+    protected $_atom = null;
+
+    /**
+     * DC feed
+     *
+     * @var Zend_Feed_Reader_Extension_DublinCore_Feed
+     */
+    protected $_dc = null;
+
+    /**
+     * Constructor
+     *
+     * @param  Zend_Feed_Abstract $feed
+     * @param  string $type
+     * @param  string $xpath
+     */
+    public function __construct(DomDocument $dom, $type = null)
+    {
+        parent::__construct($dom, $type);
+
+        $dublinCoreClass = Zend_Feed_Reader::getPluginLoader()->getClassName('DublinCore_Feed');
+        $this->_dc = new $dublinCoreClass($dom, $this->_data['type'], $this->_xpath);
+        $atomClass = Zend_Feed_Reader::getPluginLoader()->getClassName('Atom_Feed');
+        $this->_atom = new $atomClass($dom, $this->_data['type'], $this->_xpath);
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $xpathPrefix = '/rss/channel';
+        } else {
+            $xpathPrefix = '/rdf:RDF/rss:channel';
+        }
+        $this->_dc->setXpathPrefix($xpathPrefix);
+        $this->_atom->setXpathPrefix($xpathPrefix);
+        foreach ($this->_extensions as $extension) {
+            $extension->setXpathPrefix($xpathPrefix);
+        }
+    }
+
+	/**
+     * Get a single author
+     *
+     * @param  int $index
+     * @return string|null
+     */
+    public function getAuthor($index = 0)
+    {
+        $authors = $this->getAuthors();
+
+        if (isset($authors[$index])) {
+            return $authors[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get an array with feed authors
+     *
+     * @return array
+     */
+    public function getAuthors()
+    {
+        if (array_key_exists('authors', $this->_data)) {
+            return $this->_data['authors'];
+        }
+
+        $authors = array();
+
+        if (empty($authors)) {
+            $authors = $this->_dc->getAuthors();
+        }
+
+        if (empty($authors)) {
+            if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+                $list = $this->_xpath->query('//author');
+            } else {
+                $list = $this->_xpath->query('//rss:author');
+            }
+
+            foreach ($list as $authorObj) {
+                $authors[] = $authorObj->nodeValue;
+            }
+        }
+
+        if (empty($authors)) {
+            $authors = $this->_atom->getAuthors();
+        }
+
+        if (empty($authors)) {
+            $authors = null;
+        } else {
+            $authors = array_unique($authors);
+        }
+
+        $this->_data['authors'] = $authors;
+
+        return $this->_data['authors'];
+    }
+
+    /**
+     * Get the copyright entry
+     *
+     * @return string|null
+     */
+    public function getCopyright()
+    {
+        if (array_key_exists('copyright', $this->_data)) {
+            return $this->_data['copyright'];
+        }
+
+        $copyright = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $copyright = $this->_xpath->evaluate('string(/rss/channel/copyright)');
+        }
+
+        if (!$copyright && !is_null($this->_dc)) {
+            $copyright = $this->_dc->getCopyright();
+        }
+
+        if (empty($copyright)) {
+            $copyright = $this->_atom->getCopyright();
+        }
+
+        if (!$copyright) {
+            $copyright = null;
+        }
+
+        $this->_data['copyright'] = $copyright;
+
+        return $this->_data['copyright'];
+    }
+
+	/**
+     * Get the feed creation date
+     *
+     * @return string|null
+     */
+    public function getDateCreated()
+    {
+        return $this->getDateModified();
+    }
+
+    /**
+     * Get the feed modification date
+     *
+     * @return Zend_Date
+     */
+    public function getDateModified()
+    {
+        if (array_key_exists('datemodified', $this->_data)) {
+            return $this->_data['datemodified'];
+        }
+
+        $dateModified = null;
+        $date = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $dateModified = $this->_xpath->evaluate('string(/rss/channel/pubDate)');
+            if (!$dateModified) {
+                $dateModified = $this->_xpath->evaluate('string(/rss/channel/lastBuildDate)');
+            }
+            if ($dateModified) {
+                $date = new Zend_Date();
+                try {
+                    $date->set($dateModified, Zend_Date::RFC_822);
+                } catch (Zend_Date_Exception $e) {
+                    try {
+                        $date->set($dateModified, Zend_Date::RFC_2822);
+                    } catch (Zend_Date_Exception $e) {
+                        require_once 'Zend/Feed/Exception.php';
+                        throw new Zend_Feed_Exception(
+                        'Could not load date due to unrecognised format (should follow RFC 822 or 2822): '
+                        .$e->getMessage()
+                        );
+                    }
+                }
+            }
+        }
+
+        if (!$date) {
+            $date = $this->_dc->getDate();
+        }
+
+        if (!$date) {
+            $date = $this->_atom->getDateModified();
+        }
+
+        if (!$date) {
+            $date = null;
+        }
+
+        $this->_data['datemodified'] = $date;
+
+        return $this->_data['datemodified'];
+    }
+
+    /**
+     * Get the feed description
+     *
+     * @return string|null
+     */
+    public function getDescription()
+    {
+        if (array_key_exists('description', $this->_data)) {
+            return $this->_data['description'];
+        }
+
+        $description = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $description = $this->_xpath->evaluate('string(/rss/channel/description)');
+        } else {
+            $description = $this->_xpath->evaluate('string(/rdf:RDF/rss:channel/rss:description)');
+        }
+
+        if (!$description && !is_null($this->_dc)) {
+            $description = $this->_dc->getDescription();
+        }
+
+        if (empty($description)) {
+            $description = $this->_atom->getDescription();
+        }
+
+        if (!$description) {
+            $description = null;
+        }
+
+        $this->_data['description'] = $description;
+
+        return $this->_data['description'];
+    }
+
+    /**
+     * Get the feed ID
+     *
+     * @return string|null
+     */
+    public function getId()
+    {
+        if (array_key_exists('id', $this->_data)) {
+            return $this->_data['id'];
+        }
+
+        $id = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $id = $this->_xpath->evaluate('string(/rss/channel/guid)');
+        }
+
+        if (!$id && !is_null($this->_dc)) {
+            $id = $this->_dc->getId();
+        }
+
+        if (empty($id)) {
+            $id = $this->_atom->getId();
+        }
+
+        if (!$id) {
+            if ($this->getLink()) {
+                $id = $this->getLink();
+            } elseif ($this->getTitle()) {
+                $id = $this->getTitle();
+            } else {
+                $id = null;
+            }
+        }
+
+        $this->_data['id'] = $id;
+
+        return $this->_data['id'];
+    }
+
+    /**
+     * Get the feed language
+     *
+     * @return string|null
+     */
+    public function getLanguage()
+    {
+        if (array_key_exists('language', $this->_data)) {
+            return $this->_data['language'];
+        }
+
+        $language = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $language = $this->_xpath->evaluate('string(/rss/channel/language)');
+        }
+
+        if (!$language && !is_null($this->_dc)) {
+            $language = $this->_dc->getLanguage();
+        }
+
+        if (empty($language)) {
+            $language = $this->_atom->getLanguage();
+        }
+
+        if (!$language) {
+            $language = $this->_xpath->evaluate('string(//@xml:lang[1])');
+        }
+
+        if (!$language) {
+            $language = null;
+        }
+
+        $this->_data['language'] = $language;
+
+        return $this->_data['language'];
+    }
+
+    /**
+     * Get a link to the feed
+     *
+     * @return string|null
+     */
+    public function getLink()
+    {
+        if (array_key_exists('link', $this->_data)) {
+            return $this->_data['link'];
+        }
+
+        $link = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $link = $this->_xpath->evaluate('string(/rss/channel/link)');
+        } else {
+            $link = $this->_xpath->evaluate('string(/rdf:RDF/rss:channel/rss:link)');
+        }
+
+        if (empty($link)) {
+            $link = $this->_atom->getLink();
+        }
+
+        if (!$link) {
+            $link = null;
+        }
+
+        $this->_data['link'] = $link;
+
+        return $this->_data['link'];
+    }
+
+    /**
+     * Get a link to the feed XML
+     *
+     * @return string|null
+     */
+    public function getFeedLink()
+    {
+        if (array_key_exists('feedlink', $this->_data)) {
+            return $this->_data['feedlink'];
+        }
+
+        $link = null;
+
+        $link = $this->_atom->getFeedLink();
+
+        if (!$link) {
+            $link = null;
+        }
+
+        $this->_data['feedlink'] = $link;
+
+        return $this->_data['feedlink'];
+    }
+
+    /**
+     * Get the feed generator entry
+     *
+     * @return string|null
+     */
+    public function getGenerator()
+    {
+        if (array_key_exists('generator', $this->_data)) {
+            return $this->_data['generator'];
+        }
+
+        $generator = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $generator = $this->_xpath->evaluate('string(/rss/channel/generator)');
+        }
+
+        if (!$generator) {
+            if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+                $generator = $this->_xpath->evaluate('string(/rss/channel/atom:generator)');
+            } else {
+                $generator = $this->_xpath->evaluate('string(/rdf:RDF/rss:channel/atom:generator)');
+            }
+            if ($generator) {
+                $generator = html_entity_decode($generator, ENT_QUOTES, $this->getEncoding());
+            }
+        }
+
+        if (empty($generator)) {
+            $generator = $this->_atom->getGenerator();
+        }
+
+        if (!$generator) {
+            $generator = null;
+        }
+
+        $this->_data['generator'] = $generator;
+
+        return $this->_data['generator'];
+    }
+
+    /**
+     * Get the feed title
+     *
+     * @return string|null
+     */
+    public function getTitle()
+    {
+        if (array_key_exists('title', $this->_data)) {
+            return $this->_data['title'];
+        }
+
+        $title = null;
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 &&
+            $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $title = $this->_xpath->evaluate('string(/rss/channel/title)');
+        } else {
+            $title = $this->_xpath->evaluate('string(/rdf:RDF/rss:channel/rss:title)');
+        }
+
+        if (!$title && !is_null($this->_dc)) {
+            $title = $this->_dc->getTitle();
+        }
+
+        if (!$title) {
+            $title = $this->_atom->getTitle();
+        }
+
+        if (!$title) {
+            $title = null;
+        }
+
+        $this->_data['title'] = $title;
+
+        return $this->_data['title'];
+    }
+
+	/**
+     * Read all entries to the internal entries array
+     *
+     */
+    protected function _indexEntries()
+    {
+        $entries = array();
+
+        if ($this->getType() !== Zend_Feed_Reader::TYPE_RSS_10 && $this->getType() !== Zend_Feed_Reader::TYPE_RSS_090) {
+            $entries = $this->_xpath->evaluate('//item');
+        } else {
+            $entries = $this->_xpath->evaluate('//rss:item');
+        }
+
+        foreach($entries as $index=>$entry) {
+            $this->_entries[$index] = $entry;
+        }
+    }
+
+    /**
+     * Register the default namespaces for the current feed format
+     *
+     */
+    protected function _registerNamespaces()
+    {
+        switch ($this->_data['type']) {
+            case Zend_Feed_Reader::TYPE_RSS_10:
+                $this->_xpath->registerNamespace('rdf', Zend_Feed_Reader::NAMESPACE_RDF);
+                $this->_xpath->registerNamespace('rss', Zend_Feed_Reader::NAMESPACE_RSS_10);
+                break;
+
+            case Zend_Feed_Reader::TYPE_RSS_090:
+                $this->_xpath->registerNamespace('rdf', Zend_Feed_Reader::NAMESPACE_RDF);
+                $this->_xpath->registerNamespace('rss', Zend_Feed_Reader::NAMESPACE_RSS_090);
+                break;
+        }
+    }
+}

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

@@ -46,6 +46,16 @@ require_once 'Zend/Feed/ImportTest.php';
 require_once 'Zend/Feed/IteratorTest.php';
 require_once 'Zend/Feed/Entry/RssTest.php';
 
+require_once 'Zend/Feed/ReaderTest.php';
+require_once 'Zend/Feed/Reader/Feed/RssTest.php';
+require_once 'Zend/Feed/Reader/Entry/RssTest.php';
+require_once 'Zend/Feed/Reader/Feed/AtomTest.php';
+require_once 'Zend/Feed/Reader/Entry/AtomTest.php';
+
+require_once 'Zend/Feed/Reader/Integration/WordpressRss2DcAtomTest.php';
+require_once 'Zend/Feed/Reader/Integration/WordpressAtom10Test.php';
+require_once 'Zend/Feed/Reader/Integration/LautDeRdfTest.php';
+require_once 'Zend/Feed/Reader/Integration/H-OnlineComAtom10Test.php';
 
 /**
  * @category   Zend
@@ -74,6 +84,25 @@ class Zend_Feed_AllTests
         $suite->addTestSuite('Zend_Feed_IteratorTest');
         $suite->addTestSuite('Zend_Feed_Entry_RssTest');
 
+        /* Zend_Feed_Reader tests */
+        // Base parent class
+        $suite->addTestSuite('Zend_Feed_ReaderTest');
+        // RSS - Feed Level
+        $suite->addTestSuite('Zend_Feed_Reader_Feed_RssTest');
+        // RSS - Item Level
+        $suite->addTestSuite('Zend_Feed_Reader_Entry_RssTest');
+        // ATOM - Feed Level
+        $suite->addTestSuite('Zend_Feed_Reader_Feed_AtomTest');
+        // ATOM - Item Level
+        $suite->addTestSuite('Zend_Feed_Reader_Entry_AtomTest');
+        /**
+         * Real World Feed Tests
+         */
+        $suite->addTestSuite('Zend_Feed_Reader_Integration_WordpressRss2DcAtomTest');
+        $suite->addTestSuite('Zend_Feed_Reader_Integration_WordpressAtom10Test');
+        $suite->addTestSuite('Zend_Feed_Reader_Integration_LautDeRdfTest');
+        $suite->addTestSuite('Zend_Feed_Reader_Integration_HOnlineComAtom10Test');
+
         return $suite;
     }
 }

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

@@ -0,0 +1,226 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Feed/Reader.php';
+
+class Zend_Feed_Reader_Entry_AtomTest extends PHPUnit_Framework_TestCase
+{
+
+    protected $_feedSamplePath = null;
+
+    public function setup()
+    {
+        $this->_feedSamplePath = dirname(__FILE__) . '/_files/Atom';
+    }
+
+    /**
+     * Get Id (Unencoded Text)
+     */
+    public function testGetsIdFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/id/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('1', $entry->getId());
+    }
+
+    public function testGetsIdFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/id/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('1', $entry->getId());
+    }
+
+    /**
+     * Get creation date (Unencoded Text)
+     */
+    public function testGetsDateCreatedFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/datecreated/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000',
+        $entry->getDateCreated()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateCreatedFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/datecreated/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000',
+        $entry->getDateCreated()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    /**
+     * Get modification date (Unencoded Text)
+     */
+    public function testGetsDateModifiedFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/datemodified/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000',
+        $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/datemodified/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000',
+        $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    /**
+     * Get Title (Unencoded Text)
+     */
+    public function testGetsTitleFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/title/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/title/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    /**
+     * Get Authors (Unencoded Text)
+     */
+    public function testGetsAuthorsFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/author/plain/atom03.xml')
+        );
+
+        $authors = array(
+            0 => 'joe@example.com (Joe Bloggs)',
+            1 => 'Joe Bloggs',
+            3 => 'joe@example.com',
+            4 => 'http://www.example.com',
+            6 => 'jane@example.com (Jane Bloggs)'
+        );
+
+        $entry = $feed->current();
+        $this->assertEquals($authors, $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/author/plain/atom10.xml')
+        );
+
+        $authors = array(
+            0 => 'joe@example.com (Joe Bloggs)',
+            1 => 'Joe Bloggs',
+            3 => 'joe@example.com',
+            4 => 'http://www.example.com',
+            6 => 'jane@example.com (Jane Bloggs)'
+        );
+
+        $entry = $feed->current();
+        $this->assertEquals($authors, $entry->getAuthors());
+    }
+
+    /**
+     * Get Author (Unencoded Text)
+     */
+    public function testGetsAuthorFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/author/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('joe@example.com (Joe Bloggs)', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/author/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('joe@example.com (Joe Bloggs)', $entry->getAuthor());
+    }
+
+    /**
+     * Get Description (Unencoded Text)
+     */
+    public function testGetsDescriptionFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/description/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/description/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    /**
+     * Get Content (Unencoded Text)
+     */
+    public function testGetsContentFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/content/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/content/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    /**
+     * Get Link (Unencoded Text)
+     */
+    public function testGetsLinkFromAtom03()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/link/plain/atom03.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromAtom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath . '/link/plain/atom10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+}

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

@@ -0,0 +1,2563 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Feed/Reader.php';
+
+class Zend_Feed_Reader_Entry_RssTest extends PHPUnit_Framework_TestCase
+{
+
+    protected $_feedSamplePath = null;
+
+    public function setup()
+    {
+        $this->_feedSamplePath = dirname(__FILE__) . '/_files/Rss';
+    }
+
+    /**
+     * Get Id (Unencoded Text)
+     */
+    public function testGetsIdFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    // DC 1.0
+
+    public function testGetsIdFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    // DC 1.1
+
+    public function testGetsIdFromRss20_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss094_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss093_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss092_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss091_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss10_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    public function testGetsIdFromRss090_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/dc11/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/1', $entry->getId());
+    }
+
+    // Missing Id (but alternates to Title)
+
+    public function testGetsIdFromRss20_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    public function testGetsIdFromRss094_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    public function testGetsIdFromRss093_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    public function testGetsIdFromRss092_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    public function testGetsIdFromRss091_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    public function testGetsIdFromRss10_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    public function testGetsIdFromRss090_Title()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/title/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getId());
+    }
+
+    // Missing Any Id
+
+    public function testGetsIdFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    public function testGetsIdFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/id/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getId());
+    }
+
+    /**
+     * Get Title (Unencoded Text)
+     */
+    public function testGetsTitleFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    // DC 1.0
+
+    public function testGetsTitleFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    // DC 1.1
+
+    public function testGetsTitleFromRss20_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss094_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss093_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss092_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss091_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss10_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss090_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/dc11/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Title', $entry->getTitle());
+    }
+
+    // Missing Title
+
+    public function testGetsTitleFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    public function testGetsTitleFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/title/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getTitle());
+    }
+
+    /**
+     * Get Authors (Unencoded Text)
+     */
+    public function testGetsAuthorsFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    // DC 1.0
+
+    public function testGetsAuthorsFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    // DC 1.1
+
+    public function testGetsAuthorsFromRss20_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss094_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss093_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss092_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss091_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss10_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss090_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array('Joe Bloggs','Jane Bloggs'), $entry->getAuthors());
+    }
+
+    // Missing Author
+
+    public function testGetsAuthorsFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+    public function testGetsAuthorsFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(array(), $entry->getAuthors());
+    }
+
+
+    /**
+     * Get Author (Unencoded Text)
+     */
+    public function testGetsAuthorFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    // DC 1.0
+
+    public function testGetsAuthorFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    public function testGetsAuthorFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    public function testGetsAuthorFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    public function testGetsAuthorFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    // DC 1.1
+
+    public function testGetsAuthorFromRss20_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    public function testGetsAuthorFromRss094_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss093_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    public function testGetsAuthorFromRss092_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss091_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    public function testGetsAuthorFromRss10_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Joe Bloggs', $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss090_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/dc11/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Jane Bloggs', $entry->getAuthor(1));
+    }
+
+    // Missing Id
+
+    public function testGetsAuthorFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    public function testGetsAuthorFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/author/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getAuthor());
+    }
+
+    /**
+     * Get Description (Unencoded Text)
+     */
+    public function testGetsDescriptionFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    // DC 1.0
+
+    public function testGetsDescriptionFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    // DC 1.1
+
+    public function testGetsDescriptionFromRss20_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss094_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss093_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss092_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss091_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss10_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss090_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/dc11/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getDescription());
+    }
+
+    // Missing Description
+
+    public function testGetsDescriptionFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    public function testGetsDescriptionFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/description/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDescription());
+    }
+
+    /**
+     * Get Content (Unencoded Text)
+     */
+    public function testGetsContentFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Content', $entry->getContent());
+    }
+
+    // Revert to Description if no Content
+
+    public function testGetsContentFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    public function testGetsContentFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/description/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Entry Description', $entry->getContent());
+    }
+
+    // Missing Content and Description
+
+    public function testGetsContentFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    public function testGetsContentFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    public function testGetsContentFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    public function testGetsContentFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    public function testGetsContentFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    public function testGetsContentFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    public function testGetsContentFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/content/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getContent());
+    }
+
+    /**
+     * Get Link (Unencoded Text)
+     */
+    public function testGetsLinkFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss090()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry', $entry->getLink());
+    }
+
+    // Missing Link
+
+    public function testGetsLinkFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    public function testGetsLinkFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/link/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getLink());
+    }
+
+    /**
+     * Get DateModified (Unencoded Text)
+     */
+    public function testGetsDateModifiedFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    // DC 1.0
+
+    public function testGetsDateModifiedFromRss20_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss094_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss093_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss092_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss091_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss10_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss090_Dc10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    // DC 1.1
+
+    public function testGetsDateModifiedFromRss20_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss094_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss093_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss092_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss091_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss10_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    public function testGetsDateModifiedFromRss090_Dc11()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/dc11/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('Saturday 07 March 2009 08 03 50 +0000', $entry->getDateModified()->toString('EEEE dd MMMM YYYY HH mm ss ZZZ'));
+    }
+
+    // Missing DateModified
+
+    public function testGetsDateModifiedFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    public function testGetsDateModifiedFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    public function testGetsDateModifiedFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    public function testGetsDateModifiedFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    public function testGetsDateModifiedFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    public function testGetsDateModifiedFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    public function testGetsDateModifiedFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/datemodified/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getDateModified());
+    }
+
+    /**
+     * Get CommentCount (Unencoded Text)
+     */
+
+    // Slash 1.0
+
+    public function testGetsCommentCountFromRss20_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss094_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss093_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss092_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss091_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss10_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss090_Slash10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/slash10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    // Atom Threaded 1.0
+
+    public function testGetsCommentCountFromRss20_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss094_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss093_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss092_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss091_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss10_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss090_Thread10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/thread10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    // Atom 1.0 (Threaded 1.0 atom:link attribute)
+
+    public function testGetsCommentCountFromRss20_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss094_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss093_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss092_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss091_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss10_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss090_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/atom10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('321', $entry->getCommentCount());
+    }
+
+    // Missing Any CommentCount
+
+    public function testGetsCommentCountFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    public function testGetsCommentCountFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentcount/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentCount());
+    }
+
+    /**
+     * Get CommentLink (Unencoded Text)
+     */
+
+    public function testGetsCommentLinkFromRss20()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss094()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss093()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss092()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss091()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    // Atom 1.0
+
+    public function testGetsCommentLinkFromRss20_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss094_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss093_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss092_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss091_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss10_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss090_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/atom10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/comments', $entry->getCommentLink());
+    }
+
+    // Missing Any CommentLink
+
+    public function testGetsCommentLinkFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    public function testGetsCommentLinkFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentlink/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentLink());
+    }
+
+    /**
+     * Get CommentFeedLink (Unencoded Text)
+     */
+
+ // Atom 1.0
+
+    public function testGetsCommentFeedLinkFromRss20_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss094_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss093_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss092_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss091_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss10_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss090_WellFormedWeb10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/wellformedweb/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    // Atom 1.0
+
+    public function testGetsCommentFeedLinkFromRss20_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss094_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss093_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss092_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss091_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss10_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss090_Atom10()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/atom10/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals('http://www.example.com/entry/321/feed/rss/', $entry->getCommentFeedLink());
+    }
+
+    // Missing Any CommentFeedLink
+
+    public function testGetsCommentFeedLinkFromRss20_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss20.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss094_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss094.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss093_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss093.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss092_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss092.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss091_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss091.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss10_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss10.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+    public function testGetsCommentFeedLinkFromRss090_None()
+    {
+        $feed = Zend_Feed_Reader::importString(
+            file_get_contents($this->_feedSamplePath.'/commentfeedlink/plain/none/rss090.xml')
+        );
+        $entry = $feed->current();
+        $this->assertEquals(null, $entry->getCommentFeedLink());
+    }
+
+}

+ 45 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/author/plain/atom03.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <author>
+	        <name>Joe Bloggs</name>
+	        <uri>http://www.example.com</uri>
+	        <email>joe@example.com</email>
+	    </author>
+        <author>
+            <name>Joe Bloggs</name>
+            <uri>http://www.example.com</uri>
+            <email></email>
+        </author>
+        <author>
+            <name>Joe Bloggs</name>
+            <uri></uri>
+            <email></email>
+        </author>
+        <author>
+            <name></name>
+            <uri>http://www.example.com</uri>
+            <email>joe@example.com</email>
+        </author>
+        <author>
+            <name></name>
+            <uri>http://www.example.com</uri>
+            <email></email>
+        </author>
+        <author>
+            <name></name>
+            <uri></uri>
+            <email>joe@example.com</email>
+        </author>
+        <author>
+            <name></name>
+            <uri></uri>
+            <email></email>
+        </author>
+	    <contributor>
+	        <name>Jane Bloggs</name>
+	        <uri>http://www.example.com</uri>
+	        <email>jane@example.com</email>
+	    </contributor>
+    </entry>
+</feed>

+ 45 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/author/plain/atom10.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+        <author>
+	        <name>Joe Bloggs</name>
+	        <uri>http://www.example.com</uri>
+	        <email>joe@example.com</email>
+	    </author>
+        <author>
+            <name>Joe Bloggs</name>
+            <uri>http://www.example.com</uri>
+            <email></email>
+        </author>
+        <author>
+            <name>Joe Bloggs</name>
+            <uri></uri>
+            <email></email>
+        </author>
+        <author>
+            <name></name>
+            <uri>http://www.example.com</uri>
+            <email>joe@example.com</email>
+        </author>
+        <author>
+            <name></name>
+            <uri>http://www.example.com</uri>
+            <email></email>
+        </author>
+        <author>
+            <name></name>
+            <uri></uri>
+            <email>joe@example.com</email>
+        </author>
+        <author>
+            <name></name>
+            <uri></uri>
+            <email></email>
+        </author>
+	    <contributor>
+	        <name>Jane Bloggs</name>
+	        <uri>http://www.example.com</uri>
+	        <email>jane@example.com</email>
+	    </contributor>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/content/plain/atom03.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">Entry Content</content>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/content/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+		<content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">Entry Content</content>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/datecreated/plain/atom03.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <created>2009-03-07T08:03:50Z</created>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/datecreated/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+		<published>2009-03-07T08:03:50Z</published>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/datemodified/plain/atom03.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <modified>2009-03-07T08:03:50Z</modified>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/datemodified/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+		<updated>2009-03-07T08:03:50Z</updated>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/description/plain/atom03.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <summary>Entry Description</summary>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/description/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+        <summary>Entry Description</summary>
+    </entry>
+</feed>

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

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <id>1</id>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/id/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+        <id>1</id>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/link/plain/atom03.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <link rel="alternate" type="text/html" href="http://www.example.com/entry"/>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/link/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+        <link href="http://www.example.com/entry"/>
+    </entry>
+</feed>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/links/plain/atom03.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <link rel="alternate" type="text/html" href="http://www.example.com/entry"/>
+        <link rel="alternate" type="text/html" href="http://www.example.com/entry2"/>
+    </entry>
+</feed>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/links/plain/atom10.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+        <link rel="alternate" type="text/html" href="http://www.example.com/entry"/>
+        <link rel="alternate" type="text/html" href="http://www.example.com/entry2"/>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/title/plain/atom03.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+    <entry>
+        <title>Entry Title</title>
+    </entry>
+</feed>

+ 6 - 0
tests/Zend/Feed/Reader/Entry/_files/Atom/title/plain/atom10.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+    <entry>
+        <title>Entry Title</title>
+    </entry>
+</feed>

+ 12 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss090.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rdf:RDF>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss091.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss092.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss093.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss094.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 12 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss10.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rdf:RDF>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc10/rss20.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0"
+    xmlns:dc="http://purl.org/dc/elements/1.0/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 12 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss090.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rdf:RDF>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss091.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss092.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss093.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss094.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 12 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss10.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rdf:RDF>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/dc11/rss20.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0"
+    xmlns:dc="http://purl.org/dc/elements/1.1/">
+    <channel>
+        <item>
+            <dc:creator>Joe Bloggs</dc:creator>
+            <dc:creator>Jane Bloggs</dc:creator>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss090.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rdf:RDF>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss091.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss092.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss093.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss094.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss10.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rdf:RDF>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/none/rss20.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss090.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rdf:RDF>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss091.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss092.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss093.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss094.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss10.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rdf:RDF>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/author/plain/rss20.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0">
+    <channel>
+        <item>
+            <author>joe@example.com (Joe Bloggs)</author>
+            <author>jane@example.com (Jane Bloggs)</author>
+        </item>
+    </channel>
+</rss>

+ 12 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss090.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rdf:RDF>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss091.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss092.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss093.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rss>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss094.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rss>

+ 12 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss10.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rdf:RDF>

+ 10 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/atom10/rss20.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0"
+    xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <atom:link type="text/html" rel="replies" href="http://www.example.com/comments" thread:count="321" />
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss090.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rdf:RDF>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss091.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss092.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss093.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss094.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss10.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rdf:RDF>

+ 7 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/none/rss20.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0">
+    <channel>
+        <item>
+        </item>
+    </channel>
+</rss>

+ 11 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss090.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rdf:RDF>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss091.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss092.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss093.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss094.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rss>

+ 11 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss10.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rdf:RDF>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/slash10/rss20.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0"
+    xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
+    <channel>
+        <item>
+            <slash:comments>321</slash:comments>
+        </item>
+    </channel>
+</rss>

+ 11 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss090.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://my.netscape.com/rdf/simple/0.9/"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <thread:total>321</thread:total>
+        </item>
+    </channel>
+</rdf:RDF>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss091.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.91"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <thread:total>321</thread:total>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss092.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.92"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <thread:total>321</thread:total>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss093.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.93"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <thread:total>321</thread:total>
+        </item>
+    </channel>
+</rss>

+ 9 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss094.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="0.94"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <thread:total>321</thread:total>
+        </item>
+    </channel>
+</rss>

+ 11 - 0
tests/Zend/Feed/Reader/Entry/_files/Rss/commentcount/plain/thread10/rss10.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns="http://purl.org/rss/1.0/"
+    xmlns:thread="http://purl.org/syndication/thread/1.0">
+    <channel>
+        <item>
+            <thread:total>321</thread:total>
+        </item>
+    </channel>
+</rdf:RDF>

Some files were not shown because too many files changed in this diff