Zend_Feed_Reader.xml 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 17618 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.feed.reader">
  5. <title>Zend_Feed_Reader</title>
  6. <sect2 id="zend.feed.reader.introduction">
  7. <title>Einführung</title>
  8. <para>
  9. <classname>Zend_Feed_Reader</classname> ist eine Komponente die verwendet wird um
  10. <acronym>RSS</acronym> und Atom Feeds jeder Version zu konsumieren, inklusive
  11. <acronym>RDF</acronym>/<acronym>RSS</acronym> 1.0, <acronym>RSS</acronym> 2.0 und
  12. Atom 0.3/1.0. Die <acronym>API</acronym> für das Empfangen von Feed Daten ist relativ
  13. einfach da <classname>Zend_Feed_Reader</classname> in der Lage ist jeden Feed eines
  14. jeden Typs mit Hilfe der <acronym>API</acronym> nach den angefragten Informationen zu
  15. durchsuchen. Wenn die typischen Elemente die diese Informationen enthalten nicht
  16. vorhanden sind, werden diese adaptiert und statt dessen auf eine Vielzahl von
  17. alternativen Elementen zurück gegriffen. Diese Fähigkeit, von Alternativen auszuwählen,
  18. verhindert das Benutzer Ihren eigenen astrakten Layer über die Komponente legen müssen
  19. damit Sie nützlich ist, oder beliebig tiefes Wissen des zugrundeliegenden Standard,
  20. aktueller alternativen und namespaces Erweiterungen haben müssen.
  21. </para>
  22. <para>
  23. Intern arbeitet <classname>Zend_Feed_Reader</classname> fast komplett auf Basis der
  24. Erstellung von XPath Abfragen gegen das Dokument Objekt Modell des Feed
  25. <acronym>XML</acronym>'s. Das <acronym>DOM</acronym> wird nicht durch eine gekettete
  26. Eigenschaften <acronym>API</acronym> wie bei <classname>Zend_Feed</classname> bekannt
  27. gegeben und durch die darunterliegenden <classname>DOMDocument</classname>,
  28. <classname>DOMElement</classname> und <classname>DOMXPath</classname> Objekte für eine
  29. externe Manipulation bekannt gegeben. Dieser Singular Weg des Parsens ist Konsistent
  30. und die Komponente bietet ein Plugin System um dem Feed hinzuzufügen und eine Eintrags
  31. Level <acronym>API</acronym> durch das Schreiben von Erweiterungen auf einer ähnlichen
  32. Basis.
  33. </para>
  34. <para>
  35. Geschwindigkeit wird auf drei Wegen bereitgestellt. Erstens unterstützt
  36. <classname>Zend_Feed_Reader</classname> das Cachen durch Verwendung von
  37. <classname>Zend_Cache</classname> um eine Kopie des Originalen Feed
  38. <acronym>XML</acronym> zu halten. Das erlaubt es Netzwerk Anfragen für eine Feed
  39. <acronym>URI</acronym> zu überspringen wenn der Cache gültig ist. Zweitens wird die
  40. Feed und Eintrag- Level <acronym>API</acronym> durch einen internen Cache gesichert
  41. (nicht persistent) damit wiederholte <acronym>API</acronym> Aufrufe für den gleichen
  42. Feed eine zusätzliche Verwendung von <acronym>DOM</acronym>/XPath verhindert.
  43. Drittens erlaubt das Importieren von Feeds von einer <acronym>URI</acronym> den
  44. Vorteil von konditionellen <acronym>HTTP</acronym> GET Anfragen was es Servern
  45. erlaubt eine leere 304 Anfrage auszulösen wenn der angefragte Fed seit der Zeit zu der
  46. er das letzte Mal angefragt wurde, nicht verändert wurde. Im letzten Fall hält eine
  47. Instanz von <classname>Zend_Cache</classname> den zuletzt empfangenen Feed zusammen mit
  48. dem ETag und dem Last-Modified Header Werten die in der <acronym>HTTP</acronym>
  49. Antwort gesendet wurde.
  50. </para>
  51. <para>
  52. Relativ zu <classname>Zend_Feed</classname> wurde
  53. <classname>Zend_Feed_Reader</classname> als frei stehender Ersatz für
  54. <classname>Zend_Feed</classname> formuliert der aber nicht mit
  55. <classname>Zend_Feed</classname> rückwärts kompatibel ist. Aber es ist eine Alternative
  56. die einer anderen Ideologie folgt die darin fokusiert ist einfach verwendbar zu sein,
  57. flexibel, konsistent und durch das Plugin System erweiterbar.
  58. <classname>Zend_Feed_Reader</classname> ist auch nicht dazu fähig Feeds zu erstellen,
  59. das wird aber zu einem späteren Zeitpunkt hinzugefügt.
  60. </para>
  61. </sect2>
  62. <sect2 id="zend.feed.reader.import">
  63. <title>Feeds importieren</title>
  64. <para>
  65. Das importieren eines Feeds mit <classname>Zend_Feed_Reader</classname> ist zu
  66. <classname>Zend_Feed</classname> nicht sehr unterschiedlich. Feeds können von einem
  67. String, einer Datei, <acronym>URI</acronym> oder einer Instanz des Typs
  68. <classname>Zend_Feed_Abstract</classname> importiert werden. Das importieren von einer
  69. <acronym>URI</acronym> kann zusätzlich eine konditionelle <acronym>HTTP</acronym>
  70. GET Anfrage benützen. Wenn das importieren fehlschlägt, wird eine Exception geworfen.
  71. Das Endergebnis wird ein Objekt des Typs
  72. <classname>Zend_Feed_Reader_FeedInterface</classname> sein, die Core Implementation
  73. von <classname>Zend_Feed_Reader_Feed_Rss</classname> und
  74. <classname>Zend_Feed_Reader_Feed_Atom</classname> (<classname>Zend_Feed</classname>
  75. hat alle kurzen Namen genommen!). Beide Objekte unterstützen mehrere (alle
  76. existierenden) Versionen dieser breiten Feed Typen.
  77. </para>
  78. <para>
  79. Im folgenden Beispiel importieren wir einen
  80. <acronym>RDF</acronym>/<acronym>RSS</acronym> 1.0 Feed und extrahieren einige
  81. grundsätzliche Information die dann in einer Datenbank oder wo anders gespeichert
  82. werden können.
  83. </para>
  84. <programlisting language="php"><![CDATA[
  85. $feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
  86. $data = array(
  87. 'title' => $feed->getTitle(),
  88. 'link' => $feed->getLink(),
  89. 'dateModified' => $feed->getDateModified(),
  90. 'description' => $feed->getDescription(),
  91. 'language' => $feed->getLanguage(),
  92. 'entries' => array(),
  93. );
  94. foreach ($feed as $entry) {
  95. $edata = array(
  96. 'title' => $entry->getTitle(),
  97. 'description' => $entry->getDescription(),
  98. 'dateModified' => $entry->getDateModified(),
  99. 'author' => $entry->getAuthor(),
  100. 'link' => $entry->getLink(),
  101. 'content' => $entry->getContent()
  102. );
  103. $data['entries'][] = $edata;
  104. }
  105. ]]></programlisting>
  106. <para>
  107. Das obige Beispiel demonstriert die <acronym>API</acronym> von
  108. <classname>Zend_Feed_Reader</classname> und es demonstriert auch einige seiner
  109. internen Operationen. In Wirklichkeit hat der ausgewählte <acronym>RDF</acronym> Feed
  110. keine nativen Daten oder Author Elemente, trotzdem verwendet er das Dublin Core 1.1
  111. Modul welches Namespaced Ersteller und Datums Elemente anbietet.
  112. <classname>Zend_Feed_Reader</classname> fällt auf diese und ähnliche Operationen zurück
  113. wenn keine relativ nativen Elemente existieren. Wenn es absolut keine alternative
  114. finden kann wird es <constant>NULL</constant> zurückgeben, was anzeigt das die
  115. Informationen nicht im Feed gefunden werden können. Man sollte beachten das Klassen die
  116. <classname>Zend_Feed_Reader_FeedInterface</classname> implementieren auch
  117. die <acronym>SPL</acronym> Interfaces <classname>Iterator</classname> und
  118. <classname>Countable</classname> implementieren.
  119. </para>
  120. <para>
  121. Feeds können auch von Strings, Dateien und sogar Objekten des Typs
  122. <classname>Zend_Feed_Abstract</classname> importiert werden.
  123. </para>
  124. <programlisting language="php"><![CDATA[
  125. // von einer URI
  126. $feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
  127. // von einem String
  128. $feed = Zend_Feed_Reader::importString($feedXmlString);
  129. // von einer Datei
  130. $feed = Zend_Feed_Reader::importFile('./feed.xml');
  131. // von einem abstrakten Zend_Feed_Abstract Objekt
  132. $zfeed = Zend_Feed::import('http://www.planet-php.net/atom/');
  133. $feed = Zend_Feed_Reader::importFeed($zfeed);
  134. ]]></programlisting>
  135. </sect2>
  136. <sect2 id="zend.feed.reader.sources">
  137. <title>Empfangen darunterliegender Quellen von Feeds und Einträgen</title>
  138. <para>
  139. <classname>Zend_Feed_Reader</classname> macht sein bestes um Ihnen die Details
  140. abzunehmen. Wenn man an einem Feed ausserhalb von
  141. <classname>Zend_Feed_Reader</classname> arbeiten muß, kann man das grundsätzliche
  142. <classname>DOMDocument</classname> oder <classname>DOMElement</classname> von jeder
  143. Klasse extrahieren, oder sogar einen <acronym>XML</acronym> String der sie enthält.
  144. Es werden auch Methoden angeboten um das aktuelle <classname>DOMXPath</classname>
  145. Objekt (mit allen registrierten Kern und Erweiterungs Namespaces) zu extrahieren, und
  146. den richtigen Präfix der in allen XPath Anfragen für den aktuellen Feed oder Eintrag
  147. verwendet wird. Die grundsätzlich zu verwenden Methoden (für jedes Objekt) sind
  148. <methodname>saveXml()</methodname>, <methodname>getDomDocument()</methodname>,
  149. <methodname>getElement()</methodname>, <methodname>getXpath()</methodname> und
  150. <methodname>getXpathPrefix()</methodname>. Diese erlauben es sich von
  151. <classname>Zend_Feed_Reader</classname> zu lösen und das zu tun was man selbst
  152. machen will.
  153. </para>
  154. <itemizedlist>
  155. <listitem>
  156. <para>
  157. <methodname>saveXml()</methodname> gibt einen <acronym>XML</acronym> String
  158. zurück der nur das Element enthält welches das aktuelle Objekt repräsentiert.
  159. </para>
  160. </listitem>
  161. <listitem>
  162. <para>
  163. <methodname>getDomDocument()</methodname> gibt das
  164. <classname>DOMDocument</classname> Objekt zurück das den kompletten Feed
  165. repräsentiert (sogar wenn es von einem Entry Objekt aus aufgerufen wird).
  166. </para>
  167. </listitem>
  168. <listitem>
  169. <para>
  170. <methodname>getElement()</methodname> gibt das <classname>DOMElement</classname>
  171. des aktuellen Objekts zurück (z.B. den Feed oder aktuellen Eintrag).
  172. </para>
  173. </listitem>
  174. <listitem>
  175. <para>
  176. <methodname>getXpath()</methodname> gibt das aktuelle
  177. <classname>DOMXPath</classname> Objekt für den aktuellen Feed zurück (sogar wenn
  178. es von einem Entry Objekt aus aufgerufen wird) mit den Namespaces des aktuellen
  179. Feed Typs und allen vor-registrierten geladenen Erweiterungen.
  180. </para>
  181. </listitem>
  182. <listitem>
  183. <para>
  184. <methodname>getXpathPrefix()</methodname> gibt den Präfix der Abfrage für das
  185. aktuelle Objekt zurück (z.B. den Feed oder den aktuellen Eintrag) welcher den
  186. richtigen XPath Query Pfad für den spezifizierten Feed oder Eintrag enthält.
  187. </para>
  188. </listitem>
  189. </itemizedlist>
  190. <para>
  191. Hier ist ein Beispiel bei dem ein Feed eine <acronym>RSS</acronym> Erweiterung enthalten
  192. können die von <classname>Zend_Feed_Reader</classname> nicht out of the Box unterstützt
  193. wird. Beachtenswert ist, das man eine Erweiterungen schreiben und registrieren könnte
  194. (wird später behandelt) um das zu bewerkstelligen, aber das ist nicht immer eine
  195. Garantie für einen schnellen Check. Man muß jeden neuen Namespace beim
  196. <classname>DOMXPath</classname> Objekt registrieren bevor es verwendet wird ausser Sie
  197. werden vorab von <classname>Zend_Feed_Reader</classname> oder einer Erweiterung
  198. registriert.
  199. </para>
  200. <programlisting language="php"><![CDATA[
  201. $feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
  202. $xpathPrefix = $feed->getXpathPrefix();
  203. $xpath = $feed->getXpath();
  204. $xpath->registerNamespace('admin', 'http://webns.net/mvcb/');
  205. $reportErrorsTo = $xpath->evaluate('string('
  206. . $xpathPrefix
  207. . '/admin:errorReportsTo)');
  208. ]]></programlisting>
  209. <warning>
  210. <para>
  211. Wenn man einen bereits registrierten Namespace mit einem anderen Präfix Namen
  212. registriert als jenen der von <classname>Zend_Feed_Reader</classname> intern
  213. verwendet wird, zerstört das die Interne Arbeitsweise dieser Komponente.
  214. </para>
  215. </warning>
  216. </sect2>
  217. <sect2 id="zend.feed.reader.cache-request">
  218. <title>Unterstützung für Caches und intelligente Anfragen</title>
  219. <sect3 id="zend.feed.reader.cache-request.cache">
  220. <title>Unterstützung für Caches in Zend_Feed_Reader hinzufügen</title>
  221. <para>
  222. <classname>Zend_Feed_Reader</classname> unterstützt die Verwendung einer Instanz von
  223. <classname>Zend_Cache</classname> um Feeds zu cachen (als <acronym>XML</acronym>)
  224. um unnötige Anfragen im Netzwerk zu vermeiden. Das Hinzufügen eines Caches ist hier
  225. so einfach wie bei anderen Zend Framework Komponenten. Den Cache erstellen und
  226. konfigurieren und dann <classname>Zend_Feed_Reader</classname> mitteilen das er
  227. verwendet werden soll! Der verwendete Cache Schlüssel ist
  228. "<classname>Zend_Feed_Reader_</classname>" gefolgt von dem
  229. <acronym>MD5</acronym> Hash der <acronym>URI</acronym> des Feeds.
  230. </para>
  231. <programlisting language="php"><![CDATA[
  232. $frontendOptions = array(
  233. 'lifetime' => 7200,
  234. 'automatic_serialization' => true
  235. );
  236. $backendOptions = array('cache_dir' => './tmp/');
  237. $cache = Zend_Cache::factory(
  238. 'Core', 'File', $frontendOptions, $backendOptions
  239. );
  240. Zend_Feed_Reader::setCache($cache);
  241. ]]></programlisting>
  242. <note>
  243. <para>
  244. Auch wenn es etwas abseits ist, sollte man daran denken zu
  245. <classname>Zend_Loader_PluginLoader</classname> einen Cache hinzuzufügen der
  246. von <classname>Zend_Feed_Reader</classname> verwendet wird um Erweiterungen zu
  247. laden.
  248. </para>
  249. </note>
  250. </sect3>
  251. <sect3 id="zend.feed.reader.cache-request.http-conditional-get">
  252. <title>Unterstützung für HTTP Conditional GET</title>
  253. <para>
  254. Die große Frage wenn man ofters einen Feed importiert, ist ob er sich geändert hat.
  255. Wenn ein Cache aktiviert ist, kann man die Unterstützung für <acronym>HTTP</acronym>
  256. Conditional GET hinzufügen um diese Frage zu beantworten.
  257. </para>
  258. <para>
  259. Durch Verwendung dieser Methode kann man Feeds von <acronym>URI</acronym> anfragen
  260. und deren letzte bekannte Werte der ETag und Last-Modified Antwort Header mit der
  261. Anfrage inkludieren (wobei die If-None-Match und If-Modified-Since Header verwendet
  262. werden). Wenn der Feed auf dem Server unverändert ist, sollte man eine 304 Antwort
  263. empfangen die <classname>Zend_Feed_Reader</classname> mitteilt das die gecachte
  264. Version zu verwenden ist. Wenn ein kompletter Feed in einer Antwort mit einem Status
  265. Code von 200 geschickt wird, bedeutet dieses, das der Feed verändert wurde und
  266. <classname>Zend_Feed_Reader</classname> wird die neue Version parsen und Sie im
  267. Cache abspeichern. Es werden auch die neuen Werte der ETag und Last-Modified Header
  268. für eine zukünftige Verwendung gespeichert.
  269. </para>
  270. <para>
  271. Bei diesen "konditionalen" Abfragen ist nicht garantiert das Sie, vom Server von dem
  272. man eine <acronym>URI</acronym> abfragt, unterstützt werden, können aber trotzdem
  273. angefragt werden. Die meisten Feed Quellen wie Blogs sollten hierfür eine
  274. Unterstützung haben. Um konditionale Anfragen zu erlauben, muss man einen Cache
  275. bei <classname>Zend_Feed_Reader</classname> angeben.
  276. </para>
  277. <programlisting language="php"><![CDATA[
  278. $frontendOptions = array(
  279. 'lifetime' => 86400,
  280. 'automatic_serialization' => true
  281. );
  282. $backendOptions = array('cache_dir' => './tmp/');
  283. $cache = Zend_Cache::factory(
  284. 'Core', 'File', $frontendOptions, $backendOptions
  285. );
  286. Zend_Feed_Reader::setCache($cache);
  287. Zend_Feed_Reader::useHttpConditionalGet();
  288. $feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
  289. ]]></programlisting>
  290. <para>
  291. Im obige Beispiel werden, bei aktivierten <acronym>HTTP</acronym> Conditional GET
  292. Anfragen, die Werte der Antwort Header für ETag und Last-Modified mit dem Feed
  293. gecacht. Für die nächsten 24 Stunden (die Lebenszeit des Caches) werden Feed am
  294. Cache nur dann aktualisiert wenn eine nicht-304 Antwort empfangen wird, die ein
  295. gültiges <acronym>RSS</acronym> oder Atom <acronym>XML</acronym> Dokument enthält.
  296. </para>
  297. <para>
  298. Wenn man darauf anzielt die Antwort Header ausserhalb von
  299. <classname>Zend_Feed_Reader</classname> zu managen, kann man die relevanten
  300. If-None-Matches und If-Modified-Since Antwort Header über die <acronym>URI</acronym>
  301. Import Methode setzen.
  302. </para>
  303. <programlisting language="php"><![CDATA[
  304. $lastEtagReceived = '5e6cefe7df5a7e95c8b1ba1a2ccaff3d';
  305. $lastModifiedDateReceived = 'Wed, 08 Jul 2009 13:37:22 GMT';
  306. $feed = Zend_Feed_Reader::import(
  307. $uri, $lastEtagReceived, $lastModifiedDateReceived
  308. );
  309. ]]></programlisting>
  310. </sect3>
  311. </sect2>
  312. <sect2 id="zend.feed.reader.locate">
  313. <title>Feed URIs von Webseiten erkennen</title>
  314. <para>
  315. Dieser Tage ist vielen Webseiten bekannt das der Ort Ihrer <acronym>XML</acronym> Feeds
  316. nicht immer eindeutig ist. Eine kleine <acronym>RDF</acronym>, <acronym>RSS</acronym>
  317. oder Atom Grafik hilft wenn der Benutzer die Seite liest, aber was wenn eine Maschine
  318. kommt und versucht herauszufinden So die Feed sind? Um hierbei zu helfen, zeigen viele
  319. Webseiten zu Ihren Feeds indem &lt;link&gt; Tags in der &lt;head&gt; Sektion Ihres
  320. <acronym>HTML</acronym>s verwendet werden. Um diesen Vorteil zu nutzen, kann man
  321. <classname>Zend_Feed_Reader</classname> verwenden diese Feeds zu erkennen, indem die
  322. statische <methodname>findFeedLinks()</methodname> Methode verwendet wird.
  323. </para>
  324. <para>
  325. Diese Methode ruft irgendeine <acronym>URI</acronym> auf und sucht nach dem Ort der
  326. <acronym>RSS</acronym>, <acronym>RDF</acronym> und Atom Feeds mit der Annahme dass das
  327. <acronym>HTML</acronym> der Webseite nur die relevanten Links enthält. Sie gibt dann ein
  328. Wert Objekt zurück indem man die Existenz einer <acronym>RSS</acronym>,
  329. <acronym>RDF</acronym> oder Atom Feed <acronym>URI</acronym> prüfen kann.
  330. </para>
  331. <programlisting language="php"><![CDATA[
  332. $links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');
  333. if(isset($links->rdf)) {
  334. echo $links->rdf, "\n"; // http://www.planet-php.org/rdf/
  335. }
  336. if(isset($links->rss)) {
  337. echo $links->rss, "\n"; // http://www.planet-php.org/rss/
  338. }
  339. if(isset($links->atom)) {
  340. echo $links->atom, "\n"; // http://www.planet-php.org/atom/
  341. }
  342. ]]></programlisting>
  343. <para>
  344. Basierend auf diesen Links kann man dann, von welchen Quellen man auch immer will,
  345. importieren indem die übliche Vorgehensweise verwendet wird.
  346. </para>
  347. </sect2>
  348. <sect2 id="zend.feed.reader.retrieve-info">
  349. <title>Empfangen von Feed Informationen</title>
  350. <para>
  351. Das Empfangen von Informationen von einem Feed (wir reden über Einträge und Elemente in
  352. der nächsten Sektion da Sie identischen Prinzipien folgen) verwendet eine klar
  353. definierte <acronym>API</acronym> welche exakt die gleiche ist, unabhängig davon ob der
  354. angefragte Feed <acronym>RSS</acronym>/<acronym>RDF</acronym>/Atom ist. Das selbe gilt
  355. für Sub-Versionen dieser Standards da wir jede einzelne <acronym>RSS</acronym> und Atom
  356. Version getestet haben. Wärend sich der darunterliegende <acronym>XML</acronym> Feed
  357. substantiell unterscheiden kann, im Sinne von Tags und Elementen die vorhanden sind,
  358. versuchen trotzdem alle ähnliche Informationen zu geben und um das alles zu reflektieren
  359. werden unterschiede und das Hanteln durch alternative Tags intern von
  360. <classname>Zend_Feed_Reader</classname> behandelt welche einem ein identisches Interface
  361. für jeden anzeigt. Idealerweise sollte man sich nicht darum kümmern ob ein Feed
  362. <acronym>RSS</acronym> oder Atom ist, solange man die Informationen extrahieren kann
  363. die man benötigt.
  364. </para>
  365. <para>
  366. Natürlich leben wir nicht in einer idealen Welt sodas es Zeiten gibt in denen die
  367. <acronym>API</acronym> einfach nicht das bietet wonach man sucht. Um hierbei zu helfen
  368. bietet <classname>Zend_Feed_Reader</classname> ein Plugin System an das es erlaubt
  369. Erweiterungen zu schreiben und die Kern <acronym>API</acronym> zu erweitern sowie
  370. alle zusätzliche Daten abzudecken die man von Feeds extrahieren will. Wenn das schreiben
  371. einer weiteren Erweiterung zu problematisch ist, kann man einfach das darunterliegende
  372. <acronym>DOM</acronym> oder die XPath Objekte holen und das von Hand in der Anwendung
  373. machen. Natürlich sollte wirklich eine Erweiterung geschrieben werden, einfach um es
  374. portabler und wiederverwendbarer zu machen.
  375. </para>
  376. <para>
  377. Hier ist eine Zusammenfassung der Kern <acronym>API</acronym> für Feeds. Man sollte
  378. beachten das es nicht nur die grundsätzlichen <acronym>RSS</acronym> und Atom
  379. Standard abdeckt, sondern das es auch eine Anzahl von mitgelieferten Erweiterungen
  380. gibt die mit <classname>Zend_Feed_Reader</classname> gebündelt sind. Die Benennung
  381. dieser Methoden von Erweiterungen ist recht generisch - alle erweiterten Methoden
  382. arbeiten auf dem gleichen Level wie die Kern <acronym>API</acronym> da wir es
  383. erlauben alle spefizischen Erweiterungs Objekte separat zu empfangen wenn das
  384. notwendig ist.
  385. </para>
  386. <table>
  387. <title>API Methoden auf dem Level des Feeds</title>
  388. <tgroup cols="2">
  389. <tbody>
  390. <row>
  391. <entry><methodname>getId()</methodname></entry>
  392. <entry>
  393. Gibt eine eindeutige ID zurück die mit dem Feed assoziiert ist
  394. </entry>
  395. </row>
  396. <row>
  397. <entry><methodname>getTitle()</methodname></entry>
  398. <entry>Gibt den Titel des Feeds zurück</entry>
  399. </row>
  400. <row>
  401. <entry><methodname>getDescription()</methodname></entry>
  402. <entry>Gibt die textuelle Beschreibung des Feeds zurück</entry>
  403. </row>
  404. <row>
  405. <entry><methodname>getLink()</methodname></entry>
  406. <entry>
  407. Gibt eine <acronym>URI</acronym> zu der <acronym>HTML</acronym> Webseite
  408. zurück welche die gleiche oder ähnliche Informationen wie dieser Feed
  409. enthält (z.B. wenn der Feed von einem Blog ist, sollte die
  410. <acronym>URI</acronym> des Blogs enthalten sein indem die
  411. <acronym>HTML</acronym> Version des Eintrags gelesen werden kann)
  412. </entry>
  413. </row>
  414. <row>
  415. <entry><methodname>getFeedLink()</methodname></entry>
  416. <entry>
  417. Gibt die <acronym>URI</acronym> dieses Feeds zurück, welche die gleiche
  418. sein sollte wie die <acronym>URI</acronym> welche verwendet wurde um
  419. den Feed zu importieren
  420. </entry>
  421. </row>
  422. <row>
  423. <entry><methodname>getAuthors()</methodname></entry>
  424. <entry>
  425. Gibt ein Array aller Authoren zurück die mit diesem Feed assoziiert
  426. sind, inklusive Email Adressen im String des Authors wenn vorhanden
  427. </entry>
  428. </row>
  429. <row>
  430. <entry><methodname>getAuthor(integer $index = 0)</methodname></entry>
  431. <entry>
  432. Gibt entweder den ersten bekannten Author zurück, oder mit dem
  433. optionalen Parameter <varname>$index</varname> jeden spezifischen
  434. Index des Arrays von Authoren (gibt null bei einem ungültigen Index
  435. zurück).
  436. </entry>
  437. </row>
  438. <row>
  439. <entry><methodname>getDateCreated()</methodname></entry>
  440. <entry>
  441. Gibt das Datum zurück zu dem dieser Feed erstellt wurde. Generell nur
  442. anwendbar bei Atom da es das Datum repräsentiert zu der das Atom 1.0
  443. Dokument erstellt wurde das die Ressource beschreibt.
  444. </entry>
  445. </row>
  446. <row>
  447. <entry><methodname>getDateModified()</methodname></entry>
  448. <entry>
  449. Gibt das Datum zurück zu dem der Feed das letzte mal geändert wurde
  450. </entry>
  451. </row>
  452. <row>
  453. <entry><methodname>getLanguage()</methodname></entry>
  454. <entry>
  455. Gibt die Sprache des Feeds zurüc (wenn definiert) oder einfach die
  456. Sprache die im <acronym>XML</acronym> Dokument notiert wurde
  457. </entry>
  458. </row>
  459. <row>
  460. <entry><methodname>getGenerator()</methodname></entry>
  461. <entry>
  462. Gibt den Erzeuger des Feeds zurück, z.B. die Software die Ihm erzeugt
  463. hat. Das kann sich zwischen <acronym>RSS</acronym> und Atom
  464. unterscheiden, da Atom eine andere Schreibweise definiert.
  465. </entry>
  466. </row>
  467. <row>
  468. <entry><methodname>getCopyright()</methodname></entry>
  469. <entry>
  470. Gibt alle Copyright Notizen zurück die mit dem Feed assoziiert sind
  471. </entry>
  472. </row>
  473. </tbody>
  474. </tgroup>
  475. </table>
  476. <para>
  477. Angehend von der Vielzahl von Feeds in der Wildnis, werden einige dieser Methoden
  478. erwartungsgemäßg <constant>NULL</constant> zurückgeben, was anzeigt das die relevanten
  479. Informationen nicht gefunden wurden. Wo es möglich ist wird
  480. <classname>Zend_Feed_Reader</classname> wärend der Suche auf alternative Elemente
  481. zurück greifen. Zum Beispiel ist das Durchsuchen eines <acronym>RSS</acronym> Feeds
  482. nach einem Modifikations Datum komplizierter als es aussieht. <acronym>RSS</acronym>
  483. Feeds sollten ein <code>&lt;lastBuildDate&gt;</code> Tag und/oder ein
  484. <code>&lt;pubDate&gt;</code> Element enthalten. Aber was wenn Sie es nicht tun, weil es
  485. z.B. ein <acronym>RSS</acronym> 1.0 Feed ist? Vielleicht ist stattdessen ein
  486. <code>&lt;atom:updated&gt;</code> Element mit identischen Informationen vorhanden (Atom
  487. kann verwendet werden um die <acronym>RSS</acronym> Syntax anzubieten)? Bei einem
  488. Fehlschlag können wir einfach auf die Einträge sehen, den aktuellsten herausholen, und
  489. sein <code>&lt;pubDate&gt;</code> Element verwenden. In der Annahme das es existiert...
  490. viele Feeds verwenden auch Dublin Core 1.0/1.1 <code>&lt;dc:date&gt;</code> Elemente für
  491. Feeds und Einträge. Oder wir können wieder ein Atom finden das herumliegt.
  492. </para>
  493. <para>
  494. Der Punkt ist, das <classname>Zend_Feed_Reader</classname> entwickelt wurde um das zu
  495. wissen. Wenn man nach dem Änderungsdatum fragt (oder irgendwas anderes), wird er
  496. starten und alle diese Alternativen suchen bis er entweder aufgibt und
  497. <constant>NULL</constant> zurückgibt, oder eine Alternative findet welche die richtige
  498. Antwort hat.
  499. </para>
  500. <para>
  501. Zusätzlich zu den obigen Methoden, implementieren alle Feed Objekte Methoden für das
  502. empfangen der <acronym>DOM</acronym> und XPath Objekte für die aktuellen Feeds wie
  503. vorher beschrieben. Feed Objekte implementieren auch die Interfaces für
  504. <acronym>SPL</acronym> Iterator und Countable. Die erweiterte <acronym>API</acronym>
  505. wird anbei zusammengefasst.
  506. </para>
  507. <table>
  508. <title>Erweiterte API Methoden auf Level des Feeds</title>
  509. <tgroup cols="2">
  510. <tbody>
  511. <row>
  512. <entry><methodname>getDomDocument()</methodname></entry>
  513. <entry>
  514. Gibt das elterliche <classname>DOMDocument</classname> Objekt für das
  515. komplette <acronym>XML</acronym> Quelldokument zurück
  516. </entry>
  517. </row>
  518. <row>
  519. <entry><methodname>getElement()</methodname></entry>
  520. <entry>
  521. Gibt das aktuelle <classname>DOMElement</classname> Objekt des Feed
  522. Levels zurück
  523. </entry>
  524. </row>
  525. <row>
  526. <entry><methodname>saveXml()</methodname></entry>
  527. <entry>
  528. Gibt einen String zurück der ein <acronym>XML</acronym> Dokument
  529. zurück welches das komplette Feed Element enthält (das ist nicht das
  530. originale Dokument sondern eine nachgebaute Version)
  531. </entry>
  532. </row>
  533. <row>
  534. <entry><methodname>getXpath()</methodname></entry>
  535. <entry>
  536. Gibt das intern verwendete <classname>DOMXPath</classname> Objekt
  537. zurück mit dem Abfragen auf das <classname>DOMDocument</classname>
  538. Objekt durchgeführt werden (das enthält die Kern und Erweiterungs
  539. Namespaces die vor-registriert sind)
  540. </entry>
  541. </row>
  542. <row>
  543. <entry><methodname>getXpathPrefix()</methodname></entry>
  544. <entry>
  545. Gibt den gültigen <acronym>DOM</acronym> Pfad Präfix zurück der bei
  546. allen XPath Abfragen passt die dem Feed entsprechen der abgefragt wird.
  547. </entry>
  548. </row>
  549. <row>
  550. <entry><methodname>getEncoding()</methodname></entry>
  551. <entry>
  552. Gibt die Kodierung des <acronym>XML</acronym> Quelldokuments zurück
  553. (Beachte: Das kann nicht verwendet werden für Fehler wie einen
  554. Server der Dokumente in einer anderen Kodierung verschickt)
  555. </entry>
  556. </row>
  557. <row>
  558. <entry><methodname>count()</methodname></entry>
  559. <entry>
  560. Gibt eine Zahl von Einträgen oder Elementen zurück welche dieser Feed
  561. enthält (implementiert das <acronym>SPL</acronym> Interface
  562. <classname>Countable</classname>)
  563. </entry>
  564. </row>
  565. <row>
  566. <entry><methodname>current()</methodname></entry>
  567. <entry>
  568. Gibt nur den aktuellen Eintrag zurück (verwendet den aktuellen
  569. Index von <methodname>key()</methodname>)
  570. </entry>
  571. </row>
  572. <row>
  573. <entry><methodname>key()</methodname></entry>
  574. <entry>Gibt den aktuellen Index für Einträge zurück</entry>
  575. </row>
  576. <row>
  577. <entry><methodname>next()</methodname></entry>
  578. <entry>Addiert den Wert des Index für Einträge um Eins</entry>
  579. </row>
  580. <row>
  581. <entry><methodname>rewind()</methodname></entry>
  582. <entry>Resetiert den Index für Einträge auf 0</entry>
  583. </row>
  584. <row>
  585. <entry><methodname>valid()</methodname></entry>
  586. <entry>
  587. Prüft pb der aktuelle Index für Einträge gültig ist, z.B. ob er nicht
  588. unter 0 fällt und die Anzahl der existierenden Einträge nicht
  589. übersteigt.
  590. </entry>
  591. </row>
  592. <row>
  593. <entry><methodname>getExtensions()</methodname></entry>
  594. <entry>
  595. Gibt ein Array aller Erweiterungs Objekte zurück die für den aktuellen
  596. Feed geladen sind (Beachte: sowohl Feel-Level als auch Element-Level
  597. Erweiterungen exstieren, aber nur Feed-Level Erweiterungen werden hier
  598. zurückgegeben). Die Array Schlüssel sind in der Form
  599. (ErweiterungsName)_Feed.
  600. </entry>
  601. </row>
  602. <row>
  603. <entry><methodname>getExtension(string $name)</methodname></entry>
  604. <entry>
  605. Gibt ein Erweiterungs Objekt für den Feed zurück der unter dem
  606. angegebenen Namen registriert ist. Das erlaubt einen feiner
  607. gestaffelten Zugriff auf Erweiterungen welche andernfalls in der
  608. Implementation der standardmäßigen <acronym>API</acronym> Methoden
  609. versteckt wären.
  610. </entry>
  611. </row>
  612. <row>
  613. <entry><methodname>getType()</methodname></entry>
  614. <entry>
  615. Gibt eine statische Klassenkonstante zurück (z.B.
  616. <constant>Zend_Feed_Reader::TYPE_ATOM_03</constant>, z.B. Atom 0.3)
  617. welche exakt anzeigt welche Art von Feed gerade konsumiert wird.
  618. </entry>
  619. </row>
  620. </tbody>
  621. </tgroup>
  622. </table>
  623. </sect2>
  624. <sect2 id="zend.feed.reader.entry">
  625. <title>Empfangen von Informationen aus Einträgen/Elementen</title>
  626. <para>
  627. Das Empfangen von Informationen für spezifische Einträge oder Elemente (abhängig davon
  628. ob man Atom oder <acronym>RSS</acronym> spricht) ist identisch wie bei den Daten auf
  629. Feed Level. Der Zugriff auf Einträge ist einfach ein Fall von Iteration über ein Feed
  630. Objekt oder durch Verwendung des <acronym>SPL</acronym> Interfaces
  631. <classname>Iterator</classname> welches Feed Objekte implementieren und durch
  632. Aufruf der betreffenden Methoden auf Ihnen.
  633. </para>
  634. <table>
  635. <title>API Methoden auf Level des Eintrags</title>
  636. <tgroup cols="2">
  637. <tbody>
  638. <row>
  639. <entry><methodname>getId()</methodname></entry>
  640. <entry>Gibt eine eindeutige ID für den aktuellen Eintrag zurück</entry>
  641. </row>
  642. <row>
  643. <entry><methodname>getTitle()</methodname></entry>
  644. <entry>Gibt den Titel des aktuellen Eintrags zurück</entry>
  645. </row>
  646. <row>
  647. <entry><methodname>getDescription()</methodname></entry>
  648. <entry>Gibt eine Beschreibung des aktuellen Eintrags zurück</entry>
  649. </row>
  650. <row>
  651. <entry><methodname>getLink()</methodname></entry>
  652. <entry>
  653. Gibt eine <acronym>URI</acronym> zur <acronym>HTML</acronym> Version des
  654. aktuellen Eintrags zurück
  655. </entry>
  656. </row>
  657. <row>
  658. <entry><methodname>getPermaLink()</methodname></entry>
  659. <entry>
  660. Gibt einen permanenten Link zum aktuellen Eintrag zurück
  661. </entry>
  662. </row>
  663. <row>
  664. <entry><methodname>getAuthors()</methodname></entry>
  665. <entry>
  666. Gibt ein Array aller Authoren zurück die mit diesem Eintrag assoziiert
  667. sind, inklusive Email Adresse wenn diese im String des Authors vorhanden
  668. ist
  669. </entry>
  670. </row>
  671. <row>
  672. <entry><methodname>getAuthor($index = 0)</methodname></entry>
  673. <entry>
  674. Gibt entweder den ersten bekannten Author zurück, oder mit dem
  675. optionalen Parameter <varname>$index</varname> jeden spezifischen Index
  676. aus dem Array der Authoren (gibt null zurück wenn der Index ungültig
  677. ist).
  678. </entry>
  679. </row>
  680. <row>
  681. <entry><methodname>getDateCreated()</methodname></entry>
  682. <entry>
  683. Gibt das Datum zurück an dem der aktuelle Eintrag erstellt wurde.
  684. Generell kann das nur auf Atom angewendet werden wo es das Datum der
  685. Ressource beschreibt zu welche das Atom 1.0 Dokument erstellt wurde.
  686. </entry>
  687. </row>
  688. <row>
  689. <entry><methodname>getDateModified()</methodname></entry>
  690. <entry>
  691. Gibt das Datum zurück an welchem der aktuelle Eintrag zuletzt geändert
  692. wurde.
  693. </entry>
  694. </row>
  695. <row>
  696. <entry><methodname>getContent()</methodname></entry>
  697. <entry>
  698. Gibt den Inhalt des aktuellen Eintrags zurück (das retourniert alle
  699. Entities wenn das möglich ist, mit der Annahme das der Content Type
  700. <acronym>HTML</acronym> ist). Die Beschreibung wird zurückgegeben wenn
  701. ein kein seperates Content Element existiert.
  702. </entry>
  703. </row>
  704. <row>
  705. <entry><methodname>getCommentCount()</methodname></entry>
  706. <entry>
  707. Gibt die Anzahl der Kommentare zurück die auf diesen Eintrag gemacht
  708. wurden seit der Zeit an welcher der Feed erstellt wurde
  709. </entry>
  710. </row>
  711. <row>
  712. <entry><methodname>getCommentLink()</methodname></entry>
  713. <entry>
  714. Gibt eine <acronym>URI</acronym> zurück welche auf die
  715. <acronym>HTML</acronym> Seite zeigt, auf der Kommentare zu diesem
  716. Eintrag gemacht werden können
  717. </entry>
  718. </row>
  719. <row>
  720. <entry>
  721. <methodname>getCommentFeedLink(string $type =
  722. 'atom'|'rss')</methodname>
  723. </entry>
  724. <entry>
  725. Gibt eine <acronym>URI</acronym> zurück die auf einen Feed zeigt welcher
  726. vom angegebenen Typ ist, und alle Kommentare für diesen Eintrag enthält
  727. (Der Typ ist standardmäßig Atom/<acronym>RSS</acronym> abhängig vom
  728. aktuellen Feed Typ).
  729. </entry>
  730. </row>
  731. </tbody>
  732. </tgroup>
  733. </table>
  734. <para>
  735. Die erweiterte <acronym>API</acronym> für Einträge ist identisch zu der für die Feed
  736. mit der Aufnahme der Iterator Methoden die hier nicht benötigt werden.
  737. </para>
  738. <caution>
  739. <para>
  740. Es gibt oft Missverständnisse über die Konzepte vom Zeitpunkt der Änderung und des
  741. Erstellungsdatums. In Atom, sind diese zwei klar definierte Konzepte aber in
  742. <acronym>RSS</acronym> sind Sie vage. <acronym>RSS</acronym> 2.0 definiert ein
  743. einzelnes <emphasis>&lt;pubDate&gt;</emphasis> Element das typischerweise auf das
  744. Datum referiert an dem dieser Eintrag veröffentlicht wurde, z.B. etwas in der Art
  745. eines Erstellungsdatums. Das ist nicht immer das gleiche, und kann sich durch
  746. Updates ändern oder auch nicht. Als Resultat sollte man sich, wenn man wirklich
  747. prüfen will ob der Eintrag geändert wurde oder nicht, nicht auf das Ergebnis von
  748. <methodname>getDateModified()</methodname> verlassen. Stattdessen sollte man
  749. Erwägen den <acronym>MD5</acronym> Hash von drei anderen verknpüften Elementen
  750. zu beobachten, z.B. durch Verwendung von <methodname>getTitle()</methodname>,
  751. <methodname>getDescription()</methodname> und
  752. <methodname>getContent()</methodname>. Wenn der Eintrag wirklich geändert wurde,
  753. gibt diese Hash Berechnung ein anderes Ergebnis als die vorher gespeicherten Hashs
  754. für den gleichen Eintrag. Weitere Schritte in diesen Wassern zeigen das die Daten
  755. von Feeds unterschiedlichen Standards folgen. Atom und Dublin Core Daten sollten
  756. <acronym>ISO</acronym> 86001 folgen und <acronym>RSS</acronym> Daten sollten
  757. <acronym>RFC</acronym> 822 oder <acronym>RFC</acronym> 2822 folgen welche auch
  758. üblicherweise verwendet werden. Datumsmethoden werfen eine Exception wenn
  759. <classname>Zend_Date</classname> das Datum durch Verwendung der obigen Standards
  760. nicht laden kann.
  761. </para>
  762. </caution>
  763. <warning>
  764. <para>
  765. Die Werte die von diesen Methoden zurückgegeben werden, sind nicht geprüft. Das
  766. bedeutet das der Benutzer Prüfungen auf allen empfangenen Daten durchführen muss
  767. inklusive filtern von jeglichem <acronym>HTML</acronym> wie von
  768. <methodname>getContent()</methodname> bevor es von der eigenen Anwendung ausgegeben
  769. wird. Es ist zu beachten das die meisten Feeds von externen Quellen kommen, und
  770. deshalb die normale Annahme sein sollte das man Ihnen nicht trauen kann.
  771. </para>
  772. </warning>
  773. <table>
  774. <title>Erweiterte API Methoden auf Level des Eintrags</title>
  775. <tgroup cols="2">
  776. <tbody>
  777. <row>
  778. <entry><methodname>getDomDocument()</methodname></entry>
  779. <entry>
  780. Gibt das elterliche <classname>DOMDocument</classname> Objekt für den
  781. kompletten Feed zurück (nicht nur den aktuellen Eintrag)
  782. </entry>
  783. </row>
  784. <row>
  785. <entry><methodname>getElement()</methodname></entry>
  786. <entry>
  787. Gibt das <classname>DOMDocument</classname> Objekt für den
  788. aktuellen Level des Eintrags zurück
  789. </entry>
  790. </row>
  791. <row>
  792. <entry><methodname>getXpath()</methodname></entry>
  793. <entry>
  794. Gibt das <classname>DOMXPath</classname> Objekt zurück welches
  795. intern verwendet wird um Abfragen auf dem
  796. <classname>DOMDocument</classname> Objekt durchzuführen (das enthält
  797. auch die vorregistrierten Kern und Erweiterungs Namespaces)
  798. </entry>
  799. </row>
  800. <row>
  801. <entry><methodname>getXpathPrefix()</methodname></entry>
  802. <entry>
  803. Gibt einen gültigen <acronym>DOM</acronym> Pfad Präfix zurück der allen
  804. XPath Abfrage vorangestellt wird, welche dem Eintrag entsprechen der
  805. abgefragt wird.
  806. </entry>
  807. </row>
  808. <row>
  809. <entry><methodname>getEncoding()</methodname></entry>
  810. <entry>
  811. Gibt die Kodierung des <acronym>XML</acronym> Quelldokuments zurück
  812. (Achtung: Das kann nicht für Fehler genommen werden bei denen der
  813. Server eine andere Kodierung sendet als die Dokumente)
  814. </entry>
  815. </row>
  816. <row>
  817. <entry><methodname>getExtensions()</methodname></entry>
  818. <entry>
  819. Gibt ein Array aller Erweiterungsobjekte zurück die für den aktuellen
  820. Eintrag geladen wurden (Achtung: Sowohl Erweiterung auf Level von Feeds
  821. als auch auf Level von Einträgen existieren, und nur Erweiterungen auf
  822. Level von Einträgen werden hier zurückgegeben). Die Arrayschlüssel sind
  823. im Format {ErweiterungsName}_Entry.
  824. </entry>
  825. </row>
  826. <row>
  827. <entry><methodname>getExtension(string $name)</methodname></entry>
  828. <entry>
  829. Gibt das Erweiterungsobjekt zurück für das der Eintrag mit dem
  830. angegebenen Namen registriert wurde. Das erlaubt einen feineren
  831. Zugriff auf Erweiterungen welche andernfalls innerhalb der
  832. Implementierung der standardmäßigen <acronym>API</acronym> Methoden
  833. versteckt wären.
  834. </entry>
  835. </row>
  836. <row>
  837. <entry><methodname>getType()</methodname></entry>
  838. <entry>
  839. Gibt eine statische Klassenkonstante zurück (z.B.
  840. <constant>Zend_Feed_Reader::TYPE_ATOM_03</constant>, z.B. Atom 0.3)
  841. die exakt anzeigt von welcher Art der Feed ist der gerade konsumiert
  842. wird.
  843. </entry>
  844. </row>
  845. </tbody>
  846. </tgroup>
  847. </table>
  848. </sect2>
  849. <sect2 id="zend.feed.reader.extending">
  850. <title>Erweitern der APIs für Feeds und Einträge</title>
  851. <para>
  852. Die Erweiterung von <classname>Zend_Feed_Reader</classname> erlaubt es Methoden sowohl
  853. auf Level von Feeds als auch auf Level von Einträgen hinzuzufügen, welche das Empfangen
  854. von Informationen abdecken die nicht bereits von <classname>Zend_Feed_Reader</classname>
  855. unterstützt werden. Bei der Anzahl an <acronym>RSS</acronym> und Atom Erweiterungen die
  856. existieren, ist das ein guter Weg da <classname>Zend_Feed_Reader</classname> einfach
  857. nicht alles hinzufügen kann.
  858. </para>
  859. <para>
  860. There are two types of Extensions possible, those which retrieve
  861. information from elements which are immediate children of the root
  862. element (e.g. <code>&lt;channel&gt;</code> for <acronym>RSS</acronym> or
  863. <code>&lt;feed&gt;</code> for Atom) and those who retrieve
  864. information from child elements of an entry (e.g.
  865. <code>&lt;item&gt;</code> for <acronym>RSS</acronym> or <code>&lt;entry&gt;</code> for
  866. Atom). On the filesystem these are grouped as classes within
  867. a namespace based on the extension standard's name. For
  868. example, internally we have
  869. <classname>Zend_Feed_Reader_Extension_DublinCore_Feed</classname>
  870. and <classname>Zend_Feed_Reader_Extension_DublinCore_Entry</classname>
  871. classes which are two Extensions implementing Dublin Core
  872. 1.0/1.1 support.
  873. </para>
  874. <para>
  875. Extensions are loaded into <classname>Zend_Feed_Reader</classname>
  876. using <classname>Zend_Loader_PluginLoader</classname>, so their operation
  877. will be familiar from other Zend Framework components.
  878. <classname>Zend_Feed_Reader</classname> already bundles a number of
  879. these Extensions, however those which are not used internally and
  880. registered by default (so called Core Extensions) must be registered
  881. to <classname>Zend_Feed_Reader</classname> before they are used. The
  882. bundled Extensions include:
  883. </para>
  884. <table>
  885. <title>Core Extensions (pre-registered)</title>
  886. <tgroup cols="2">
  887. <tbody>
  888. <row>
  889. <entry>DublinCore (Feed and Entry)</entry>
  890. <entry>Implements support for Dublin Core Metadata Element Set 1.0
  891. and 1.1 </entry>
  892. </row>
  893. <row>
  894. <entry>Content (Entry only)</entry>
  895. <entry>Implements support for Content 1.0</entry>
  896. </row>
  897. <row>
  898. <entry>Atom (Feed and Entry)</entry>
  899. <entry>Implements support for Atom 0.3 and Atom 1.0</entry>
  900. </row>
  901. <row>
  902. <entry>Slash</entry>
  903. <entry>Implements support for the Slash <acronym>RSS</acronym> 1.0 module</entry>
  904. </row>
  905. <row>
  906. <entry>WellFormedWeb</entry>
  907. <entry>Implements support for the Well Formed Web CommentAPI 1.0</entry>
  908. </row>
  909. <row>
  910. <entry>Thread</entry>
  911. <entry>Implements support for Atom Threading Extensions as described
  912. in <acronym>RFC</acronym> 4685</entry>
  913. </row>
  914. <row>
  915. <entry>Podcast</entry>
  916. <entry>Implements support for the Podcast 1.0 <acronym>DTD</acronym> from Apple</entry>
  917. </row>
  918. </tbody>
  919. </tgroup>
  920. </table>
  921. <para>
  922. The Core Extensions are somewhat special since they are extremely
  923. common and multi-faceted. For example, we have a Core Extension for Atom.
  924. Atom is implemented as an Extension (not just a base class) because it
  925. doubles as a valid <acronym>RSS</acronym> module - you can insert
  926. Atom elements into <acronym>RSS</acronym> feeds. I've even seen
  927. <acronym>RDF</acronym> feeds which use a lot of Atom in place of more
  928. common Extensions like Dublin Core.
  929. </para>
  930. <table>
  931. <title>Non-Core Extensions (must register manually)</title>
  932. <tgroup cols="2">
  933. <tbody>
  934. <row>
  935. <entry>Syndication</entry>
  936. <entry>Implements Syndication 1.0 support for <acronym>RSS</acronym> feeds</entry>
  937. </row>
  938. <row>
  939. <entry>CreativeCommons</entry>
  940. <entry>A <acronym>RSS</acronym> module that adds an element at the &lt;channel&gt;
  941. or &lt;item&gt; level that specifies which Creative Commons license
  942. applies.</entry>
  943. </row>
  944. </tbody>
  945. </tgroup>
  946. </table>
  947. <para>
  948. The additional non-Core Extensions are offered but not registered to
  949. <classname>Zend_Feed_Reader</classname> by default. If you want to
  950. use them, you'll need to tell
  951. <classname>Zend_Feed_Reader</classname> to load them in advance of
  952. importing a feed. Additional non-Core Extensions will be included
  953. in future iterations of the component.
  954. </para>
  955. <para>
  956. Registering an Extension with
  957. <classname>Zend_Feed_Reader</classname>, so it is loaded and its <acronym>API</acronym>
  958. is available to Feed and Entry objects, is a simple affair using the
  959. <classname>Zend_Loader_PluginLoader</classname>. Here we register
  960. the optional Slash Extension, and discover that it can be directly
  961. called from the Entry level <acronym>API</acronym> without any effort. Note that
  962. Extension names are case sensitive and use camel casing for multiple
  963. terms.
  964. </para>
  965. <programlisting language="php"><![CDATA[
  966. Zend_Feed_Reader::registerExtension('Syndication');
  967. $feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
  968. $updatePeriod = $feed->current()->getUpdatePeriod();
  969. ]]></programlisting>
  970. <para>
  971. In the simple example above, we checked how frequently a feed is being updated
  972. using the <methodname>getUpdatePeriod()</methodname>
  973. method. Since it's not part of
  974. <classname>Zend_Feed_Reader</classname>'s core <acronym>API</acronym>, it could only be
  975. a method supported by the newly registered Syndication Extension.
  976. </para>
  977. <para>
  978. As you can also notice, the new methods from Extensions are accessible from the main
  979. <acronym>API</acronym> using <acronym>PHP</acronym>'s magic methods. As an alternative,
  980. you can also directly access any Extension object for a similar result as seen below.
  981. </para>
  982. <programlisting language="php"><![CDATA[
  983. Zend_Feed_Reader::registerExtension('Syndication');
  984. $feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
  985. $syndication = $feed->getExtension('Syndication');
  986. $updatePeriod = $syndication->getUpdatePeriod();
  987. ]]></programlisting>
  988. <sect3 id="zend.feed.reader.extending.feed">
  989. <title>Writing Zend_Feed_Reader Extensions</title>
  990. <para>
  991. Inevitably, there will be times when the
  992. <classname>Zend_Feed_Reader</classname> <acronym>API</acronym> is just not capable
  993. of getting something you need from a feed or entry. You can use
  994. the underlying source objects, like
  995. <classname>DOMDocument</classname>, to get these by hand however
  996. there is a more reusable method available by writing Extensions
  997. supporting these new queries.
  998. </para>
  999. <para>
  1000. As an example, let's take the case of a purely fictitious
  1001. corporation named Jungle Books. Jungle Books have been
  1002. publishing a lot of reviews on books they sell (from external
  1003. sources and customers), which are distributed as an <acronym>RSS</acronym> 2.0
  1004. feed. Their marketing department realises that web applications
  1005. using this feed cannot currently figure out exactly what book is
  1006. being reviewed. To make life easier for everyone, they determine
  1007. that the geek department needs to extend <acronym>RSS</acronym> 2.0 to include a
  1008. new element per entry supplying the <acronym>ISBN</acronym>-10 or
  1009. <acronym>ISBN</acronym>-13 number of
  1010. the publication the entry concerns. They define the new
  1011. <code>&lt;isbn&gt;</code> element quite simply with a standard
  1012. name and namespace <acronym>URI</acronym>:
  1013. </para>
  1014. <programlisting language="php"><![CDATA[
  1015. JungleBooks 1.0:
  1016. http://example.com/junglebooks/rss/module/1.0/
  1017. ]]></programlisting>
  1018. <para>
  1019. A snippet of <acronym>RSS</acronym> containing this extension in practice could be
  1020. something similar to:
  1021. </para>
  1022. <programlisting language="php"><![CDATA[
  1023. <?xml version="1.0" encoding="utf-8" ?>
  1024. <rss version="2.0"
  1025. xmlns:content="http://purl.org/rss/1.0/modules/content/"
  1026. xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/">
  1027. <channel>
  1028. <title>Jungle Books Customer Reviews</title>
  1029. <link>http://example.com/junglebooks</link>
  1030. <description>Many book reviews!</description>
  1031. <pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate>
  1032. <jungle:dayPopular>http://example.com/junglebooks/book/938</jungle:dayPopular>
  1033. <item>
  1034. <title>Review Of Flatland: A Romance of Many Dimensions</title>
  1035. <link>http://example.com/junglebooks/review/987</link>
  1036. <author>Confused Physics Student</author>
  1037. <content:encoded>
  1038. A romantic square?!
  1039. </content:encoded>
  1040. <pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate>
  1041. <jungle:isbn>048627263X</jungle:isbn>
  1042. </item>
  1043. </channel>
  1044. </rss>
  1045. ]]></programlisting>
  1046. <para>
  1047. Implementing this new <acronym>ISBN</acronym> element as a simple entry level
  1048. extension would require the following class (using your own class
  1049. namespace outside of Zend).
  1050. </para>
  1051. <programlisting language="php"><![CDATA[
  1052. class My_FeedReader_Extension_JungleBooks_Entry
  1053. extends Zend_Feed_Reader_Extension_EntryAbstract
  1054. {
  1055. public function getIsbn()
  1056. {
  1057. if (isset($this->_data['isbn'])) {
  1058. return $this->_data['isbn'];
  1059. }
  1060. $isbn = $this->_xpath->evaluate(
  1061. 'string(' . $this->getXpathPrefix() . '/jungle:isbn)'
  1062. );
  1063. if (!$isbn) {
  1064. $isbn = null;
  1065. }
  1066. $this->_data['isbn'] = $isbn;
  1067. return $this->_data['isbn'];
  1068. }
  1069. protected function _registerNamespaces()
  1070. {
  1071. $this->_xpath->registerNamespace(
  1072. 'jungle', 'http://example.com/junglebooks/rss/module/1.0/'
  1073. );
  1074. }
  1075. }
  1076. ]]></programlisting>
  1077. <para>
  1078. This extension is easy enough to follow. It creates a new method
  1079. <methodname>getIsbn()</methodname> which runs an XPath query on
  1080. the current entry to extract the <acronym>ISBN</acronym> number enclosed by the
  1081. <code>&lt;jungle:isbn&gt;</code> element. It can optionally
  1082. store this to the internal non-persistent cache (no need to keep
  1083. querying the <acronym>DOM</acronym> if it's called again on the same entry). The
  1084. value is returned to the caller. At the end we have a protected
  1085. method (it's abstract so it must exist) which registers the
  1086. Jungle Books namespace for their custom <acronym>RSS</acronym> module. While we
  1087. call this an <acronym>RSS</acronym> module, there's nothing to prevent the same
  1088. element being used in Atom feeds - and all Extensions which use
  1089. the prefix provided by <methodname>getXpathPrefix()</methodname>
  1090. are actually neutral and work on <acronym>RSS</acronym> or Atom feeds with no
  1091. extra code.
  1092. </para>
  1093. <para>
  1094. Since this Extension is stored outside of Zend Framework, you'll
  1095. need to register the path prefix for your Extensions so
  1096. <classname>Zend_Loader_PluginLoader</classname> can find them.
  1097. After that, it's merely a matter of registering the Extension,
  1098. if it's not already loaded, and using it in practice.
  1099. </para>
  1100. <programlisting language="php"><![CDATA[
  1101. if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
  1102. Zend_Feed_Reader::addPrefixPath(
  1103. '/path/to/My/FeedReader/Extension', 'My_FeedReader_Extension'
  1104. );
  1105. Zend_Feed_Reader::registerExtension('JungleBooks');
  1106. }
  1107. $feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
  1108. // ISBN for whatever book the first entry in the feed was concerned with
  1109. $firstIsbn = $feed->current()->getIsbn();
  1110. ]]></programlisting>
  1111. <para>
  1112. Writing a feed level Extension is not much different. The
  1113. example feed from earlier included an unmentioned
  1114. <code>&lt;jungle:dayPopular&gt;</code> element which Jungle
  1115. Books have added to their standard to include a link to the
  1116. day's most popular book (in terms of visitor traffic). Here's
  1117. an Extension which adds a
  1118. <methodname>getDaysPopularBookLink()</methodname> method to the
  1119. feel level <acronym>API</acronym>.
  1120. </para>
  1121. <programlisting language="php"><![CDATA[
  1122. class My_FeedReader_Extension_JungleBooks_Feed
  1123. extends Zend_Feed_Reader_Extension_FeedAbstract
  1124. {
  1125. public function getDaysPopularBookLink()
  1126. {
  1127. if (isset($this->_data['dayPopular'])) {
  1128. return $this->_data['dayPopular'];
  1129. }
  1130. $dayPopular = $this->_xpath->evaluate(
  1131. 'string(' . $this->getXpathPrefix() . '/jungle:dayPopular)'
  1132. );
  1133. if (!$dayPopular) {
  1134. $dayPopular = null;
  1135. }
  1136. $this->_data['dayPopular'] = $dayPopular;
  1137. return $this->_data['dayPopular'];
  1138. }
  1139. protected function _registerNamespaces()
  1140. {
  1141. $this->_xpath->registerNamespace(
  1142. 'jungle', 'http://example.com/junglebooks/rss/module/1.0/'
  1143. );
  1144. }
  1145. }
  1146. ]]></programlisting>
  1147. <para>
  1148. Let's repeat the last example using a custom Extension to show the
  1149. method being used.
  1150. </para>
  1151. <programlisting language="php"><![CDATA[
  1152. if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
  1153. Zend_Feed_Reader::addPrefixPath(
  1154. '/path/to/My/FeedReader/Extension', 'My_FeedReader_Extension'
  1155. );
  1156. Zend_Feed_Reader::registerExtension('JungleBooks');
  1157. }
  1158. $feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
  1159. // URI to the information page of the day's most popular book with visitors
  1160. $daysPopularBookLink = $feed->getDaysPopularBookLink();
  1161. // ISBN for whatever book the first entry in the feed was concerned with
  1162. $firstIsbn = $feed->current()->getIsbn();
  1163. ]]></programlisting>
  1164. <para>
  1165. Going through these examples, you'll note that we don't register
  1166. feed and entry Extensions separately. Extensions within the same
  1167. standard may or may not include both a feed and entry class, so
  1168. <classname>Zend_Feed_Reader</classname> only requires you to
  1169. register the overall parent name, e.g. JungleBooks, DublinCore,
  1170. Slash. Internally, it can check at what level Extensions exist
  1171. and load them up if found. In our case, we have a full set of
  1172. Extensions now: <classname>JungleBooks_Feed</classname> and
  1173. <classname>JungleBooks_Entry</classname>.
  1174. </para>
  1175. </sect3>
  1176. </sect2>
  1177. </sect1>