| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390 |
- <?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 <link> Tags in der <head> 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><lastBuildDate></code> Tag und/oder ein
- <code><pubDate></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><atom:updated></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><pubDate></code> Element verwenden. In der Annahme das es existiert...
- viele Feeds verwenden auch Dublin Core 1.0/1.1 <code><dc:date></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>
- There is often confusion over the concepts of modified and
- created dates. In Atom, these are two clearly defined concepts
- (so knock yourself out) but in <acronym>RSS</acronym> they are vague.
- <acronym>RSS</acronym> 2.0
- defines a single <emphasis><pubDate></emphasis> element
- which typically refers to the date this entry was published,
- i.e. a creation date of sorts. This is not always the case, and
- it may change with updates or not. As a result, if you really
- want to check whether an entry has changed, don't rely on the
- results of <methodname>getDateModified()</methodname>. Instead,
- consider tracking the <acronym>MD5</acronym> hash of three other elements
- concatenated, e.g. using <methodname>getTitle()</methodname>,
- <methodname>getDescription()</methodname> and
- <methodname>getContent()</methodname>. If the entry was trully
- updated, this hash computation will give a different result than
- previously saved hashes for the same entry. Further muddying the
- waters, dates in feeds may follow different standards. Atom and
- Dublin Core dates should follow <acronym>ISO</acronym> 8601,
- and <acronym>RSS</acronym> dates should
- follow <acronym>RFC</acronym> 822 or <acronym>RFC</acronym> 2822
- which is also common. Date methods
- will throw an exception if <classname>Zend_Date</classname>
- cannot load the date string using one of the above standards.
- </para>
- </caution>
- <warning>
- <para>
- The values returned from these methods are not validated. This
- means users must perform validation on all retrieved data
- including the filtering of any <acronym>HTML</acronym> such as from
- <methodname>getContent()</methodname> before it is output from
- your application. Remember that most feeds come from external
- sources, and therefore the default assumption should be that
- they cannot be trusted.
- </para>
- </warning>
- <table>
- <title>Extended Entry Level API Methods</title>
- <tgroup cols="2">
- <tbody>
- <row>
- <entry><methodname>getDomDocument()</methodname></entry>
- <entry>
- Returns the parent
- <classname>DOMDocument</classname> object for the
- entire feed (not just the current entry)
- </entry>
- </row>
- <row>
- <entry><methodname>getElement()</methodname></entry>
- <entry>
- Returns the current entry level
- <classname>DOMElement</classname> object
- </entry>
- </row>
- <row>
- <entry><methodname>getXpath()</methodname></entry>
- <entry>
- Returns the <classname>DOMXPath</classname> object
- used internally to run queries on the
- <classname>DOMDocument</classname> object (this
- includes core and Extension namespaces
- pre-registered)
- </entry>
- </row>
- <row>
- <entry><methodname>getXpathPrefix()</methodname></entry>
- <entry>
- Returns the valid <acronym>DOM</acronym> path prefix prepended
- to all XPath queries matching the entry being queried
- </entry>
- </row>
- <row>
- <entry><methodname>getEncoding()</methodname></entry>
- <entry>
- Returns the encoding of the source <acronym>XML</acronym> document
- (note: this cannot account for errors such as the server sending
- documents in a different encoding)
- </entry>
- </row>
- <row>
- <entry><methodname>getExtensions()</methodname></entry>
- <entry>
- Returns an array of all Extension objects loaded for
- the current entry (note: both feed-level and entry-level
- Extensions exist, and only entry-level Extensions are returned
- here). The array keys are in the form {ExtensionName}_Entry.
- </entry>
- </row>
- <row>
- <entry><methodname>getExtension(string $name)</methodname></entry>
- <entry>
- Returns an Extension object for the entry registered under the
- provided name. This allows more fine-grained access to
- Extensions which may otherwise be hidden within the implementation
- of the standard <acronym>API</acronym> methods.
- </entry>
- </row>
- <row>
- <entry><methodname>getType()</methodname></entry>
- <entry>
- Returns a static class constant (e.g.
- <constant>Zend_Feed_Reader::TYPE_ATOM_03</constant>,
- i.e. Atom 0.3) indicating exactly what kind
- of feed is being consumed.
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect2>
- <sect2 id="zend.feed.reader.extending">
- <title>Extending Feed and Entry APIs</title>
- <para>
- Extending <classname>Zend_Feed_Reader</classname> allows you to add
- methods at both the feed and entry level which cover the retrieval
- of information not already supported by
- <classname>Zend_Feed_Reader</classname>. Given the number of
- <acronym>RSS</acronym> and
- Atom extensions that exist, this is a good thing since
- <classname>Zend_Feed_Reader</classname> couldn't possibly add
- everything.
- </para>
- <para>
- There are two types of Extensions possible, those which retrieve
- information from elements which are immediate children of the root
- element (e.g. <code><channel></code> for <acronym>RSS</acronym> or
- <code><feed></code> for Atom) and those who retrieve
- information from child elements of an entry (e.g.
- <code><item></code> for <acronym>RSS</acronym> or <code><entry></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 <channel>
- or <item> 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><isbn></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><jungle:isbn></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><jungle:dayPopular></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>
|