<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 17618 -->
<!-- Reviewed: no -->
<sect1 id="zend.feed.reader">
    <title>Zend_Feed_Reader</title>

    <sect2 id="zend.feed.reader.introduction">
        <title>Einführung</title>

        <para>
            <classname>Zend_Feed_Reader</classname> ist eine Komponente die verwendet wird um
            <acronym>RSS</acronym> und Atom Feeds jeder Version zu konsumieren, inklusive
            <acronym>RDF</acronym>/<acronym>RSS</acronym> 1.0, <acronym>RSS</acronym> 2.0 und
            Atom 0.3/1.0. Die <acronym>API</acronym> für das Empfangen von Feed Daten ist relativ
            einfach da <classname>Zend_Feed_Reader</classname> in der Lage ist jeden Feed eines
            jeden Typs mit Hilfe der <acronym>API</acronym> nach den angefragten Informationen zu
            durchsuchen. Wenn die typischen Elemente die diese Informationen enthalten nicht
            vorhanden sind, werden diese adaptiert und statt dessen auf eine Vielzahl von
            alternativen Elementen zurück gegriffen. Diese Fähigkeit, von Alternativen auszuwählen,
            verhindert das Benutzer Ihren eigenen astrakten Layer über die Komponente legen müssen
            damit Sie nützlich ist, oder beliebig tiefes Wissen des zugrundeliegenden Standard,
            aktueller alternativen und namespaces Erweiterungen haben müssen.
        </para>

        <para>
            Intern arbeitet <classname>Zend_Feed_Reader</classname> fast komplett auf Basis der
            Erstellung von XPath Abfragen gegen das Dokument Objekt Modell des Feed
            <acronym>XML</acronym>'s. Das <acronym>DOM</acronym> wird nicht durch eine gekettete
            Eigenschaften <acronym>API</acronym> wie bei <classname>Zend_Feed</classname> bekannt
            gegeben und durch die darunterliegenden <classname>DOMDocument</classname>,
            <classname>DOMElement</classname> und <classname>DOMXPath</classname> Objekte für eine
            externe Manipulation bekannt gegeben. Dieser Singular Weg des Parsens ist Konsistent
            und die Komponente bietet ein Plugin System um dem Feed hinzuzufügen und eine Eintrags
            Level <acronym>API</acronym> durch das Schreiben von Erweiterungen auf einer ähnlichen
            Basis.
        </para>

        <para>
            Geschwindigkeit wird auf drei Wegen bereitgestellt. Erstens unterstützt
            <classname>Zend_Feed_Reader</classname> das Cachen durch Verwendung von
            <classname>Zend_Cache</classname> um eine Kopie des Originalen Feed
            <acronym>XML</acronym> zu halten. Das erlaubt es Netzwerk Anfragen für eine Feed
            <acronym>URI</acronym> zu überspringen wenn der Cache gültig ist. Zweitens wird die
            Feed und Eintrag- Level <acronym>API</acronym> durch einen internen Cache gesichert
            (nicht persistent) damit wiederholte <acronym>API</acronym> Aufrufe für den gleichen
            Feed eine zusätzliche Verwendung von <acronym>DOM</acronym>/XPath verhindert.
            Drittens erlaubt das Importieren von Feeds von einer <acronym>URI</acronym> den
            Vorteil von konditionellen <acronym>HTTP</acronym> GET Anfragen was es Servern
            erlaubt eine leere 304 Anfrage auszulösen wenn der angefragte Fed seit der Zeit zu der
            er das letzte Mal angefragt wurde, nicht verändert wurde. Im letzten Fall hält eine
            Instanz von <classname>Zend_Cache</classname> den zuletzt empfangenen Feed zusammen mit
            dem ETag und dem Last-Modified Header Werten die in der <acronym>HTTP</acronym>
            Antwort gesendet wurde.
        </para>

        <para>
            Relativ zu <classname>Zend_Feed</classname> wurde
            <classname>Zend_Feed_Reader</classname> als frei stehender Ersatz für
            <classname>Zend_Feed</classname> formuliert der aber nicht mit
            <classname>Zend_Feed</classname> rückwärts kompatibel ist. Aber es ist eine Alternative
            die einer anderen Ideologie folgt die darin fokusiert ist einfach verwendbar zu sein,
            flexibel, konsistent und durch das Plugin System erweiterbar.
            <classname>Zend_Feed_Reader</classname> ist auch nicht dazu fähig Feeds zu erstellen,
            das wird aber zu einem späteren Zeitpunkt hinzugefügt.
        </para>
    </sect2>

    <sect2 id="zend.feed.reader.import">
        <title>Feeds importieren</title>

        <para>
            Das importieren eines Feeds mit <classname>Zend_Feed_Reader</classname> ist zu
            <classname>Zend_Feed</classname> nicht sehr unterschiedlich. Feeds können von einem
            String, einer Datei, <acronym>URI</acronym> oder einer Instanz des Typs
            <classname>Zend_Feed_Abstract</classname> importiert werden. Das importieren von einer
            <acronym>URI</acronym> kann zusätzlich eine konditionelle <acronym>HTTP</acronym>
            GET Anfrage benützen. Wenn das importieren fehlschlägt, wird eine Exception geworfen.
            Das Endergebnis wird ein Objekt des Typs
            <classname>Zend_Feed_Reader_FeedInterface</classname> sein, die Core Implementation
            von <classname>Zend_Feed_Reader_Feed_Rss</classname> und
            <classname>Zend_Feed_Reader_Feed_Atom</classname> (<classname>Zend_Feed</classname>
            hat alle kurzen Namen genommen!). Beide Objekte unterstützen mehrere (alle
            existierenden) Versionen dieser breiten Feed Typen.
        </para>

        <para>
            Im folgenden Beispiel importieren wir einen
            <acronym>RDF</acronym>/<acronym>RSS</acronym> 1.0 Feed und extrahieren einige
            grundsätzliche Information die dann in einer Datenbank oder wo anders gespeichert
            werden können.
        </para>

        <programlisting language="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>
            Das obige Beispiel demonstriert die <acronym>API</acronym> von
            <classname>Zend_Feed_Reader</classname> und es demonstriert auch einige seiner
            internen Operationen. In Wirklichkeit hat der ausgewählte <acronym>RDF</acronym> Feed
            keine nativen Daten oder Author Elemente, trotzdem verwendet er das Dublin Core 1.1
            Modul welches Namespaced Ersteller und Datums Elemente anbietet.
            <classname>Zend_Feed_Reader</classname> fällt auf diese und ähnliche Operationen zurück
            wenn keine relativ nativen Elemente existieren. Wenn es absolut keine alternative
            finden kann wird es <constant>NULL</constant> zurückgeben, was anzeigt das die
            Informationen nicht im Feed gefunden werden können. Man sollte beachten das Klassen die
            <classname>Zend_Feed_Reader_FeedInterface</classname> implementieren auch
            die <acronym>SPL</acronym> Interfaces <classname>Iterator</classname> und
            <classname>Countable</classname> implementieren.
        </para>

        <para>
            Feeds können auch von Strings, Dateien und sogar Objekten des Typs
            <classname>Zend_Feed_Abstract</classname> importiert werden.
        </para>

        <programlisting language="php"><![CDATA[
// von einer URI
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');

// von einem String
$feed = Zend_Feed_Reader::importString($feedXmlString);

// von einer Datei
$feed = Zend_Feed_Reader::importFile('./feed.xml');

// von einem abstrakten Zend_Feed_Abstract Objekt
$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>Empfangen darunterliegender Quellen von Feeds und Einträgen</title>

        <para>
            <classname>Zend_Feed_Reader</classname> macht sein bestes um Ihnen die Details
            abzunehmen. Wenn man an einem Feed ausserhalb von
            <classname>Zend_Feed_Reader</classname> arbeiten muß, kann man das grundsätzliche
            <classname>DOMDocument</classname> oder <classname>DOMElement</classname> von jeder
            Klasse extrahieren, oder sogar einen <acronym>XML</acronym> String der sie enthält.
            Es werden auch Methoden angeboten um das aktuelle <classname>DOMXPath</classname>
            Objekt (mit allen registrierten Kern und Erweiterungs Namespaces) zu extrahieren, und
            den richtigen Präfix der in allen XPath Anfragen für den aktuellen Feed oder Eintrag
            verwendet wird. Die grundsätzlich zu verwenden Methoden (für jedes Objekt) sind
            <methodname>saveXml()</methodname>, <methodname>getDomDocument()</methodname>,
            <methodname>getElement()</methodname>, <methodname>getXpath()</methodname> und
            <methodname>getXpathPrefix()</methodname>. Diese erlauben es sich von
            <classname>Zend_Feed_Reader</classname> zu lösen und das zu tun was man selbst
            machen will.
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <methodname>saveXml()</methodname> gibt einen <acronym>XML</acronym> String
                    zurück der nur das Element enthält welches das aktuelle Objekt repräsentiert.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>getDomDocument()</methodname> gibt das
                    <classname>DOMDocument</classname> Objekt zurück das den kompletten Feed
                    repräsentiert (sogar wenn es von einem Entry Objekt aus aufgerufen wird).
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>getElement()</methodname> gibt das <classname>DOMElement</classname>
                    des aktuellen Objekts zurück (z.B. den Feed oder aktuellen Eintrag).
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>getXpath()</methodname> gibt das aktuelle
                    <classname>DOMXPath</classname> Objekt für den aktuellen Feed zurück (sogar wenn
                    es von einem Entry Objekt aus aufgerufen wird) mit den Namespaces des aktuellen
                    Feed Typs und allen vor-registrierten geladenen Erweiterungen.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>getXpathPrefix()</methodname> gibt den Präfix der Abfrage für das
                    aktuelle Objekt zurück (z.B. den Feed oder den aktuellen Eintrag) welcher den
                    richtigen XPath Query Pfad für den spezifizierten Feed oder Eintrag enthält.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Hier ist ein Beispiel bei dem ein Feed eine <acronym>RSS</acronym> Erweiterung enthalten
            können die von <classname>Zend_Feed_Reader</classname> nicht out of the Box unterstützt
            wird. Beachtenswert ist, das man eine Erweiterungen schreiben und registrieren könnte
            (wird später behandelt) um das zu bewerkstelligen, aber das ist nicht immer eine
            Garantie für einen schnellen Check. Man muß jeden neuen Namespace beim
            <classname>DOMXPath</classname> Objekt registrieren bevor es verwendet wird ausser Sie
            werden vorab von <classname>Zend_Feed_Reader</classname> oder einer Erweiterung
            registriert.
        </para>

        <programlisting language="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>
                Wenn man einen bereits registrierten Namespace mit einem anderen Präfix Namen
                registriert als jenen der von <classname>Zend_Feed_Reader</classname> intern
                verwendet wird, zerstört das die Interne Arbeitsweise dieser Komponente.
            </para>
        </warning>
    </sect2>

    <sect2 id="zend.feed.reader.cache-request">
        <title>Unterstützung für Caches und intelligente Anfragen</title>

        <sect3 id="zend.feed.reader.cache-request.cache">
            <title>Unterstützung für Caches in Zend_Feed_Reader hinzufügen</title>

            <para>
                <classname>Zend_Feed_Reader</classname> unterstützt die Verwendung einer Instanz von
                <classname>Zend_Cache</classname> um Feeds zu cachen (als <acronym>XML</acronym>)
                um unnötige Anfragen im Netzwerk zu vermeiden. Das Hinzufügen eines Caches ist hier
                so einfach wie bei anderen Zend Framework Komponenten. Den Cache erstellen und
                konfigurieren und dann <classname>Zend_Feed_Reader</classname> mitteilen das er
                verwendet werden soll! Der verwendete Cache Schlüssel ist
                "<classname>Zend_Feed_Reader_</classname>" gefolgt von dem
                <acronym>MD5</acronym> Hash der <acronym>URI</acronym> des Feeds.
            </para>

            <programlisting language="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>
                    Auch wenn es etwas abseits ist, sollte man daran denken zu
                    <classname>Zend_Loader_PluginLoader</classname> einen Cache hinzuzufügen der
                    von <classname>Zend_Feed_Reader</classname> verwendet wird um Erweiterungen zu
                    laden.
                </para>
            </note>
        </sect3>

        <sect3 id="zend.feed.reader.cache-request.http-conditional-get">
            <title>Unterstützung für HTTP Conditional GET</title>

            <para>
                Die große Frage wenn man ofters einen Feed importiert, ist ob er sich geändert hat.
                Wenn ein Cache aktiviert ist, kann man die Unterstützung für <acronym>HTTP</acronym>
                Conditional GET hinzufügen um diese Frage zu beantworten.
            </para>

            <para>
                Durch Verwendung dieser Methode kann man Feeds von <acronym>URI</acronym> anfragen
                und deren letzte bekannte Werte der ETag und Last-Modified Antwort Header mit der
                Anfrage inkludieren (wobei die If-None-Match und If-Modified-Since Header verwendet
                werden). Wenn der Feed auf dem Server unverändert ist, sollte man eine 304 Antwort
                empfangen die <classname>Zend_Feed_Reader</classname> mitteilt das die gecachte
                Version zu verwenden ist. Wenn ein kompletter Feed in einer Antwort mit einem Status
                Code von 200 geschickt wird, bedeutet dieses, das der Feed verändert wurde und
                <classname>Zend_Feed_Reader</classname> wird die neue Version parsen und Sie im
                Cache abspeichern. Es werden auch die neuen Werte der ETag und Last-Modified Header
                für eine zukünftige Verwendung gespeichert.
            </para>

            <para>
                Bei diesen "konditionalen" Abfragen ist nicht garantiert das Sie, vom Server von dem
                man eine <acronym>URI</acronym> abfragt, unterstützt werden, können aber trotzdem
                angefragt werden. Die meisten Feed Quellen wie Blogs sollten hierfür eine
                Unterstützung haben. Um konditionale Anfragen zu erlauben, muss man einen Cache
                bei <classname>Zend_Feed_Reader</classname> angeben.
            </para>

            <programlisting language="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>
                Im obige Beispiel werden, bei aktivierten <acronym>HTTP</acronym> Conditional GET
                Anfragen, die Werte der Antwort Header für ETag und Last-Modified mit dem Feed
                gecacht. Für die nächsten 24 Stunden (die Lebenszeit des Caches) werden Feed am
                Cache nur dann aktualisiert wenn eine nicht-304 Antwort empfangen wird, die ein
                gültiges <acronym>RSS</acronym> oder Atom <acronym>XML</acronym> Dokument enthält.
            </para>

            <para>
                Wenn man darauf anzielt die Antwort Header ausserhalb von
                <classname>Zend_Feed_Reader</classname> zu managen, kann man die relevanten
                If-None-Matches und If-Modified-Since Antwort Header über die <acronym>URI</acronym>
                Import Methode setzen.
            </para>

            <programlisting language="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>Feed URIs von Webseiten erkennen</title>

        <para>
            Dieser Tage ist vielen Webseiten bekannt das der Ort Ihrer <acronym>XML</acronym> Feeds
            nicht immer eindeutig ist. Eine kleine <acronym>RDF</acronym>, <acronym>RSS</acronym>
            oder Atom Grafik hilft wenn der Benutzer die Seite liest, aber was wenn eine Maschine
            kommt und versucht herauszufinden So die Feed sind? Um hierbei zu helfen, zeigen viele
            Webseiten zu Ihren Feeds indem &lt;link&gt; Tags in der &lt;head&gt; Sektion Ihres
            <acronym>HTML</acronym>s verwendet werden. Um diesen Vorteil zu nutzen, kann man
            <classname>Zend_Feed_Reader</classname> verwenden diese Feeds zu erkennen, indem die
            statische <methodname>findFeedLinks()</methodname> Methode verwendet wird.
        </para>

        <para>
            Diese Methode ruft irgendeine <acronym>URI</acronym> auf und sucht nach dem Ort der
            <acronym>RSS</acronym>, <acronym>RDF</acronym> und Atom Feeds mit der Annahme dass das
            <acronym>HTML</acronym> der Webseite nur die relevanten Links enthält. Sie gibt dann ein
            Wert Objekt zurück indem man die Existenz einer <acronym>RSS</acronym>,
            <acronym>RDF</acronym> oder Atom Feed <acronym>URI</acronym> prüfen kann.
        </para>

        <programlisting language="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>
            Basierend auf diesen Links kann man dann, von welchen Quellen man auch immer will,
            importieren indem die übliche Vorgehensweise verwendet wird.
        </para>
  </sect2>

  <sect2 id="zend.feed.reader.retrieve-info">
        <title>Empfangen von Feed Informationen</title>

        <para>
            Das Empfangen von Informationen von einem Feed (wir reden über Einträge und Elemente in
            der nächsten Sektion da Sie identischen Prinzipien folgen) verwendet eine klar
            definierte <acronym>API</acronym> welche exakt die gleiche ist, unabhängig davon ob der
            angefragte Feed <acronym>RSS</acronym>/<acronym>RDF</acronym>/Atom ist. Das selbe gilt
            für Sub-Versionen dieser Standards da wir jede einzelne <acronym>RSS</acronym> und Atom
            Version getestet haben. Wärend sich der darunterliegende <acronym>XML</acronym> Feed
            substantiell unterscheiden kann, im Sinne von Tags und Elementen die vorhanden sind,
            versuchen trotzdem alle ähnliche Informationen zu geben und um das alles zu reflektieren
            werden unterschiede und das Hanteln durch alternative Tags intern von
            <classname>Zend_Feed_Reader</classname> behandelt welche einem ein identisches Interface
            für jeden anzeigt. Idealerweise sollte man sich nicht darum kümmern ob ein Feed
            <acronym>RSS</acronym> oder Atom ist, solange man die Informationen extrahieren kann
            die man benötigt.
        </para>

        <para>
            Natürlich leben wir nicht in einer idealen Welt sodas es Zeiten gibt in denen die
            <acronym>API</acronym> einfach nicht das bietet wonach man sucht. Um hierbei zu helfen
            bietet <classname>Zend_Feed_Reader</classname> ein Plugin System an das es erlaubt
            Erweiterungen zu schreiben und die Kern <acronym>API</acronym> zu erweitern sowie
            alle zusätzliche Daten abzudecken die man von Feeds extrahieren will. Wenn das schreiben
            einer weiteren Erweiterung zu problematisch ist, kann man einfach das darunterliegende
            <acronym>DOM</acronym> oder die XPath Objekte holen und das von Hand in der Anwendung
            machen. Natürlich sollte wirklich eine Erweiterung geschrieben werden, einfach um es
            portabler und wiederverwendbarer zu machen.
        </para>

        <para>
            Hier ist eine Zusammenfassung der Kern <acronym>API</acronym> für Feeds. Man sollte
            beachten das es nicht nur die grundsätzlichen <acronym>RSS</acronym> und Atom
            Standard abdeckt, sondern das es auch eine Anzahl von mitgelieferten Erweiterungen
            gibt die mit <classname>Zend_Feed_Reader</classname> gebündelt sind. Die Benennung
            dieser Methoden von Erweiterungen ist recht generisch - alle erweiterten Methoden
            arbeiten auf dem gleichen Level wie die Kern <acronym>API</acronym> da wir es
            erlauben alle spefizischen Erweiterungs Objekte separat zu empfangen wenn das
            notwendig ist.
        </para>

        <table>
            <title>API Methoden auf dem Level des Feeds</title>

            <tgroup cols="2">
                <tbody>
                    <row>
                        <entry><methodname>getId()</methodname></entry>

                        <entry>
                            Gibt eine eindeutige ID zurück die mit dem Feed assoziiert ist
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getTitle()</methodname></entry>

                        <entry>Gibt den Titel des Feeds zurück</entry>
                    </row>

                    <row>
                        <entry><methodname>getDescription()</methodname></entry>

                        <entry>Gibt die textuelle Beschreibung des Feeds zurück</entry>
                    </row>

                    <row>
                        <entry><methodname>getLink()</methodname></entry>

                        <entry>
                            Gibt eine <acronym>URI</acronym> zu der <acronym>HTML</acronym> Webseite
                            zurück welche die gleiche oder ähnliche Informationen wie dieser Feed
                            enthält (z.B. wenn der Feed von einem Blog ist, sollte die
                            <acronym>URI</acronym> des Blogs enthalten sein indem die
                            <acronym>HTML</acronym> Version des Eintrags gelesen werden kann)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getFeedLink()</methodname></entry>

                        <entry>
                            Gibt die <acronym>URI</acronym> dieses Feeds zurück, welche die gleiche
                            sein sollte wie die <acronym>URI</acronym> welche verwendet wurde um
                            den Feed zu importieren
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getAuthors()</methodname></entry>

                        <entry>
                            Gibt ein Array aller Authoren zurück die mit diesem Feed assoziiert
                            sind, inklusive Email Adressen im String des Authors wenn vorhanden
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getAuthor(integer $index = 0)</methodname></entry>

                        <entry>
                            Gibt entweder den ersten bekannten Author zurück, oder mit dem
                            optionalen Parameter <varname>$index</varname> jeden spezifischen
                            Index des Arrays von Authoren (gibt null bei einem ungültigen Index
                            zurück).
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getDateCreated()</methodname></entry>

                        <entry>
                            Gibt das Datum zurück zu dem dieser Feed erstellt wurde. Generell nur
                            anwendbar bei Atom da es das Datum repräsentiert zu der das Atom 1.0
                            Dokument erstellt wurde das die Ressource beschreibt.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getDateModified()</methodname></entry>

                        <entry>
                            Gibt das Datum zurück zu dem der Feed das letzte mal geändert wurde
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getLanguage()</methodname></entry>

                        <entry>
                            Gibt die Sprache des Feeds zurüc (wenn definiert) oder einfach die
                            Sprache die im <acronym>XML</acronym> Dokument notiert wurde
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getGenerator()</methodname></entry>

                        <entry>
                            Gibt den Erzeuger des Feeds zurück, z.B. die Software die Ihm erzeugt
                            hat. Das kann sich zwischen <acronym>RSS</acronym> und Atom
                            unterscheiden, da Atom eine andere Schreibweise definiert.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getCopyright()</methodname></entry>

                        <entry>
                            Gibt alle Copyright Notizen zurück die mit dem Feed assoziiert sind
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>

        <para>
            Angehend von der Vielzahl von Feeds in der Wildnis, werden einige dieser Methoden
            erwartungsgemäßg <constant>NULL</constant> zurückgeben, was anzeigt das die relevanten
            Informationen nicht gefunden wurden. Wo es möglich ist wird
            <classname>Zend_Feed_Reader</classname> wärend der Suche auf alternative Elemente
            zurück greifen. Zum Beispiel ist das Durchsuchen eines <acronym>RSS</acronym> Feeds
            nach einem Modifikations Datum komplizierter als es aussieht. <acronym>RSS</acronym>
            Feeds sollten ein <code>&lt;lastBuildDate&gt;</code> Tag und/oder ein
            <code>&lt;pubDate&gt;</code> Element enthalten. Aber was wenn Sie es nicht tun, weil es
            z.B. ein <acronym>RSS</acronym> 1.0 Feed ist? Vielleicht ist stattdessen ein
            <code>&lt;atom:updated&gt;</code> Element mit identischen Informationen vorhanden (Atom
            kann verwendet werden um die <acronym>RSS</acronym> Syntax anzubieten)? Bei einem
            Fehlschlag können wir einfach auf die Einträge sehen, den aktuellsten herausholen, und
            sein <code>&lt;pubDate&gt;</code> Element verwenden. In der Annahme das es existiert...
            viele Feeds verwenden auch Dublin Core 1.0/1.1 <code>&lt;dc:date&gt;</code> Elemente für
            Feeds und Einträge. Oder wir können wieder ein Atom finden das herumliegt.
        </para>

        <para>
            Der Punkt ist, das <classname>Zend_Feed_Reader</classname> entwickelt wurde um das zu
            wissen. Wenn man nach dem Änderungsdatum fragt (oder irgendwas anderes), wird er
            starten und alle diese Alternativen suchen bis er entweder aufgibt und
            <constant>NULL</constant> zurückgibt, oder eine Alternative findet welche die richtige
            Antwort hat.
        </para>

        <para>
            Zusätzlich zu den obigen Methoden, implementieren alle Feed Objekte Methoden für das
            empfangen der <acronym>DOM</acronym> und XPath Objekte für die aktuellen Feeds wie
            vorher beschrieben. Feed Objekte implementieren auch die Interfaces für
            <acronym>SPL</acronym> Iterator und Countable. Die erweiterte <acronym>API</acronym>
            wird anbei zusammengefasst.
        </para>

        <table>
            <title>Erweiterte API Methoden auf Level des Feeds</title>

            <tgroup cols="2">
                <tbody>
                    <row>
                        <entry><methodname>getDomDocument()</methodname></entry>

                        <entry>
                            Gibt das elterliche <classname>DOMDocument</classname> Objekt für das
                            komplette <acronym>XML</acronym> Quelldokument zurück
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getElement()</methodname></entry>

                        <entry>
                            Gibt das aktuelle <classname>DOMElement</classname> Objekt des Feed
                            Levels zurück
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>saveXml()</methodname></entry>

                        <entry>
                            Gibt einen String zurück der ein <acronym>XML</acronym> Dokument
                            zurück welches das komplette Feed Element enthält (das ist nicht das
                            originale Dokument sondern eine nachgebaute Version)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getXpath()</methodname></entry>

                        <entry>
                            Gibt das intern verwendete <classname>DOMXPath</classname> Objekt
                            zurück mit dem Abfragen auf das <classname>DOMDocument</classname>
                            Objekt durchgeführt werden (das enthält die Kern und Erweiterungs
                            Namespaces die vor-registriert sind)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getXpathPrefix()</methodname></entry>

                        <entry>
                            Gibt den gültigen <acronym>DOM</acronym> Pfad Präfix zurück der bei
                            allen XPath Abfragen passt die dem Feed entsprechen der abgefragt wird.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getEncoding()</methodname></entry>

                        <entry>
                            Gibt die Kodierung des <acronym>XML</acronym> Quelldokuments zurück
                            (Beachte: Das kann nicht verwendet werden für Fehler wie einen
                            Server der Dokumente in einer anderen Kodierung verschickt)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>count()</methodname></entry>

                        <entry>
                            Gibt eine Zahl von Einträgen oder Elementen zurück welche dieser Feed
                            enthält (implementiert das <acronym>SPL</acronym> Interface
                            <classname>Countable</classname>)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>current()</methodname></entry>

                        <entry>
                            Gibt nur den aktuellen Eintrag zurück (verwendet den aktuellen
                            Index von <methodname>key()</methodname>)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>key()</methodname></entry>

                        <entry>Gibt den aktuellen Index für Einträge zurück</entry>
                    </row>

                    <row>
                        <entry><methodname>next()</methodname></entry>

                        <entry>Addiert den Wert des Index für Einträge um Eins</entry>
                    </row>

                    <row>
                        <entry><methodname>rewind()</methodname></entry>

                        <entry>Resetiert den Index für Einträge auf 0</entry>
                    </row>

                    <row>
                        <entry><methodname>valid()</methodname></entry>

                        <entry>
                            Prüft pb der aktuelle Index für Einträge gültig ist, z.B. ob er nicht
                            unter 0 fällt und die Anzahl der existierenden Einträge nicht
                            übersteigt.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getExtensions()</methodname></entry>

                        <entry>
                            Gibt ein Array aller Erweiterungs Objekte zurück die für den aktuellen
                            Feed geladen sind (Beachte: sowohl Feel-Level als auch Element-Level
                            Erweiterungen exstieren, aber nur Feed-Level Erweiterungen werden hier
                            zurückgegeben). Die Array Schlüssel sind in der Form
                            (ErweiterungsName)_Feed.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getExtension(string $name)</methodname></entry>

                        <entry>
                            Gibt ein Erweiterungs Objekt für den Feed zurück der unter dem
                            angegebenen Namen registriert ist. Das erlaubt einen feiner
                            gestaffelten Zugriff auf Erweiterungen welche andernfalls in der
                            Implementation der standardmäßigen <acronym>API</acronym> Methoden
                            versteckt wären.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getType()</methodname></entry>

                        <entry>
                            Gibt eine statische Klassenkonstante zurück (z.B.
                            <constant>Zend_Feed_Reader::TYPE_ATOM_03</constant>, z.B. Atom 0.3)
                            welche exakt anzeigt welche Art von Feed gerade konsumiert wird.
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>
    </sect2>

    <sect2 id="zend.feed.reader.entry">
        <title>Empfangen von Informationen aus Einträgen/Elementen</title>

        <para>
            Das Empfangen von Informationen für spezifische Einträge oder Elemente (abhängig davon
            ob man Atom oder <acronym>RSS</acronym> spricht) ist identisch wie bei den Daten auf
            Feed Level. Der Zugriff auf Einträge ist einfach ein Fall von Iteration über ein Feed
            Objekt oder durch Verwendung des <acronym>SPL</acronym> Interfaces
            <classname>Iterator</classname> welches Feed Objekte implementieren und durch
            Aufruf der betreffenden Methoden auf Ihnen.
        </para>

        <table>
            <title>API Methoden auf Level des Eintrags</title>

            <tgroup cols="2">
                <tbody>
                    <row>
                        <entry><methodname>getId()</methodname></entry>

                        <entry>Gibt eine eindeutige ID für den aktuellen Eintrag zurück</entry>
                    </row>

                    <row>
                        <entry><methodname>getTitle()</methodname></entry>

                        <entry>Gibt den Titel des aktuellen Eintrags zurück</entry>
                    </row>

                    <row>
                        <entry><methodname>getDescription()</methodname></entry>

                        <entry>Gibt eine Beschreibung des aktuellen Eintrags zurück</entry>
                    </row>

                    <row>
                        <entry><methodname>getLink()</methodname></entry>

                        <entry>
                            Gibt eine <acronym>URI</acronym> zur <acronym>HTML</acronym> Version des
                            aktuellen Eintrags zurück
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getPermaLink()</methodname></entry>

                        <entry>
                            Gibt einen permanenten Link zum aktuellen Eintrag zurück
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getAuthors()</methodname></entry>

                        <entry>
                            Gibt ein Array aller Authoren zurück die mit diesem Eintrag assoziiert
                            sind, inklusive Email Adresse wenn diese im String des Authors vorhanden
                            ist
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getAuthor($index = 0)</methodname></entry>

                        <entry>
                            Gibt entweder den ersten bekannten Author zurück, oder mit dem
                            optionalen Parameter <varname>$index</varname> jeden spezifischen Index
                            aus dem Array der Authoren (gibt null zurück wenn der Index ungültig
                            ist).
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getDateCreated()</methodname></entry>

                        <entry>
                            Gibt das Datum zurück an dem der aktuelle Eintrag erstellt wurde.
                            Generell kann das nur auf Atom angewendet werden wo es das Datum der
                            Ressource beschreibt zu welche das Atom 1.0 Dokument erstellt wurde.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getDateModified()</methodname></entry>

                        <entry>
                            Gibt das Datum zurück an welchem der aktuelle Eintrag zuletzt geändert
                            wurde.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getContent()</methodname></entry>

                        <entry>
                            Gibt den Inhalt des aktuellen Eintrags zurück (das retourniert alle
                            Entities wenn das möglich ist, mit der Annahme das der Content Type
                            <acronym>HTML</acronym> ist). Die Beschreibung wird zurückgegeben wenn
                            ein kein seperates Content Element existiert.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getCommentCount()</methodname></entry>

                        <entry>
                            Gibt die Anzahl der Kommentare zurück die auf diesen Eintrag gemacht
                            wurden seit der Zeit an welcher der Feed erstellt wurde
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getCommentLink()</methodname></entry>

                        <entry>
                            Gibt eine <acronym>URI</acronym> zurück welche auf die
                            <acronym>HTML</acronym> Seite zeigt, auf der Kommentare zu diesem
                            Eintrag gemacht werden können
                        </entry>
                    </row>

                    <row>
                        <entry>
                            <methodname>getCommentFeedLink(string $type =
                                'atom'|'rss')</methodname>
                        </entry>

                        <entry>
                            Gibt eine <acronym>URI</acronym> zurück die auf einen Feed zeigt welcher
                            vom angegebenen Typ ist, und alle Kommentare für diesen Eintrag enthält
                            (Der Typ ist standardmäßig Atom/<acronym>RSS</acronym> abhängig vom
                            aktuellen Feed Typ).
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>

        <para>
            Die erweiterte <acronym>API</acronym> für Einträge ist identisch zu der für die Feed
            mit der Aufnahme der Iterator Methoden die hier nicht benötigt werden.
        </para>

        <caution>
            <para>
                Es gibt oft Missverständnisse über die Konzepte vom Zeitpunkt der Änderung und des
                Erstellungsdatums. In Atom, sind diese zwei klar definierte Konzepte aber in
                <acronym>RSS</acronym> sind Sie vage. <acronym>RSS</acronym> 2.0 definiert ein
                einzelnes <emphasis>&lt;pubDate&gt;</emphasis> Element das typischerweise auf das
                Datum referiert an dem dieser Eintrag veröffentlicht wurde, z.B. etwas in der Art
                eines Erstellungsdatums. Das ist nicht immer das gleiche, und kann sich durch
                Updates ändern oder auch nicht. Als Resultat sollte man sich, wenn man wirklich
                prüfen will ob der Eintrag geändert wurde oder nicht, nicht auf das Ergebnis von
                <methodname>getDateModified()</methodname> verlassen. Stattdessen sollte man
                Erwägen den <acronym>MD5</acronym> Hash von drei anderen verknpüften Elementen
                zu beobachten, z.B. durch Verwendung von <methodname>getTitle()</methodname>,
                <methodname>getDescription()</methodname> und
                <methodname>getContent()</methodname>. Wenn der Eintrag wirklich geändert wurde,
                gibt diese Hash Berechnung ein anderes Ergebnis als die vorher gespeicherten Hashs
                für den gleichen Eintrag. Weitere Schritte in diesen Wassern zeigen das die Daten
                von Feeds unterschiedlichen Standards folgen. Atom und Dublin Core Daten sollten
                <acronym>ISO</acronym> 86001 folgen und <acronym>RSS</acronym> Daten sollten
                <acronym>RFC</acronym> 822 oder <acronym>RFC</acronym> 2822 folgen welche auch
                üblicherweise verwendet werden. Datumsmethoden werfen eine Exception wenn
                <classname>Zend_Date</classname> das Datum durch Verwendung der obigen Standards
                nicht laden kann.
            </para>
        </caution>

        <warning>
            <para>
                Die Werte die von diesen Methoden zurückgegeben werden, sind nicht geprüft. Das
                bedeutet das der Benutzer Prüfungen auf allen empfangenen Daten durchführen muss
                inklusive filtern von jeglichem <acronym>HTML</acronym> wie von
                <methodname>getContent()</methodname> bevor es von der eigenen Anwendung ausgegeben
                wird. Es ist zu beachten das die meisten Feeds von externen Quellen kommen, und
                deshalb die normale Annahme sein sollte das man Ihnen nicht trauen kann.
            </para>
        </warning>

        <table>
            <title>Erweiterte API Methoden auf Level des Eintrags</title>

            <tgroup cols="2">
                <tbody>
                    <row>
                        <entry><methodname>getDomDocument()</methodname></entry>

                        <entry>
                            Gibt das elterliche <classname>DOMDocument</classname> Objekt für den
                            kompletten Feed zurück (nicht nur den aktuellen Eintrag)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getElement()</methodname></entry>

                        <entry>
                            Gibt das <classname>DOMDocument</classname> Objekt für den
                            aktuellen Level des Eintrags zurück
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getXpath()</methodname></entry>

                        <entry>
                            Gibt das <classname>DOMXPath</classname> Objekt zurück welches
                            intern verwendet wird um Abfragen auf dem
                            <classname>DOMDocument</classname> Objekt durchzuführen (das enthält
                            auch die vorregistrierten Kern und Erweiterungs Namespaces)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getXpathPrefix()</methodname></entry>

                        <entry>
                            Gibt einen gültigen <acronym>DOM</acronym> Pfad Präfix zurück der allen
                            XPath Abfrage vorangestellt wird, welche dem Eintrag entsprechen der
                            abgefragt wird.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getEncoding()</methodname></entry>

                        <entry>
                            Gibt die Kodierung des <acronym>XML</acronym> Quelldokuments zurück
                            (Achtung: Das kann nicht für Fehler genommen werden bei denen der
                            Server eine andere Kodierung sendet als die Dokumente)
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getExtensions()</methodname></entry>

                        <entry>
                            Gibt ein Array aller Erweiterungsobjekte zurück die für den aktuellen
                            Eintrag geladen wurden (Achtung: Sowohl Erweiterung auf Level von Feeds
                            als auch auf Level von Einträgen existieren, und nur Erweiterungen auf
                            Level von Einträgen werden hier zurückgegeben). Die Arrayschlüssel sind
                            im Format {ErweiterungsName}_Entry.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getExtension(string $name)</methodname></entry>

                        <entry>
                            Gibt das Erweiterungsobjekt zurück für das der Eintrag mit dem
                            angegebenen Namen registriert wurde. Das erlaubt einen feineren
                            Zugriff auf Erweiterungen welche andernfalls innerhalb der
                            Implementierung der standardmäßigen <acronym>API</acronym> Methoden
                            versteckt wären.
                        </entry>
                    </row>

                    <row>
                        <entry><methodname>getType()</methodname></entry>

                        <entry>
                            Gibt eine statische Klassenkonstante zurück (z.B.
                            <constant>Zend_Feed_Reader::TYPE_ATOM_03</constant>, z.B. Atom 0.3)
                            die exakt anzeigt von welcher Art der Feed ist der gerade konsumiert
                            wird.
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>
    </sect2>

    <sect2 id="zend.feed.reader.extending">
        <title>Erweitern der APIs für Feeds und Einträge</title>

        <para>
            Die Erweiterung von <classname>Zend_Feed_Reader</classname> erlaubt es Methoden sowohl
            auf Level von Feeds als auch auf Level von Einträgen hinzuzufügen, welche das Empfangen
            von Informationen abdecken die nicht bereits von <classname>Zend_Feed_Reader</classname>
            unterstützt werden. Bei der Anzahl an <acronym>RSS</acronym> und Atom Erweiterungen die
            existieren, ist das ein guter Weg da <classname>Zend_Feed_Reader</classname> einfach
            nicht alles hinzufügen kann.
        </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 <acronym>RSS</acronym> 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 <acronym>RSS</acronym> 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 <classname>Zend_Loader_PluginLoader</classname>, 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 Dublin Core Metadata Element Set 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>Implements support for the Slash <acronym>RSS</acronym> 1.0 module</entry>
                    </row>

                    <row>
                        <entry>WellFormedWeb</entry>

                        <entry>Implements support for the Well Formed Web CommentAPI 1.0</entry>
                    </row>

                    <row>
                        <entry>Thread</entry>

                        <entry>Implements support for Atom Threading Extensions as described
                        in <acronym>RFC</acronym> 4685</entry>
                    </row>

                    <row>
                        <entry>Podcast</entry>

                        <entry>Implements support for the Podcast 1.0 <acronym>DTD</acronym> from Apple</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 <acronym>RSS</acronym> module - you can insert
            Atom elements into <acronym>RSS</acronym> feeds. I've even seen
            <acronym>RDF</acronym> 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="2">
                <tbody>
                    <row>
                        <entry>Syndication</entry>
                        <entry>Implements Syndication 1.0 support for <acronym>RSS</acronym> feeds</entry>
                    </row>
                    <row>
                        <entry>CreativeCommons</entry>
                        <entry>A <acronym>RSS</acronym> module that adds an element at the &lt;channel&gt;
                        or &lt;item&gt; level that specifies which Creative Commons license
                        applies.</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. Additional non-Core Extensions will be included
            in future iterations of the component.
        </para>

        <para>
            Registering an Extension with
            <classname>Zend_Feed_Reader</classname>, so it is loaded and its <acronym>API</acronym>
            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 <acronym>API</acronym> without any effort. Note that
            Extension names are case sensitive and use camel casing for multiple
            terms.
        </para>

        <programlisting language="php"><![CDATA[
Zend_Feed_Reader::registerExtension('Syndication');
$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
$updatePeriod = $feed->current()->getUpdatePeriod();
]]></programlisting>

        <para>
            In the simple example above, we checked how frequently a feed is being updated
            using the <methodname>getUpdatePeriod()</methodname>
            method. Since it's not part of
            <classname>Zend_Feed_Reader</classname>'s core <acronym>API</acronym>, it could only be
            a method supported by the newly registered Syndication Extension.
        </para>

        <para>
            As you can also notice, the new methods from Extensions are accessible from the main
            <acronym>API</acronym> using <acronym>PHP</acronym>'s magic methods. As an alternative,
            you can also directly access any Extension object for a similar result as seen below.
        </para>

        <programlisting language="php"><![CDATA[
Zend_Feed_Reader::registerExtension('Syndication');
$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
$syndication = $feed->getExtension('Syndication');
$updatePeriod = $syndication->getUpdatePeriod();
]]></programlisting>

        <sect3 id="zend.feed.reader.extending.feed">
            <title>Writing Zend_Feed_Reader Extensions</title>

            <para>
                Inevitably, there will be times when the
                <classname>Zend_Feed_Reader</classname> <acronym>API</acronym> 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 <acronym>RSS</acronym> 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 <acronym>RSS</acronym> 2.0 to include a
                new element per entry supplying the <acronym>ISBN</acronym>-10 or
                <acronym>ISBN</acronym>-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 <acronym>URI</acronym>:
            </para>

            <programlisting language="php"><![CDATA[
JungleBooks 1.0:
http://example.com/junglebooks/rss/module/1.0/
]]></programlisting>

            <para>
                A snippet of <acronym>RSS</acronym> containing this extension in practice could be
                something similar to:
            </para>

            <programlisting language="php"><![CDATA[
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0"
   xmlns:content="http://purl.org/rss/1.0/modules/content/"
   xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/">
<channel>
    <title>Jungle Books Customer Reviews</title>
    <link>http://example.com/junglebooks</link>
    <description>Many book reviews!</description>
    <pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate>
    <jungle:dayPopular>http://example.com/junglebooks/book/938</jungle:dayPopular>
    <item>
        <title>Review Of Flatland: A Romance of Many Dimensions</title>
        <link>http://example.com/junglebooks/review/987</link>
        <author>Confused Physics Student</author>
        <content:encoded>
        A romantic square?!
        </content:encoded>
        <pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate>
        <jungle:isbn>048627263X</jungle:isbn>
    </item>
</channel>
</rss>
]]></programlisting>

            <para>
                Implementing this new <acronym>ISBN</acronym> element as a simple entry level
                extension would require the following class (using your own class
                namespace outside of Zend).
            </para>

            <programlisting language="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'] = $isbn;
        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 <acronym>ISBN</acronym> 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 <acronym>DOM</acronym> 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 <acronym>RSS</acronym> module. While we
                call this an <acronym>RSS</acronym> 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 <acronym>RSS</acronym> 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 language="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 <acronym>API</acronym>.
            </para>

            <programlisting language="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 language="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>
