1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.feed.writer">
4 <title>Zend_Feed_Writer</title>
6 <sect2 id="zend.feed.writer.introduction">
7 <title>Introduction</title>
10 <classname>Zend_Feed_Writer</classname> is the sibling component to
11 <classname>Zend_Feed_Reader</classname> responsible for generating feeds for output. It
12 supports the Atom 1.0 specification (RFC 4287) and <acronym>RSS</acronym> 2.0 as
13 specified by the <acronym>RSS</acronym> Advisory Board (<acronym>RSS</acronym> 2.0.11).
14 It does not deviate from these standards. It does, however, offer a simple Extension
15 system which allows for any extension and module for either of these two specifications
16 to be implemented if they are not provided out of the box.
20 In many ways, <classname>Zend_Feed_Writer</classname> is the inverse of
21 <classname>Zend_Feed_Reader</classname>. Where <classname>Zend_Feed_Reader</classname>
22 focuses on providing an easy to use architecture fronted by getter methods,
23 <classname>Zend_Feed_Writer</classname> is fronted by similarly named setters or
24 mutators. This ensures the <acronym>API</acronym> won't pose a learning curve to anyone
25 familiar with <classname>Zend_Feed_Reader</classname>.
29 As a result of this design, the rest may even be obvious. Behind the scenes, data set on
30 any <classname>Zend_Feed_Writer</classname> Data Container object is translated at
31 render time onto a DOMDocument object using the necessary feed elements. For each
32 supported feed type there is both an Atom 1.0 and <acronym>RSS</acronym> 2.0 renderer.
33 Using a DOMDocument class rather than a templating solution has numerous advantages,
34 the most obvious being the ability to export the DOMDocument for
35 additional processing and relying on <acronym>PHP</acronym> <acronym>DOM</acronym> for
36 correct and valid rendering.
40 As with <classname>Zend_Feed_Reader</classname>, <classname>Zend_Feed_Writer</classname>
41 is a standalone replacement for <classname>Zend_Feed</classname>'s Builder architecture
42 and is not compatible with those classes.
46 <sect2 id="zend.feed.writer.architecture">
47 <title>Architecture</title>
50 The architecture of <classname>Zend_Feed_Writer</classname> is very simple. It has two
51 core sets of classes: data containers and renderers.
55 The containers include the <classname>Zend_Feed_Writer_Feed</classname> and
56 <classname>Zend_Feed_Writer_Entry</classname> classes. The Entry classes can be attached
57 to any Feed class. The sole purpose of these containers is to collect data about the
58 feed to generate using a simple interface of setter methods. These methods perform some
59 data validity testing. For example, it will validate any passed <acronym>URI</acronym>s,
60 dates, etc. These checks are not tied to any of the feed standards definitions. The
61 container objects also contain methods to allow for fast rendering and export of the
62 final feed, and these can be reused at will.
66 In addition to the main data container classes, there are two additional Atom 2.0
67 specific classes. <classname>Zend_Feed_Writer_Source</classname> and
68 <classname>Zend_Feed_Writer_Deleted</classname>. The former implements Atom 2.0 source
69 elements which carry source feed metadata for a specific entry within an aggregate feed
70 (i.e. the current feed is not the entry's original source). The latter implements the
71 Atom Tombstones RFC allowing feeds to carry references to entries which have been
76 While there are two main data container types, there are four renderers - two matching
77 container renderers per supported feed type. Each renderer accepts a container, and
78 based on its content attempts to generate valid feed markup. If the renderer is unable
79 to generate valid feed markup, perhaps due to the container missing an obligatory data
80 point, it will report this by throwing an <classname>Exception</classname>. While it is
81 possible to ignore <classname>Exception</classname>s, this removes the default safeguard
82 of ensuring you have sufficient data set to render a wholly valid feed.
86 To explain this more clearly, you may construct a set of data containers for a feed
87 where there is a Feed container, into which has been added some Entry containers and a
88 Deleted container. This forms a data hierarchy resembling a normal feed. When rendering
89 is performed, this hierarchy has its pieces passed to relevant renderers and the partial
90 feeds (all DOMDocuments) are then pieced together to create a complete feed. In the case
91 of Source or Deleted (Tomestone) containers, these are rendered only for Atom 2.0 and
96 Due to the system being divided between data containers and renderers, it can make
97 Extensions somewhat interesting. A typical Extension offering namespaced feed and entry
98 level elements, must itself reflect the exact same architecture, i.e. offer feed and
99 entry level data containers, and matching renderers. There is, fortunately, no complex
100 integration work required since all Extension classes are simply registered and
101 automatically used by the core classes. We'll meet Extensions in more detail at the end
106 <sect2 id="zend.feed.writer.getting.started">
107 <title>Getting Started</title>
110 Using <classname>Zend_Feed_Writer</classname> is as simple as setting data and
111 triggering the renderer. Here is an example to generate a minimal Atom 1.0 feed.
112 As this demonstrates, each feed/entry uses a separate data container.
115 <programlisting language="php"><![CDATA[
117 * Create the parent feed
119 $feed = new Zend_Feed_Writer_Feed;
120 $feed->setTitle('Paddy\'s Blog');
121 $feed->setLink('http://www.example.com');
122 $feed->setFeedLink('http://www.example.com/atom', 'atom');
123 $feed->addAuthor(array(
125 'email' => 'paddy@example.com',
126 'uri' => 'http://www.example.com',
128 $feed->setDateModified(time());
129 $feed->addHub('http://pubsubhubbub.appspot.com/');
132 * Add one or more entries. Note that entries must
133 * be manually added once created.
135 $entry = $feed->createEntry();
136 $entry->setTitle('All Your Base Are Belong To Us');
137 $entry->setLink('http://www.example.com/all-your-base-are-belong-to-us');
138 $entry->addAuthor(array(
140 'email' => 'paddy@example.com',
141 'uri' => 'http://www.example.com',
143 $entry->setDateModified(time());
144 $entry->setDateCreated(time());
145 $entry->setDescription('Exposing the difficultly of porting games to English.');
147 'I am not writing the article. The example is long enough as is ;).'
149 $feed->addEntry($entry);
152 * Render the resulting feed to Atom 1.0 and assign to $out.
153 * You can substitute "atom" with "rss" to generate an RSS 2.0 feed.
155 $out = $feed->export('atom');
159 The output rendered should be as follows:
162 <programlisting language="xml"><![CDATA[
163 <?xml version="1.0" encoding="utf-8"?>
164 <feed xmlns="http://www.w3.org/2005/Atom">
165 <title type="text">Paddy's Blog</title>
166 <subtitle type="text">Writing about PC Games since 176 BC.</subtitle>
167 <updated>2009-12-14T20:28:18+00:00</updated>
168 <generator uri="http://framework.zend.com" version="1.10.0alpha">
171 <link rel="alternate" type="text/html" href="http://www.example.com"/>
172 <link rel="self" type="application/atom+xml"
173 href="http://www.example.com/atom"/>
174 <id>http://www.example.com</id>
177 <email>paddy@example.com</email>
178 <uri>http://www.example.com</uri>
180 <link rel="hub" href="http://pubsubhubbub.appspot.com/"/>
182 <title type="html"><![CDATA[All Your Base Are Belong To
183 Us]]]]><![CDATA[></title>
184 <summary type="html">
185 <![CDATA[Exposing the difficultly of porting games to
186 English.]]]]><![CDATA[>
188 <published>2009-12-14T20:28:18+00:00</published>
189 <updated>2009-12-14T20:28:18+00:00</updated>
190 <link rel="alternate" type="text/html"
191 href="http://www.example.com/all-your-base-are-belong-to-us"/>
192 <id>http://www.example.com/all-your-base-are-belong-to-us</id>
195 <email>paddy@example.com</email>
196 <uri>http://www.example.com</uri>
198 <content type="html">
199 <![CDATA[I am not writing the article.
200 The example is long enough as is ;).]]]]><![CDATA[>
207 This is a perfectly valid Atom 1.0 example. It should be noted that omitting an
208 obligatory point of data, such as a title, will trigger an
209 <classname>Exception</classname> when rendering as Atom 1.0. This will differ for
210 <acronym>RSS</acronym> 2.0 since a title may be omitted so long as a description is
211 present. This gives rise to Exceptions that differ between the two standards depending
212 on the renderer in use. By design, <classname>Zend_Feed_Writer</classname> will not
213 render an invalid feed for either standard unless the end-user deliberately elects to
214 ignore all Exceptions. This built in safeguard was added to ensure users without
215 in-depth knowledge of the relevant specifications have a bit less to worry about.
219 <sect2 id="zend.feed.writer.setting.feed.data.points">
220 <title>Setting Feed Data Points</title>
223 Before you can render a feed, you must first setup the data necessary for
224 the feed being rendered. This utilises a simple setter style <acronym>API</acronym>
225 which doubles as an initial method for validating the data being set. By design, the
226 <acronym>API</acronym> closely matches that for <classname>Zend_Feed_Reader</classname>
227 to avoid undue confusion and uncertainty.
231 <para>Users have commented that the lack of a simple array based notation for input data
232 gives rise to lengthy tracts of code. This will be addressed in a future release.</para>
236 <classname>Zend_Feed_Writer</classname> offers this <acronym>API</acronym> via its data
237 container classes <classname>Zend_Feed_Writer_Feed</classname> and
238 <classname>Zend_Feed_Writer_Entry</classname> (not to mention the Atom 2.0 specific
239 and Extension classes). These classes merely store
240 all feed data in a type-agnostic manner, meaning you may reuse any data
241 container with any renderer without requiring additional work. Both classes
242 are also amenable to Extensions, meaning that an Extension may define its own
243 container classes which are registered to the base container classes as extensions, and
244 are checked when any method call triggers the base container's
245 <methodname>__call()</methodname> method.
249 Here's a summary of the Core <acronym>API</acronym> for Feeds. You should note it
250 comprises not only the basic <acronym>RSS</acronym> and Atom standards, but also
251 accounts for a number of included Extensions bundled with
252 <classname>Zend_Feed_Writer</classname>. The naming of these
253 Extension sourced methods remain fairly generic - all Extension
254 methods operate at the same level as the Core <acronym>API</acronym> though we do allow
255 you to retrieve any specific Extension object separately if required.
259 The Feed Level <acronym>API</acronym> for data is contained in
260 <classname>Zend_Feed_Writer_Feed</classname>. In addition to the <acronym>API</acronym>
261 detailed below, the class also implements the <classname>Countable</classname> and
262 <classname>Iterator</classname> interfaces.
266 <title>Feed Level API Methods</title>
271 <entry><methodname>setId()</methodname></entry>
274 Set a unique ID associated with this feed. For Atom 1.0
275 this is an atom:id element, whereas for <acronym>RSS</acronym> 2.0 it
276 is added as a guid element. These are optional so long as a link is
277 added, i.e. the link is set as the ID.
282 <entry><methodname>setTitle()</methodname></entry>
283 <entry>Set the title of the feed.</entry>
287 <entry><methodname>setDescription()</methodname></entry>
288 <entry>Set the text description of the feed.</entry>
292 <entry><methodname>setLink()</methodname></entry>
295 Set a <acronym>URI</acronym> to the <acronym>HTML</acronym> website
296 containing the same or
297 similar information as this feed (i.e. if the feed is from a blog,
298 it should provide the blog's <acronym>URI</acronym> where the
299 <acronym>HTML</acronym> version of the entries can be read).
304 <entry><methodname>setFeedLinks()</methodname></entry>
307 Add a link to an <acronym>XML</acronym> feed, whether the feed being
308 generated or an alternate <acronym>URI</acronym> pointing to the same
309 feed but in a different format. At a minimum, it is recommended to
310 include a link to the feed being generated so it has an identifiable
311 final <acronym>URI</acronym> allowing a client to track its location
312 changes without necessitating constant redirects. The parameter is an
313 array of arrays, where each sub-array contains the keys "type" and
314 "uri". The type should be one of "atom", "rss", or "rdf".
319 <entry><methodname>addAuthors()</methodname></entry>
322 Sets the data for authors. The parameter is an array of arrays
323 where each sub-array may contain the keys "name", "email" and
324 "uri". The "uri" value is only applicable for Atom feeds since
325 <acronym>RSS</acronym> contains no facility to show it. For
326 <acronym>RSS</acronym> 2.0, rendering will create two elements - an
327 author element containing the email reference with the name in brackets,
328 and a Dublin Core creator element only containing the name.
333 <entry><methodname>addAuthor()</methodname></entry>
336 Sets the data for a single author following the same
337 array format as described above for a single sub-array.
342 <entry><methodname>setDateCreated()</methodname></entry>
345 Sets the date on which this feed was created. Generally
346 only applicable to Atom where it represents the date the resource
347 described by an Atom 1.0 document was created. The expected parameter
348 may be a <acronym>UNIX</acronym> timestamp or a
349 <classname>Zend_Date</classname> object.
354 <entry><methodname>setDateModified()</methodname></entry>
357 Sets the date on which this feed was last modified. The expected
358 parameter may be a <acronym>UNIX</acronym> timestamp or a
359 <classname>Zend_Date</classname> object.
364 <entry><methodname>setLastBuildDate()</methodname></entry>
367 Sets the date on which this feed was last build. The expected
368 parameter may be a <acronym>UNIX</acronym> timestamp or a
369 <classname>Zend_Date</classname> object. This will only be
370 rendered for RSS 2.0 feeds and is automatically rendered as
371 the current date by default when not explicity set.
376 <entry><methodname>setLanguage()</methodname></entry>
379 Sets the language of the feed. This will be omitted unless set.
384 <entry><methodname>setGenerator()</methodname></entry>
387 Allows the setting of a generator. The parameter should be an
388 array containing the keys "name", "version" and "uri". If omitted
389 a default generator will be added referencing
390 <classname>Zend_Feed_Writer</classname>, the current Zend Framework
391 version and the Framework's <acronym>URI</acronym>.
396 <entry><methodname>setCopyright()</methodname></entry>
397 <entry>Sets a copyright notice associated with the feed.</entry>
401 <entry><methodname>addHubs()</methodname></entry>
404 Accepts an array of Pubsubhubbub Hub Endpoints to be rendered in
405 the feed as Atom links so that PuSH Subscribers may subscribe to
406 your feed. Note that you must implement a Pubsubhubbub Publisher in
407 order for real-time updates to be enabled. A Publisher may be
409 <classname>Zend_Feed_Pubsubhubbub_Publisher</classname>.
410 The method <methodname>addHub()</methodname> allows adding
411 a single hub at a time.
416 <entry><methodname>addCategories()</methodname></entry>
419 Accepts an array of categories for rendering, where each element is
420 itself an array whose possible keys include "term", "label" and
421 "scheme". The "term" is a typically a category name suitable for
422 inclusion in a <acronym>URI</acronym>. The "label" may be a human
423 readable category name supporting special characters (it is
424 <acronym>HTML</acronym> encoded during rendering) and is a required key.
425 The "scheme" (called the domain in <acronym>RSS</acronym>) is optional
426 but must be a valid <acronym>URI</acronym>. The method
427 <methodname>addCategory()</methodname> allows adding a single category
433 <entry><methodname>setImage()</methodname></entry>
435 Accepts an array of image metadata for an RSS image or Atom logo. Atom 1.0
436 only requires a URI. RSS 2.0 requires a URI, HTML link, and an image title.
437 RSS 2.0 optionally may send a width, height and image description. The array
438 parameter may contain these using the keys: uri, link, title, description,
439 height and width. The RSS 2.0 HTML link should point to the feed source's
445 <entry><methodname>createEntry()</methodname></entry>
446 <entry>Returns a new instance of <classname>Zend_Feed_Writer_Entry
447 </classname>. This is the Entry level data container. New entries
448 are not automatically assigned to the current feed, so you
449 must explicitly call <methodname>addEntry()</methodname>
450 to add the entry for rendering.</entry>
454 <entry><methodname>addEntry()</methodname></entry>
455 <entry>Adds an instance of <classname>Zend_Feed_Writer_Entry
456 </classname> to the current feed container for rendering.</entry>
460 <entry><methodname>createTombstone()</methodname></entry>
461 <entry>Returns a new instance of <classname>Zend_Feed_Writer_Deleted
462 </classname>. This is the Atom 2.0 Tombstone level data container. New
463 entries are not automatically assigned to the current feed, so you
464 must explicitly call <methodname>addTombstone()</methodname>
465 to add the deleted entry for rendering.</entry>
469 <entry><methodname>addTombstone()</methodname></entry>
470 <entry>Adds an instance of <classname>Zend_Feed_Writer_Deleted
471 </classname> to the current feed container for rendering.</entry>
475 <entry><methodname>removeEntry()</methodname></entry>
476 <entry>Accepts a parameter indicating an array index of the
477 entry to remove from the feed.</entry>
481 <entry><methodname>export()</methodname></entry>
484 Exports the entire data hierarchy to an <acronym>XML</acronym> feed. The
485 method has two parameters. The first is the feed type, one of "atom"
486 or "rss". The second is an optional boolean to set whether
487 Exceptions are thrown. The default is <constant>TRUE</constant>.
496 In addition to these setters, there are also matching getters to retrieve data from
497 the Entry data container. For example, <methodname>setImage()</methodname> is matched
498 with a <methodname>getImage()</methodname> method.
502 <!-- remaining feed stuff -->
506 <sect2 id="zend.feed.writer.setting.entry.data.points">
507 <title>Setting Entry Data Points</title>
510 Here's a summary of the Core <acronym>API</acronym> for Entries and Items. You should
511 note it comprises not only the basic <acronym>RSS</acronym> and Atom standards, but
512 also accounts for a number of included Extensions bundled with
513 <classname>Zend_Feed_Writer</classname>. The naming of these
514 Extension sourced methods remain fairly generic - all Extension
515 methods operate at the same level as the Core <acronym>API</acronym> though we do allow
516 you to retrieve any specific Extension object separately if required.
520 The Entry Level <acronym>API</acronym> for data is contained in
521 <classname>Zend_Feed_Writer_Entry</classname>.
525 <title>Entry Level API Methods</title>
530 <entry><methodname>setId()</methodname></entry>
533 Set a unique ID associated with this entry. For Atom 1.0
534 this is an atom:id element, whereas for <acronym>RSS</acronym> 2.0 it is
535 added as a guid element. These are optional so long as a link is
536 added, i.e. the link is set as the ID.
541 <entry><methodname>setTitle()</methodname></entry>
542 <entry>Set the title of the entry.</entry>
546 <entry><methodname>setDescription()</methodname></entry>
547 <entry>Set the text description of the entry.</entry>
551 <entry><methodname>setContent()</methodname></entry>
552 <entry>Set the content of the entry.</entry>
556 <entry><methodname>setLink()</methodname></entry>
559 Set a <acronym>URI</acronym> to the <acronym>HTML</acronym> website
560 containing the same or
561 similar information as this entry (i.e. if the feed is from a blog,
562 it should provide the blog article's <acronym>URI</acronym> where the
563 <acronym>HTML</acronym> version of the entry can be read).
568 <entry><methodname>setFeedLinks()</methodname></entry>
571 Add a link to an <acronym>XML</acronym> feed, whether the feed being
572 generated or an alternate <acronym>URI</acronym> pointing to the same
573 feed but in a different format. At a minimum, it is recommended to
574 include a link to the feed being generated so it has an identifiable
575 final <acronym>URI</acronym> allowing a client to track its location
576 changes without necessitating constant redirects. The parameter is an
577 array of arrays, where each sub-array contains the keys "type" and
578 "uri". The type should be one of "atom", "rss", or "rdf". If a type is
579 omitted, it defaults to the type used when rendering the feed.
584 <entry><methodname>addAuthors()</methodname></entry>
587 Sets the data for authors. The parameter is an array of arrays
588 where each sub-array may contain the keys "name", "email" and
589 "uri". The "uri" value is only applicable for Atom feeds since
590 <acronym>RSS</acronym> contains no facility to show it. For
591 <acronym>RSS</acronym> 2.0, rendering will create two elements - an
592 author element containing the email reference with the name in brackets,
593 and a Dublin Core creator element only containing the name.
598 <entry><methodname>addAuthor()</methodname></entry>
601 Sets the data for a single author following the same
602 format as described above for a single sub-array.
607 <entry><methodname>setDateCreated()</methodname></entry>
610 Sets the date on which this feed was created. Generally
611 only applicable to Atom where it represents the date the resource
612 described by an Atom 1.0 document was created. The expected parameter
613 may be a <acronym>UNIX</acronym> timestamp or a
614 <classname>Zend_Date</classname> object. If omitted, the date
615 used will be the current date and time.
620 <entry><methodname>setDateModified()</methodname></entry>
623 Sets the date on which this feed was last modified. The expected
624 parameter may be a <acronym>UNIX</acronym> timestamp or a
625 <classname>Zend_Date</classname> object. If omitted, the date
626 used will be the current date and time.
631 <entry><methodname>setCopyright()</methodname></entry>
632 <entry>Sets a copyright notice associated with the feed.</entry>
636 <entry><methodname>setCategories()</methodname></entry>
639 Accepts an array of categories for rendering, where each element is
640 itself an array whose possible keys include "term", "label" and
641 "scheme". The "term" is a typically a category name suitable for
642 inclusion in a <acronym>URI</acronym>. The "label" may be a human
643 readable category name supporting special characters (it is encoded
644 during rendering) and is a required key. The "scheme" (called the domain
645 in <acronym>RSS</acronym>) is optional but must be a valid
646 <acronym>URI</acronym>.
651 <entry><methodname>setCommentCount()</methodname></entry>
654 Sets the number of comments associated with this entry. Rendering
655 differs between <acronym>RSS</acronym> and Atom 2.0 depending
656 on the element/attribute needed.
661 <entry><methodname>setCommentLink()</methodname></entry>
664 Seta a link to a <acronym>HTML</acronym> page containing comments
665 associated with this entry.
670 <entry><methodname>setCommentFeedLink()</methodname></entry>
673 Sets a link to a <acronym>XML</acronym> feed containing comments
674 associated with this entry. The parameter is an array containing the
675 keys "uri" and "type", where the type is one of "rdf", "rss" or "atom".
680 <entry><methodname>setCommentFeedLinks()</methodname></entry>
683 Same as <methodname>setCommentFeedLink()</methodname> except it
684 accepts an array of arrays, where each subarray contains the
685 expected parameters of <methodname>setCommentFeedLink()</methodname>.
690 <entry><methodname>setEncoding()</methodname></entry>
693 Sets the encoding of entry text. This will default to UTF-8 which
694 is the preferred encoding.
703 In addition to these setters, there are also matching getters to retrieve data from
704 from the Entry data container.