Zend_Feed_Reader Einführung Zend_Feed_Reader ist eine Komponente die verwendet wird um RSS und Atom Feeds jeder Version zu konsumieren, inklusive RDF/RSS 1.0, RSS 2.0 und Atom 0.3/1.0. Die API für das Empfangen von Feed Daten ist relativ einfach da Zend_Feed_Reader in der Lage ist jeden Feed eines jeden Typs mit Hilfe der API 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. Intern arbeitet Zend_Feed_Reader fast komplett auf Basis der Erstellung von XPath Abfragen gegen das Dokument Objekt Modell des Feed XML's. Das DOM wird nicht durch eine gekettete Eigenschaften API wie bei Zend_Feed 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 API durch das Schreiben von Erweiterungen auf einer ähnlichen Basis. Geschwindigkeit wird auf drei Wegen bereitgestellt. Erstens unterstützt Zend_Feed_Reader das Cachen durch Verwendung von Zend_Cache um eine Kopie des Originalen Feed XML zu halten. Das erlaubt es Netzwerk Anfragen für eine Feed URI zu überspringen wenn der Cache gültig ist. Zweitens wird die Feed und Eintrag- Level API durch einen internen Cache gesichert (nicht persistent) damit wiederholte API Aufrufe für den gleichen Feed eine zusätzliche Verwendung von DOM/XPath verhindert. Drittens erlaubt das Importieren von Feeds von einer URI den Vorteil von konditionellen HTTP 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 Zend_Cache den zuletzt empfangenen Feed zusammen mit dem ETag und dem Last-Modified Header Werten die in der HTTP Antwort gesendet wurde. Relativ zu Zend_Feed wurde Zend_Feed_Reader als frei stehender Ersatz für Zend_Feed formuliert der aber nicht mit Zend_Feed 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. Zend_Feed_Reader ist auch nicht dazu fähig Feeds zu erstellen, das wird aber zu einem späteren Zeitpunkt hinzugefügt. Feeds importieren Das importieren eines Feeds mit Zend_Feed_Reader ist zu Zend_Feed nicht sehr unterschiedlich. Feeds können von einem String, einer Datei, URI oder einer Instanz des Typs Zend_Feed_Abstract importiert werden. Das importieren von einer URI kann zusätzlich eine konditionelle HTTP GET Anfrage benützen. Wenn das importieren fehlschlägt, wird eine Exception geworfen. Das Endergebnis wird ein Objekt des Typs Zend_Feed_Reader_FeedInterface sein, die Core Implementation von Zend_Feed_Reader_Feed_Rss und Zend_Feed_Reader_Feed_Atom (Zend_Feed hat alle kurzen Namen genommen!). Beide Objekte unterstützen mehrere (alle existierenden) Versionen dieser breiten Feed Typen. Im folgenden Beispiel importieren wir einen RDF/RSS 1.0 Feed und extrahieren einige grundsätzliche Information die dann in einer Datenbank oder wo anders gespeichert werden können. $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; } ]]> Das obige Beispiel demonstriert die API von Zend_Feed_Reader und es demonstriert auch einige seiner internen Operationen. In Wirklichkeit hat der ausgewählte RDF Feed keine nativen Daten oder Author Elemente, trotzdem verwendet er das Dublin Core 1.1 Modul welches Namespaced Ersteller und Datums Elemente anbietet. Zend_Feed_Reader fällt auf diese und ähnliche Operationen zurück wenn keine relativ nativen Elemente existieren. Wenn es absolut keine alternative finden kann wird es NULL zurückgeben, was anzeigt das die Informationen nicht im Feed gefunden werden können. Man sollte beachten das Klassen die Zend_Feed_Reader_FeedInterface implementieren auch die SPL Interfaces Iterator und Countable implementieren. Feeds können auch von Strings, Dateien und sogar Objekten des Typs Zend_Feed_Abstract importiert werden. Empfangen darunterliegender Quellen von Feeds und Einträgen Zend_Feed_Reader macht sein bestes um Ihnen die Details abzunehmen. Wenn man an einem Feed ausserhalb von Zend_Feed_Reader arbeiten muß, kann man das grundsätzliche DOMDocument oder DOMElement von jeder Klasse extrahieren, oder sogar einen XML 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 saveXml(), getDomDocument(), getElement(), getXpath() und getXpathPrefix(). Diese erlauben es sich von Zend_Feed_Reader zu lösen und das zu tun was man selbst machen will. saveXml() gibt einen XML String zurück der nur das Element enthält welches das aktuelle Objekt repräsentiert. getDomDocument() gibt das DOMDocument Objekt zurück das den kompletten Feed repräsentiert (sogar wenn es von einem Entry Objekt aus aufgerufen wird). getElement() gibt das DOMElement des aktuellen Objekts zurück (z.B. den Feed oder aktuellen Eintrag). getXpath() 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. getXpathPrefix() 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. Hier ist ein Beispiel bei dem ein Feed eine RSS Erweiterung enthalten können die von Zend_Feed_Reader 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 Zend_Feed_Reader oder einer Erweiterung registriert. getXpathPrefix(); $xpath = $feed->getXpath(); $xpath->registerNamespace('admin', 'http://webns.net/mvcb/'); $reportErrorsTo = $xpath->evaluate('string(' . $xpathPrefix . '/admin:errorReportsTo)'); ]]> Wenn man einen bereits registrierten Namespace mit einem anderen Präfix Namen registriert als jenen der von Zend_Feed_Reader intern verwendet wird, zerstört das die Interne Arbeitsweise dieser Komponente. Unterstützung für Caches und intelligente Anfragen Unterstützung für Caches in Zend_Feed_Reader hinzufügen Zend_Feed_Reader unterstützt die Verwendung einer Instanz von Zend_Cache um Feeds zu cachen (als XML) 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 Zend_Feed_Reader mitteilen das er verwendet werden soll! Der verwendete Cache Schlüssel ist "Zend_Feed_Reader_" gefolgt von dem MD5 Hash der URI des Feeds. 7200, 'automatic_serialization' => true ); $backendOptions = array('cache_dir' => './tmp/'); $cache = Zend_Cache::factory( 'Core', 'File', $frontendOptions, $backendOptions ); Zend_Feed_Reader::setCache($cache); ]]> Auch wenn es etwas abseits ist, sollte man daran denken zu Zend_Loader_PluginLoader einen Cache hinzuzufügen der von Zend_Feed_Reader verwendet wird um Erweiterungen zu laden. Unterstützung für HTTP Conditional GET 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 HTTP Conditional GET hinzufügen um diese Frage zu beantworten. Durch Verwendung dieser Methode kann man Feeds von URI 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 Zend_Feed_Reader 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 Zend_Feed_Reader 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. Bei diesen "konditionalen" Abfragen ist nicht garantiert das Sie, vom Server von dem man eine URI 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 Zend_Feed_Reader angeben. 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/'); ]]> Im obige Beispiel werden, bei aktivierten HTTP 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 RSS oder Atom XML Dokument enthält. Wenn man darauf anzielt die Antwort Header ausserhalb von Zend_Feed_Reader zu managen, kann man die relevanten If-None-Matches und If-Modified-Since Antwort Header über die URI Import Methode setzen. Feed URIs von Webseiten erkennen Dieser Tage ist vielen Webseiten bekannt das der Ort Ihrer XML Feeds nicht immer eindeutig ist. Eine kleine RDF, RSS 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 HTMLs verwendet werden. Um diesen Vorteil zu nutzen, kann man Zend_Feed_Reader verwenden diese Feeds zu erkennen, indem die statische findFeedLinks() Methode verwendet wird. Diese Methode ruft irgendeine URI auf und sucht nach dem Ort der RSS, RDF und Atom Feeds mit der Annahme dass das HTML der Webseite nur die relevanten Links enthält. Sie gibt dann ein Wert Objekt zurück indem man die Existenz einer RSS, RDF oder Atom Feed URI prüfen kann. 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/ } ]]> Basierend auf diesen Links kann man dann, von welchen Quellen man auch immer will, importieren indem die übliche Vorgehensweise verwendet wird. Empfangen von Feed Informationen 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 API welche exakt die gleiche ist, unabhängig davon ob der angefragte Feed RSS/RDF/Atom ist. Das selbe gilt für Sub-Versionen dieser Standards da wir jede einzelne RSS und Atom Version getestet haben. Wärend sich der darunterliegende XML 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 Zend_Feed_Reader behandelt welche einem ein identisches Interface für jeden anzeigt. Idealerweise sollte man sich nicht darum kümmern ob ein Feed RSS oder Atom ist, solange man die Informationen extrahieren kann die man benötigt. Natürlich leben wir nicht in einer idealen Welt sodas es Zeiten gibt in denen die API einfach nicht das bietet wonach man sucht. Um hierbei zu helfen bietet Zend_Feed_Reader ein Plugin System an das es erlaubt Erweiterungen zu schreiben und die Kern API 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 DOM 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. Hier ist eine Zusammenfassung der Kern API für Feeds. Man sollte beachten das es nicht nur die grundsätzlichen RSS und Atom Standard abdeckt, sondern das es auch eine Anzahl von mitgelieferten Erweiterungen gibt die mit Zend_Feed_Reader gebündelt sind. Die Benennung dieser Methoden von Erweiterungen ist recht generisch - alle erweiterten Methoden arbeiten auf dem gleichen Level wie die Kern API da wir es erlauben alle spefizischen Erweiterungs Objekte separat zu empfangen wenn das notwendig ist. API Methoden auf dem Level des Feeds getId() Gibt eine eindeutige ID zurück die mit dem Feed assoziiert ist getTitle() Gibt den Titel des Feeds zurück getDescription() Gibt die textuelle Beschreibung des Feeds zurück getLink() Gibt eine URI zu der HTML 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 URI des Blogs enthalten sein indem die HTML Version des Eintrags gelesen werden kann) getFeedLink() Gibt die URI dieses Feeds zurück, welche die gleiche sein sollte wie die URI welche verwendet wurde um den Feed zu importieren getAuthors() Gibt ein Array aller Authoren zurück die mit diesem Feed assoziiert sind, inklusive Email Adressen im String des Authors wenn vorhanden getAuthor(integer $index = 0) Gibt entweder den ersten bekannten Author zurück, oder mit dem optionalen Parameter $index jeden spezifischen Index des Arrays von Authoren (gibt null bei einem ungültigen Index zurück). getDateCreated() 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. getDateModified() Gibt das Datum zurück zu dem der Feed das letzte mal geändert wurde getLanguage() Gibt die Sprache des Feeds zurüc (wenn definiert) oder einfach die Sprache die im XML Dokument notiert wurde getGenerator() Gibt den Erzeuger des Feeds zurück, z.B. die Software die Ihm erzeugt hat. Das kann sich zwischen RSS und Atom unterscheiden, da Atom eine andere Schreibweise definiert. getCopyright() Gibt alle Copyright Notizen zurück die mit dem Feed assoziiert sind
Angehend von der Vielzahl von Feeds in der Wildnis, werden einige dieser Methoden erwartungsgemäßg NULL zurückgeben, was anzeigt das die relevanten Informationen nicht gefunden wurden. Wo es möglich ist wird Zend_Feed_Reader wärend der Suche auf alternative Elemente zurück greifen. Zum Beispiel ist das Durchsuchen eines RSS Feeds nach einem Modifikations Datum komplizierter als es aussieht. RSS Feeds sollten ein <lastBuildDate> Tag und/oder ein <pubDate> Element enthalten. Aber was wenn Sie es nicht tun, weil es z.B. ein RSS 1.0 Feed ist? Vielleicht ist stattdessen ein <atom:updated> Element mit identischen Informationen vorhanden (Atom kann verwendet werden um die RSS Syntax anzubieten)? Bei einem Fehlschlag können wir einfach auf die Einträge sehen, den aktuellsten herausholen, und sein <pubDate> Element verwenden. In der Annahme das es existiert... viele Feeds verwenden auch Dublin Core 1.0/1.1 <dc:date> Elemente für Feeds und Einträge. Oder wir können wieder ein Atom finden das herumliegt. Der Punkt ist, das Zend_Feed_Reader 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 NULL zurückgibt, oder eine Alternative findet welche die richtige Antwort hat. Zusätzlich zu den obigen Methoden, implementieren alle Feed Objekte Methoden für das empfangen der DOM und XPath Objekte für die aktuellen Feeds wie vorher beschrieben. Feed Objekte implementieren auch die Interfaces für SPL Iterator und Countable. Die erweiterte API wird anbei zusammengefasst. Erweiterte API Methoden auf Level des Feeds getDomDocument() Gibt das elterliche DOMDocument Objekt für das komplette XML Quelldokument zurück getElement() Gibt das aktuelle DOMElement Objekt des Feed Levels zurück saveXml() Gibt einen String zurück der ein XML Dokument zurück welches das komplette Feed Element enthält (das ist nicht das originale Dokument sondern eine nachgebaute Version) getXpath() 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) getXpathPrefix() Gibt den gültigen DOM Pfad Präfix zurück der bei allen XPath Abfragen passt die dem Feed entsprechen der abgefragt wird. getEncoding() Gibt die Kodierung des XML Quelldokuments zurück (Beachte: Das kann nicht verwendet werden für Fehler wie einen Server der Dokumente in einer anderen Kodierung verschickt) count() Gibt eine Zahl von Einträgen oder Elementen zurück welche dieser Feed enthält (implementiert das SPL Interface Countable) current() Gibt nur den aktuellen Eintrag zurück (verwendet den aktuellen Index von key()) key() Gibt den aktuellen Index für Einträge zurück next() Addiert den Wert des Index für Einträge um Eins rewind() Resetiert den Index für Einträge auf 0 valid() 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. getExtensions() 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. getExtension(string $name) 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 API Methoden versteckt wären. getType() Gibt eine statische Klassenkonstante zurück (z.B. Zend_Feed_Reader::TYPE_ATOM_03, z.B. Atom 0.3) welche exakt anzeigt welche Art von Feed gerade konsumiert wird.
Empfangen von Informationen aus Einträgen/Elementen Das Empfangen von Informationen für spezifische Einträge oder Elemente (abhängig davon ob man Atom oder RSS 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 SPL Interfaces Iterator welches Feed Objekte implementieren und durch Aufruf der betreffenden Methoden auf Ihnen. API Methoden auf Level des Eintrags getId() Gibt eine eindeutige ID für den aktuellen Eintrag zurück getTitle() Gibt den Titel des aktuellen Eintrags zurück getDescription() Gibt eine Beschreibung des aktuellen Eintrags zurück getLink() Gibt eine URI zur HTML Version des aktuellen Eintrags zurück getPermaLink() Gibt einen permanenten Link zum aktuellen Eintrag zurück getAuthors() Gibt ein Array aller Authoren zurück die mit diesem Eintrag assoziiert sind, inklusive Email Adresse wenn diese im String des Authors vorhanden ist getAuthor($index = 0) Gibt entweder den ersten bekannten Author zurück, oder mit dem optionalen Parameter $index jeden spezifischen Index aus dem Array der Authoren (gibt null zurück wenn der Index ungültig ist). getDateCreated() Returns the date on which the current entry was created. Generally only applicable to Atom where it represents the date the resource described by an Atom 1.0 document was created. getDateModified() Returns the date on which the current entry was last modified getContent() Returns the content of the current entry (this has any entities reversed if possible assuming the content type is HTML). The description is returned if a separate content element does not exist. getCommentCount() Returns the number of comments made on this entry at the time the feed was last generated getCommentLink() Returns a URI pointing to the HTML page where comments can be made on this entry getCommentFeedLink(string $type = 'atom'|'rss') Returns a URI pointing to a feed of the provided type containing all comments for this entry (type defaults to Atom/RSS depending on current feed type).
The extended API for entries is identical to that for feeds with the exception of the Iterator methods which are not needed here. There is often confusion over the concepts of modified and created dates. In Atom, these are two clearly defined concepts (so knock yourself out) but in RSS they are vague. RSS 2.0 defines a single <pubDate> 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 getDateModified(). Instead, consider tracking the MD5 hash of three other elements concatenated, e.g. using getTitle(), getDescription() and getContent(). If the entry was trully updated, this hash computation will give a different result than previously saved hashes for the same entry. Further muddying the waters, dates in feeds may follow different standards. Atom and Dublin Core dates should follow ISO 8601, and RSS dates should follow RFC 822 or RFC 2822 which is also common. Date methods will throw an exception if Zend_Date cannot load the date string using one of the above standards. The values returned from these methods are not validated. This means users must perform validation on all retrieved data including the filtering of any HTML such as from getContent() 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. Extended Entry Level API Methods getDomDocument() Returns the parent DOMDocument object for the entire feed (not just the current entry) getElement() Returns the current entry level DOMElement object getXpath() Returns the DOMXPath object used internally to run queries on the DOMDocument object (this includes core and Extension namespaces pre-registered) getXpathPrefix() Returns the valid DOM path prefix prepended to all XPath queries matching the entry being queried getEncoding() Returns the encoding of the source XML document (note: this cannot account for errors such as the server sending documents in a different encoding) getExtensions() 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. getExtension(string $name) 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 API methods. getType() Returns a static class constant (e.g. Zend_Feed_Reader::TYPE_ATOM_03, i.e. Atom 0.3) indicating exactly what kind of feed is being consumed.
Extending Feed and Entry APIs Extending Zend_Feed_Reader allows you to add methods at both the feed and entry level which cover the retrieval of information not already supported by Zend_Feed_Reader. Given the number of RSS and Atom extensions that exist, this is a good thing since Zend_Feed_Reader couldn't possibly add everything. There are two types of Extensions possible, those which retrieve information from elements which are immediate children of the root element (e.g. <channel> for RSS or <feed> for Atom) and those who retrieve information from child elements of an entry (e.g. <item> for RSS or <entry> 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 Zend_Feed_Reader_Extension_DublinCore_Feed and Zend_Feed_Reader_Extension_DublinCore_Entry classes which are two Extensions implementing Dublin Core 1.0/1.1 support. Extensions are loaded into Zend_Feed_Reader using Zend_Loader_PluginLoader, so their operation will be familiar from other Zend Framework components. Zend_Feed_Reader 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 Zend_Feed_Reader before they are used. The bundled Extensions include: Core Extensions (pre-registered) DublinCore (Feed and Entry) Implements support for Dublin Core Metadata Element Set 1.0 and 1.1 Content (Entry only) Implements support for Content 1.0 Atom (Feed and Entry) Implements support for Atom 0.3 and Atom 1.0 Slash Implements support for the Slash RSS 1.0 module WellFormedWeb Implements support for the Well Formed Web CommentAPI 1.0 Thread Implements support for Atom Threading Extensions as described in RFC 4685 Podcast Implements support for the Podcast 1.0 DTD from Apple
The Core Extensions are somewhat special since they are extremely common and multi-faceted. For example, we have a Core Extension for Atom. Atom is implemented as an Extension (not just a base class) because it doubles as a valid RSS module - you can insert Atom elements into RSS feeds. I've even seen RDF feeds which use a lot of Atom in place of more common Extensions like Dublin Core. Non-Core Extensions (must register manually) Syndication Implements Syndication 1.0 support for RSS feeds CreativeCommons A RSS module that adds an element at the <channel> or <item> level that specifies which Creative Commons license applies.
The additional non-Core Extensions are offered but not registered to Zend_Feed_Reader by default. If you want to use them, you'll need to tell Zend_Feed_Reader to load them in advance of importing a feed. Additional non-Core Extensions will be included in future iterations of the component. Registering an Extension with Zend_Feed_Reader, so it is loaded and its API is available to Feed and Entry objects, is a simple affair using the Zend_Loader_PluginLoader. Here we register the optional Slash Extension, and discover that it can be directly called from the Entry level API without any effort. Note that Extension names are case sensitive and use camel casing for multiple terms. current()->getUpdatePeriod(); ]]> In the simple example above, we checked how frequently a feed is being updated using the getUpdatePeriod() method. Since it's not part of Zend_Feed_Reader's core API, it could only be a method supported by the newly registered Syndication Extension. As you can also notice, the new methods from Extensions are accessible from the main API using PHP's magic methods. As an alternative, you can also directly access any Extension object for a similar result as seen below. getExtension('Syndication'); $updatePeriod = $syndication->getUpdatePeriod(); ]]> Writing Zend_Feed_Reader Extensions Inevitably, there will be times when the Zend_Feed_Reader API is just not capable of getting something you need from a feed or entry. You can use the underlying source objects, like DOMDocument, to get these by hand however there is a more reusable method available by writing Extensions supporting these new queries. As an example, let's take the case of a purely fictitious corporation named Jungle Books. Jungle Books have been publishing a lot of reviews on books they sell (from external sources and customers), which are distributed as an RSS 2.0 feed. Their marketing department realises that web applications using this feed cannot currently figure out exactly what book is being reviewed. To make life easier for everyone, they determine that the geek department needs to extend RSS 2.0 to include a new element per entry supplying the ISBN-10 or ISBN-13 number of the publication the entry concerns. They define the new <isbn> element quite simply with a standard name and namespace URI: A snippet of RSS containing this extension in practice could be something similar to: Jungle Books Customer Reviews http://example.com/junglebooks Many book reviews! Fri, 26 Jun 2009 19:15:10 GMT http://example.com/junglebooks/book/938 Review Of Flatland: A Romance of Many Dimensions http://example.com/junglebooks/review/987 Confused Physics Student A romantic square?! Thu, 25 Jun 2009 20:03:28 -0700 048627263X ]]> Implementing this new ISBN element as a simple entry level extension would require the following class (using your own class namespace outside of Zend). _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/' ); } } ]]> This extension is easy enough to follow. It creates a new method getIsbn() which runs an XPath query on the current entry to extract the ISBN number enclosed by the <jungle:isbn> element. It can optionally store this to the internal non-persistent cache (no need to keep querying the DOM if it's called again on the same entry). The value is returned to the caller. At the end we have a protected method (it's abstract so it must exist) which registers the Jungle Books namespace for their custom RSS module. While we call this an RSS module, there's nothing to prevent the same element being used in Atom feeds - and all Extensions which use the prefix provided by getXpathPrefix() are actually neutral and work on RSS or Atom feeds with no extra code. Since this Extension is stored outside of Zend Framework, you'll need to register the path prefix for your Extensions so Zend_Loader_PluginLoader 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. current()->getIsbn(); ]]> Writing a feed level Extension is not much different. The example feed from earlier included an unmentioned <jungle:dayPopular> 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 getDaysPopularBookLink() method to the feel level API. _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/' ); } } ]]> Let's repeat the last example using a custom Extension to show the method being used. getDaysPopularBookLink(); // ISBN for whatever book the first entry in the feed was concerned with $firstIsbn = $feed->current()->getIsbn(); ]]> 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 Zend_Feed_Reader 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: JungleBooks_Feed and JungleBooks_Entry.