<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- 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, Atom 0.3
            und Atom 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 DOMDocument, DOMElement und DOMXPath 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> oder XPath verhindert.
            Drittens erlaubt das Importieren von Feeds von einer <acronym>URI</acronym> den
            Vorteil von konditionellen <acronym>HTTP</acronym> <constant>GET</constant> Anfragen
            welche es Servern erlauben eine leere 304 Anfrage auszulösen wenn der angefragte Feed
            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,
            delegiert diese Aufgabe aber an <classname>Zend_Feed_Writer</classname>, seinen Bruder.
        </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>
            <constant>GET</constant> 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(),
        'authors'      => $entry->getAuthors(),
        '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
            DOMDocument oder DOMElement von jeder Klasse extrahieren, oder sogar einen
            <acronym>XML</acronym> String der sie enthält. Es werden auch Methoden angeboten um das
            aktuelle DOMXPath 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 DOMDocument 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 DOMElement
                    des aktuellen Objekts zurück (z.B. den Feed oder aktuellen Eintrag).
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>getXpath()</methodname> gibt das aktuelle DOMXPath 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 DOMXPath 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 <constant>GET</constant> 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
                <constant>GET</constant> 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>

        <para>
            Das zurückgegebene Objekt ist eine Unterklasse von <classname>ArrayObject</classname>
            welche <classname>Zend_Feed_Reader_Collection_FeedLink</classname> heisst, damit es in
            ein Array gecastet werden kann, oder damit man es iterieren kann um auf alle erkannten
            Links zuzugreifen. Trotzdem kann man, als einfache Abkürzung, den ersten
            <acronym>RSS</acronym>, <acronym>RDF</acronym> oder Atom Link holen indem dessen
            öffentliche Eigenschaft wie im beiliegenden Beispiel verwendet wird. Andernfalls ist
            jedes Element von <classname>ArrayObject</classname> ein einfaches Array mit den
            Schlüsseln "type" und "uri" wobei der Typ "rdf", "rss" oder "atom" sein 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>

        <para>
            Diese schnelle Methode gibt nur einen Link für jeden Feed Typ zurück, aber Webseiten
            können viele Links von jedem Typ enthalten. Möglicherweise ist es eine News Site mit
            einem <acronym>RSS</acronym> Feed für jede News Kategorie. Man kann durch alle Links
            iterieren indem der ArrayObject Iterator verwendet wird.
        </para>

        <programlisting language="php"><![CDATA[
$links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');

foreach ($links as $link) {
    echo $link['uri'], "\n";
}
]]></programlisting>
    </sect2>

    <sect2 id="zend.feed.reader.attribute-collections">
        <title>Attribut Sammlungen</title>

        <para>
            In einem Versuch die Rückgabetypen zu vereinfachen, können Rückgabetypen für die
            verschiedenen Feed und Entry Level Methoden ab Zend Framework 1.10 ein Objekt vom Typ
            <classname>Zend_Feed_Reader_Collection_CollectionAbstract</classname> enthalten.
            Abgesehen vom speziellen Klassennamen der anbei erklärt wird, ist es nur eine einfache
            Subklasse von <acronym>SPL</acronym>'s <classname>ArrayObject</classname>.
        </para>

        <para>
            Der Hauptzweck hierbei besteht darin, die Präsentation von so vielen Daten wie möglich
            von den angefragten Elementen zu erlauben, während trotzdem der Zugriff auf die meisten
            relevanten Daten über ein einfaches Array erlaubt wird. Das erzwingt auch einen
            Standardweg um solche Daten zurückzugeben, was vorher zwischen Arrays und Objekten
            gewandert ist.
        </para>

        <para>
            Der neue Klassentyp arbeitet identisch zu <classname>ArrayObject</classname>, mit der
            gleichen kleinen Änderung dass eine neue Methode <methodname>getValues()</methodname>
            existiert welche ein einfaches flaches Array zurückgibt dass die meisten relevanten
            Informationen enthält.
        </para>

        <para>
            Ein einfaches Beispiel hiervon ist
            <methodname>Zend_Feed_Reader_FeedInterface::getCategories()</methodname>. Wenn es in
            irgendeinem <acronym>RSS</acronym> oder Atom Feed verwendet wird, gibt diese Methode
            Daten der Kategorie als Container Objekt zurück welches
            <classname>Zend_Feed_Reader_Collection_Category</classname> genannt wird. Das Container
            Objekt wird, für jede Kategorie, drei Felder an Daten enthalten: term, schema und label.
            "term" ist der grundsätzliche Name der Kategorie, oft Maschinen lesbar (normalerweise
            ein <acronym>URI</acronym> Identifikator) und in <acronym>RSS</acronym> 2.0 auch bekannt
            als "domain". "label" ist ein menschlich lesbarer Kategorie Name welcher
            <acronym>HTML</acronym> Entities unterstützt. In <acronym>RSS</acronym> 2.0 gibt es kein
            Label Attribut deshalb wird es der Bequemlichkeit halber immer auf den selben Wert
            gesetzt wie der Ausdruck.
        </para>

        <para>
            Um auf die Label der Kategorie selbst als einfache Array Werte zuzugreifen könnte man
            das folgende verwenden:
        </para>

        <programlisting language="php"><![CDATA[
$feed = Zend_Feed_Reader::import('http://www.example.com/atom.xml');
$categories = $feed->getCategories();
$labels = array();
foreach ($categories as $cat) {
    $labels[] = $cat['label']
}
]]></programlisting>

        <para>
            Es ist ein erfundenes Beispiel, aber der Punkt ist, dass Label zusammen mit anderen
            Informationen gebunden sind.
        </para>

        <para>
            Trotzdem erlaubt die Container Klasse den Zugriff auf die "relevantesten" Daten als
            einfaches Array indem die Methode <methodname>getValues()</methodname> verwendet wird.
            Das Konzept der "relevantesten" Daten ist offensichtlich ein beurteilter Aufruf. Für
            Kategorien bedeutet es die Label der Kategorien (nicht die Typen oder Schemata) während
            es für Autoren der Name des Autors wäre (nicht deren Email Adressen oder die
            <acronym>URI</acronym>s). Das einfache Array ist flach (nur Werte) und durchläuft
            <methodname>array_unique</methodname> um doppelte Werte zu entfernen.
        </para>

        <programlisting language="php"><![CDATA[
$feed = Zend_Feed_Reader::import('http://www.example.com/atom.xml');
$categories = $feed->getCategories();
$labels = $categories->getValues();
]]></programlisting>

        <para>
            Das obige Beispiel zeigt wie nur die Label und sonst nichts extrahiert wird. Das gibt
            einen einfachen Zugriff auf die Label der Kategorie ohne zusätzliche Arbeit die Daten
            selbst zu extrahieren.
        </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> oder 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>

        <note>
            <para>
                Wärend die Erkennung von Gemeinsamkeiten zwischen den Feed Typen selbst sehr komplex
                ist, sollte erwähnt werden das <acronym>RSS</acronym> selbst eine konstant strittige
                "Spezifikation". Das hat seine Wurzeln im originalen <acronym>RSS</acronym> 2.0
                Dokument welches Doppeldeutigkeiten enthält und die richtige Behandlung alle
                Elemente nicht im Detail erklärt. Als Ergebnis verwendet diese Komponente riguros
                die <acronym>RSS</acronym> 2.0.11 Spezifikation welche vom
                <acronym>RSS</acronym> Advisory Board veröffentlicht wurde und dessen beigefügtes
                <acronym>RSS</acronym> Best Practices Profil. Keine andere Interpretation von
                <acronym>RSS</acronym> 2.0 wird unterstützt wobei Ausnahmen erlaubt sein können wo
                es die anwendung der zwei vorher erwähnten Dokumente nicht direkt verhindert.
            </para>
        </note>

        <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. Und nützliche Erweiterungen können für den
            Framework vorgeschlagen werden um Sie formal hinzuzufügen.
        </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 kann wie die <acronym>URI</acronym> welche verwendet wurde um
                            den Feed zu importieren. Es gibt wichtige Fälle in denen sich der Feed
                            Link unterscheiden kann weil die Quell <acronym>URI</acronym>
                            aktualisiert wird und geplant ist Sie in Zukunft zu entfernen.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt ein Objekt vom Typ
                            <classname>Zend_Feed_Reader_Collection_Author</classname> zurück welches
                            ein <classname>ArrayObject</classname> ist dessen Elemente einfach
                            Arrays sind die eine Kombination der Schlüssel "name", "email" und
                            uri" enthalten. Wo es wegen der Quelldaten irrelevant ist können
                            einige dieser Schlüssel unterdrückt werden.
                        </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 wie vorher beschrieben (gibt
                            <constant>NULL</constant> 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. Das zurückgegebene
                            Datum ist ein <classname>Zend_Date</classname> Objekt.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt das Datum zurück zu dem der Feed das letzte mal geändert wurde. Das
                            zurückgegebene Datum ist ein <classname>Zend_Date</classname> Objekt.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt das Datum zurück an dem der Feed das letzte Mal erstellt wurde. Das
                            zurückgegebene Datum ist ein <classname>Zend_Date</classname> Objekt.
                            Das wird nur von <acronym>RSS</acronym> unterstützt - Atom Feeds geben
                            immer <constant>NULL</constant> zurück.
                        </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 Ihn 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>

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

                        <entry>
                            Gibt ein Array der <acronym>URI</acronym> Endpunkte aller Hub Server
                            zurück welche vom Feed für die Berwendung mit dem Pubsubhubbub
                            Protokoll bekanntgegeben werden, und erlaubt damit das Einschreiben
                            für Feeds für Real-Time Updates.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt ein <classname>Zend_Feed_Reader_Collection_Category</classname>
                            Objekt zurück welches die Details aller Kategorien enthält die im
                            kompletten Feed enthalten sind. Die unterstützten Felder enthalten
                            "term" (den Maschinen lesbaren Namen der Kategorie), "scheme"
                            (dem Schema der Kategorisierung für diese Kategorie), und "label" (ein
                            <acronym>HTML</acronym> dekodierter menschlich lesbarer Kategoriename).
                            Wenn irgendeines der drei Felder abwesend ist, werden Sie entweder auf
                            die näheste vorhandene Alternative gesetzt, oder im Fall von "scheme",
                            auf <constant>NULL</constant> gesetzt.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt ein Array zurück welches Daten enthält die jedem Feed Bild oder
                            Logo angehören oder <constant>NULL</constant> wenn kein Bild gefunden
                            wurde. Das resultierende Array kann die folgenden Schlüssel enthalten:
                            <property>uri</property>, <property>link</property>,
                            <property>title</property>, <property>description</property>,
                            <property>height</property>, und <property>width</property>. Nur Atom
                            Logos enthalten eine <acronym>URI</acronym> so dass die anderen
                            Metadaten nur von <acronym>RSS</acronym> Feeds angehören.
                        </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ährend 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 <command>&lt;lastBuildDate&gt;</command> Tag und (oder) ein
            <command>&lt;pubDate&gt;</command> 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
            <command>&lt;atom:updated&gt;</command> 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 <command>&lt;pubDate&gt;</command> Element verwenden. In der Annahme das es
            existiert... viele Feeds verwenden auch Dublin Core 1.0 oder 1.1
            <command>&lt;dc:date&gt;</command> 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 DOMDocument Objekt für das
                            komplette <acronym>XML</acronym> Quelldokument zurück
                        </entry>
                    </row>

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

                        <entry>
                            Gibt das aktuelle DOMElement 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 DOMXPath Objekt zurück mit dem Abfragen auf
                            das DOMDocument 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). Wo diese
                            nicht definiert ist, wird die Standardkodierung <acronym>UTF-8</acronym>
                            von Unicode angewendet.
                        </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>Setzt den Index für Einträge auf 0 zurück</entry>
                    </row>

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

                        <entry>
                            Prüft ob 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. In den meisten
                            Fällen ist dies das selbe wie die Verwendung von
                            <methodname>getLink()</methodname>.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt ein Objekt vom Typ
                            <classname>Zend_Feed_Reader_Collection_Author</classname> zurück welches
                            ein <classname>ArrayObject</classname> ist, dessen Elemente alle
                            einfache Array sind welche beliebige Kombinationen der Schlüssel "name",
                            "email" und "uri" enthalten können. Wo es für die Quelldaten irrelevant
                            ist können einige dieser Schlüssel unterdrückt sein.
                        </entry>
                    </row>

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

                        <entry>
                            Gibt entweder den ersten bekannten Autor zurück, oder mit dem
                            optionalen Parameter <varname>$index</varname> jeden spezifischen Index
                            aus dem Array der Authoren wie vorher beschrieben (gibt
                            <constant>NULL</constant> 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>getEnclosure()</methodname></entry>

                        <entry>
                            Gibt ein Array zurück welches die Werte aller Attribute eines
                            Multimedia &lt;enclosure&gt; Elements enthält, inklusive der Array
                            Schlüssel: <emphasis>url</emphasis>, <emphasis>length</emphasis>,
                            <emphasis>type</emphasis>. Basierend auf dem <acronym>RSS</acronym> Best
                            Practices Profile des <acronym>RSS</acronym> Advisory Boards, wird keine
                            Unterstützung für mehrere Enclosures angeboten da so eine Unterstützung
                            kein Teil der <acronym>RSS</acronym> Spezifikation ist.
                        </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>

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

                        <entry>
                            Gibt ein <classname>Zend_Feed_Reader_Collection_Category</classname>
                            Objekt zurück welches die Details jeder Kategorie enthält welche mit dem
                            Eintrag assoziiert ist. Die unterstützten Felder sind "term" (der
                            Maschinen lesbare Name der Kategorie), "scheme" (der Name des Schemas
                            der Kategorisierung für diese Kategorie), und "label" (ein
                            <acronym>HTML</acronym> dekodierter menschlich lesbarer Name der
                            Kategorie). Wenn eines der drei Felder nicht vorhanden ist, werden Sie
                            entweder auf den näheste vorhandene Alternative, oder im Fall von
                            "scheme", auf <constant>NULL</constant> gesetzt.
                        </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. Das ist natürlich Inhalts-Orientiert und hilft nicht
                bei der Erkennung von anderen relevanten Elementen. Atom Feeds sollten solche
                Schritte nicht benötigen.
            </para>

            <para>
                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>, oder die <acronym>PHP</acronym> basierenden
                Möglichkeiten für <acronym>RSS</acronym> Daten, 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 DOMDocument Objekt für den kompletten Feed zurück
                            (nicht nur den aktuellen Eintrag)
                        </entry>
                    </row>

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

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

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

                        <entry>
                            Gibt das DOMXPath Objekt zurück welches intern verwendet wird um
                            Abfragen auf dem DOMDocument Objekt durchzuführen (es 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). Die Standard
                            Kodierung welche bei Abwesenheit jeglicher anderen Kodierung angewendet
                            wird, ist die <acronym>UTF-8</acronym> Kodierung von Unicode.
                        </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>
            Es gibt zwei Typen von Erweiterungen, jene welche Informationen von Elementen empfangen
            die unmittelbare Kunder des Root Elements sind (z.B. <command>&lt;channel&gt;</command>
            für <acronym>RSS</acronym> oder <command>&lt;feed&gt;</command> für Atom), und jene die
            Informationen von Kind-Elementen eines Eintrags empfangen (z.B.
            <command>&lt;item&gt;</command> für <acronym>RSS</acronym> oder
            <command>&lt;entry&gt;</command> für Atom). Auf dem Filesystem sind Sie als Klassen in
            einem Namespace gruppiert, basierend auf dem Standardnamen der Erweiterung. Zum Beispiel
            haben wir intern <classname>Zend_Feed_Reader_Extension_DublinCore_Feed</classname> und
            <classname>Zend_Feed_Reader_Extension_DublinCore_Entry</classname> Klassen welche zwei
            Klassen sind welche die Unterstützung für Dublin Core 1.0/1.1 implementieren.
        </para>

        <para>
            Erweiterungen werden in <classname>Zend_Feed_Reader</classname> durch Verwendung von
            <classname>Zend_Loader_PluginLoader</classname> geladen, sodas dessen Operationen
            ähnlich denen anderer Zend Framework Komponenten ist.
            <classname>Zend_Feed_Reader</classname> kommt bereits mit einer Anzahl dieser
            Erweiterungen. Trotzdem müssen jene, die nicht intern verwendet und standardmäßig
            registriert werden (sogenannte Core Erweiterungen), bei
            <classname>Zend_Feed_Reader</classname> registriert werden bevor Sie verwendet werden.
            Die gebündelten Erweiterungen sind:
        </para>

        <table>
            <title>Core Extensions (pre-registered)</title>

            <tgroup cols="2">
                <tbody>
                    <row>
                        <entry>DublinCore (Feed und Eintrag)</entry>

                        <entry>
                            Implementiert die Unterstützung für das Dublin Core Metadata Element
                            Set 1.0 und 1.1
                        </entry>
                    </row>

                    <row>
                        <entry>Content (nur Eintrag)</entry>
                        <entry>Implementiert Unterstützung für Content 1.0</entry>
                    </row>

                    <row>
                        <entry>Atom (Feed und Eintrag)</entry>
                        <entry>Implementiert Unterstützung für Atom 0.3 und Atom 1.0</entry>
                    </row>

                    <row>
                        <entry>Slash</entry>

                        <entry>
                            Implementiert Unterstützung für das Slash <acronym>RSS</acronym> 1.0
                            Modul
                        </entry>
                    </row>

                    <row>
                        <entry>WellFormedWeb</entry>

                        <entry>
                            Implementiert Unterstützung für das Well Formed Web CommentAPI 1.0
                        </entry>
                    </row>

                    <row>
                        <entry>Thread</entry>

                        <entry>
                            Implementiert Unterstützung für Atom Threading Erweiterungen wie in
                            <acronym>RFC</acronym> 4685 beschrieben
                        </entry>
                    </row>

                    <row>
                        <entry>Podcast</entry>

                        <entry>
                            Implementiert Unterstützung für das Podcast 1.0 <constant>DTD</constant>
                            von Apple
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>

        <para>
            Die Core Erweiterungen sind irgendwie speziell da Sie extrem allgemein sind und viele
            Facetten haben. Zum Beispiel haben wir eine Core Erweiterung für Atom. Atom ist als
            Erweiterung (und nicht nur als Basis Klasse) implementiert weil es ein gültiges
            <acronym>RSS</acronym> Modul dupliziert - so kann man Atom Elemente in
            <acronym>RSS</acronym> Feeds einfügen. Wir haben sogar <acronym>RDF</acronym> Feeds
            gesehen die viel von Atom verwenden statt den üblicheren Erweiterungen wie Dublin Core.
        </para>

        <table>
            <title>Nicht-Core Erweiterungen (müssen per Hand registriert werden)</title>

            <tgroup cols="2">
                <tbody>
                    <row>
                        <entry>Syndication</entry>

                        <entry>
                            Implementiert Unterstützung für Syndication 1.0 <acronym>RSS</acronym>
                            Feeds
                        </entry>
                    </row>

                    <row>
                        <entry>CreativeCommons</entry>

                        <entry>
                            Ein <acronym>RSS</acronym> Modul das ein Element auf &lt;channel&gt;
                            oder &lt;item&gt; Level hinzufügt welches spezifiziert welche
                            Creative Commons Lizenz anzuwenden ist.
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>

        <para>
            Die zusätzlichen nicht-Core Erweiterungen werden angeboten aber standardmäßig bei
            <classname>Zend_Feed_Reader</classname> nicht registriert. Wenn man Sie verwenden will,
            muß man <classname>Zend_Feed_Reader</classname> sagen dass Sie diese zusätzlich zum
            Importieren eines Feeds laden soll. Zusätzliche nicht-Core Erweiterungen werden in
            zukünftigen Releases dieser Komponente enthalten sein.
        </para>

        <para>
            Das Registrieren einer Erweiterung bei <classname>Zend_Feed_Reader</classname>, so dass
            diese geladen wird und dessen <acronym>API</acronym> für Feed und Entry Objekte zur
            Verfügung steht, ist eine einfache Sache wenn der
            <classname>Zend_Loader_PluginLoader</classname> verwendet wird. Hier registrieren wir
            die optionale Slash Erweiterung und finden heraus das Sie direkt von der Entry Level
            <acronym>API</acronym> heraus aufgerufen werden kann, ohne große Dinge notwendig sind.
            Es ist zu beachten das die Namen der Erweiterungen von der Schreibweise abhängig sind
            und Camel Casing für mehrere Ausdrücke verwenden.
        </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>
            Im obigen Beispiel haben wir geprüft wie oft ein Feed aktualisiert wurde indem wir die
            <methodname>getUpdatePeriod()</methodname> Methode verwendet haben. Da das nicht Teil
            der Kern <acronym>API</acronym> von <classname>Zend_Feed_Reader</classname> ist, kann es
            nur eine Methode sein die von der neu registrieren Syndication Erweiterung unterstützt
            wird.
        </para>

        <para>
            Wie man auch sieht, kann man auf die neuen Methoden vlon Erweiterungen aus der Haupt
            <acronym>API</acronym> heraus zugreifen indem <acronym>PHP</acronym>'s magische
            Methoden verwendet werden. Als Alternative kann man, für ein ähnliches Ergebnis, auf
            jedes Erweiterungs Objekt auch direkt zugreifen, wie anbei gezeigt.
        </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>Erweiterungen für Zend_Feed_Reader schreiben</title>

            <para>
                Unweigerlich gibt es Zeiten in denen die <acronym>API</acronym> von
                <classname>Zend_Feed_Reader</classname> einfach nicht in der Lage ist etwas das man
                von einem Feed oder Eintrag benötigt zu erhalten. Man kann die darunterliegenden
                Quell Objekte, wie ein DOMDocument, verwenden um Sie von Hand zu erhalten. Trotzdem
                sind weitere wiederverwendbare Methoden vorhanden indem man Erweiterungen schreibt
                die diese neuen Abfragen unterstützen.
            </para>

            <para>
                Als Beispiel nehmen wir den Fall eine komplett fiktiven Firma an die Jungle Books
                heißt. Jungle Books hat eine Vielzahl an Reviews für Bücher veröffentlicht die Sie
                verkaufen (von externen Quellen und Kunden), welche als <acronym>RSS</acronym> 2.0
                Feed verteilt werden. Die Marketing Abteilung realisiert das Web Anwendungen welche
                diesen Feed verwenden, aktuell nicht herausfinden können welches Buch exakt
                betrachtet wird. Um jedem das Leben leichter zu machen entscheiden Sie dass die
                Streber Abteilung <acronym>RSS</acronym> 2.0 erweitern muß um ein neues Element
                pro Eintrag hinzuzufügen das die <acronym>ISBN</acronym>-10 oder
                <acronym>ISBN</acronym>-13 Zahl der Veröffentlichung die der Eintrag betrifft
                unterstützt. Sie definieren das neue <command>&lt;isbn&gt;</command> Element recht
                einfach mit dem standardmäßigen Namen und Namespace <acronym>URI</acronym>:
            </para>

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

            <para>
                Ein Teil des <acronym>RSS</acronym> das diese Erweiterung in der Praxis enthält
                könnte in etwa so aussehen:
            </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>
                Die Implementierung dieses neuen <acronym>ISBN</acronym> Elements als eine einfache
                Eintrags Level Erweiterung wird die folgende Klasse benötigen (und die Verwendung
                des eigenen Klassen Namespaces ausserhalb von 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>
                Diese Erweiterung ist einfach genug um Ihr zu folgen. Sie erstellt eine neue Methode
                <methodname>getIsbn()</methodname>, welche eine XPath Abfrage auf dem aktuellen
                Eintrag durchführt, um die <acronym>ISBN</acronym> Nummer welche vom
                <command>&lt;jungle:isbn&gt;</command> Element umhüllt ist, zu extrahieren. Das kann
                optional auch im internen nicht-persistenten Cache gespeichert werden (keine
                Notwendigkeit den <acronym>DOM</acronym> abzufragen wenn es auf dem gleichen
                Eintrag nochmals aufgerufen wird). Der Wert wird dem Anrufer zurückgegeben.
                Am Ende haben wir eine geschützte Methode (Sie ist abstrakt, muss also existieren)
                welche den Jungle Books Namespace für Ihre eigenen <acronym>RSS</acronym> Module
                registriert. Wärend wir das ein <acronym>RSS</acronym> Modul nennen, gibt es nichts
                das verhindert dass das gleiche Element in Atom Feeds verwendet wird - und alle
                Erweiterungen welche den Prefix verwenden der von
                <methodname>getXpathPrefix()</methodname> angeboten wird, sind aktuell neutral und
                arbeiten auf <acronym>RSS</acronym> oder Atom Feeds ohne zusätzlichen Code.
            </para>

            <para>
                Da die Erweiterung ausserhalb vom Zend Framework gespeichert ist, muss man den Pfad
                Prefix für die eigenen Erweiterungen registrieren damit
                <classname>Zend_Loader_PluginLoader</classname> diese finden kann. Danach ist es
                einfach ein Problem der Registrierung der Erweiterung, wenn diese nicht bereits
                geladen wurde, und deren Verwendung in der Praxis.
            </para>

            <programlisting language="php"><![CDATA[
if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
    Zend_Feed_Reader::addPrefixPath(
        'My_FeedReader_Extension', '/path/to/My/FeedReader/Extension'
    );
    Zend_Feed_Reader::registerExtension('JungleBooks');
}
$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');

// ISBN für irgendein Buch dem der erste Eintrag im Feed gewidmet war
$firstIsbn = $feed->current()->getIsbn();
]]></programlisting>

            <para>
                Das Schreiben einer Feed Level Erweiterung unterscheidet sich nicht sehr. Der
                Beispiel Feed von vorher enthält ein nicht erwähntes
                <command>&lt;jungle:dayPopular&gt;</command> Element das Jungle Books bei Ihrem
                Standard hinzugefügt haben um einen Link zum beliebtesten Buch des Tages
                hinzuzufügen (im Sinne von Verkehr der Besucher). Hier ist eine Erweiterung welche
                eine <methodname>getDaysPopularBookLink()</methodname> Methode bei der Feed Level
                <acronym>API</acronym> hinzufügt.
            </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>
                Wiederholen wir das letzte Beispiel der Verwendung einer eigenen Erweiterung um zu
                zeigen wie die Methode verwendet wird.
            </para>

            <programlisting language="php"><![CDATA[
if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
    Zend_Feed_Reader::addPrefixPath(
        'My_FeedReader_Extension', '/path/to/My/FeedReader/Extension'
    );
    Zend_Feed_Reader::registerExtension('JungleBooks');
}
$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');

// URI zur Informations Seite des populärsten Buchs des Tages mit Besuchern
$daysPopularBookLink = $feed->getDaysPopularBookLink();

// ISBN für irgendein Buch dem der erste Eintrag im Feed gewidmet war
$firstIsbn = $feed->current()->getIsbn();
]]></programlisting>

            <para>
                Beim Betrachten dieser Beispiele, konnte man sehen das wir Feed und Eintrags
                Erweiterungen nicht separat registriert haben. Erweiterungen im selben Standard
                können sowohl eine Feed und Entry Klasse enthalten oder auch nicht, sodas
                <classname>Zend_Feed_Reader</classname> nur die Registrierung des darüberliegenden
                Eltern Namens benötigt, z.B. JungleBooks, DublinCore, Slash. Intern kann sie
                prüfen für welchen Level Erweiterungen existieren und und diese Laden wenn Sie
                gefunden werden. In unserem Fall haben wir jetzt ein komplettes Set von
                Erweiterungen: <classname>JungleBooks_Feed</classname> und
                <classname>JungleBooks_Entry</classname>.
            </para>
        </sect3>
    </sect2>
</sect1>
