View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template}

{zone-data:component-name}
Zend_Feed_Writer
{zone-data}

{zone-data:proposer-list}
[~padraic]
{zone-data}

{zone-data:revision}
1.1 - 20 September 2009
{zone-data}

{zone-data:overview}
h5. Why Propose A Replacement For Zend_Feed's Feed Creation Features?

Back in 2008, I proposed Zend_Feed_Reader as a means to layer a user friendly API on top of Zend_Feed when consuming feeds. In a short time, the proposal evolved after more and more Zend_Feed inconsistencies were discovered. For example, there was no consistent API once you left the basic usage, namespaces were not handled correctly, RDF support was non-existent (the current solution muddles namespaces even more), and in brief it forced developers to write an abstraction layer on top of it to make it work.

Zend_Feed_Reader took a different approach, targeting an implementation which used the DOM and XPath queries coupled with an internal implementation which was capable of understanding the various RSS and Atom standards. This was all built around a simple intuitive and extendable API to eliminate the guesswork users needed as well as the need to implement abstraction layers which differed wildly across projects. Zend_Feed_Reader is now a standalone component with no ties to Zend_Feed.

Zend_Feed_Writer follows an identical ideology - focusing on getting the job done with minimal effort using as simple an API as possible. The component should make transferring your data into an Atom 1.0 or RSS 2.0 feed painless. And it should do this while maintaining a rigorous and unforgiving adherence to standards.

h5. About Zend_Feed_Writer

The main differences from Zend_Feed will be very obvious. Zend_Feed_Writer will present a fixed method based API which must be clearly defined either in the core classes, or the set of currently loaded Extensions. These will help define namespaces, element positioning and syntax, and actively prevent the construction of malformed feeds. Two initial standards will be enforced: RSS 2.0 (specifically the RSS Advisory Board 2.0.11 recommendations) and Atom 1.0. The full range of Atom 1.0 will be available, as standard, as an RSS Extension where warranted (e.g. including URI's for an RSS feed's source XML which borrows from the Atom namespace). This will contrast with Zend_Feed's reliance on chained property assignments which require greater knowledge of the standards, and are not easily reduced to common steps for both RSS and Atom.

Zend_Feed_Writer will not utilise templating. Templating is a fast, effective solution for building feeds but it's also restrictive, encourages assumptions as to namespaces and positioning, and relies on templated strings which are prone to error and add to maintenance problems. Instead, Zend_Feed_Writer will build its feeds from scratch using the PHP DOM which at the end of the day requires less base code since everything is condensed into DOM operations. DOM access will be available at all levels for manipulation outside of Zend_Feed_Writer (just as Zend_Feed_Reader offers easy access to Feed/Entry level DOMElement objects). It also ensures user Extensions have complete access to manipulate a feed in progress as needed.

In short, Zend_Feed_Writer is the obvious missing partner to Zend_Feed_Reader, completing support for consuming and building feeds.
{zone-data}

{zone-data:references}
[Basic implementation with Atom support|http://github.com/padraic/zfproposals/tree/master]
{zone-data}

{zone-data:requirements}
* MUST understand the valid structure of RSS 2.0 and Atom 1.0 documents
* MUST enforce valid structures raising Exceptions on illegal actions or when missing elements are detected
* MUST allow the API to be extended via Extensions
* MUST present an API which is consistent whether the feed being built is RSS or Atom
* MUST expose the underlying parent DOMDocument or DOMElement object for external manipulation
* MAY be capable of importing Zend_Feed_Reader objects for manipulation/editing (preferred future feature)

{zone-data}

{zone-data:dependencies}
Zend_Http_Client
Zend_Cache
Zend_Loader_PluginLoader
{zone-data}

{zone-data:operation}
Since neither RSS or Atom assign any significance to the order of elements, there is no need for any complex wiring. So the simplest solution is to implement Zend_Feed_Writer as two branches of classes. The first implement a fixed method API, e.g. methods like setTitle() and getTitle(). In isolation, these classes are nothing more than container objects which can be manipulated at any time. The real work is performed by the second branch of classes implementing Zend_Feed_Writer_RendererInterface. These, including all Renderers registered from Extensions, will form a simple render queue with a DOMDocument object representing either a Feed or Entry passed along the queue so each Renderer can append additional elements or edit others. The final result is then constructed by importing all root Entry elements into the Feed level DOM. The term "Renderer" is probably not even the most suitable one since the actual XML string produced is a simple saveXML() call on the final DOMDocument, but it will suffice for now.
{zone-data}

{zone-data:milestones}
* Milestone 1: Publish and have proposal approved with Zend comments to resolve
* Milestone 2: Complete initial component with unit tests
* Milestone 3: Complete testing against a selection of web-based/desktop feed readers
* Milestone 4: Verify that code operates under PHP 5.3/6.0-dev
* Milestone 5: Complete documentation
{zone-data}

{zone-data:class-list}
* Zend_Feed_Writer
* Zend_Feed_Writer_Entry
* Zend_Feed_Writer_RendererInterface
* Zend_Feed_Writer_Renderer_Feed_Rss
* Zend_Feed_Writer_Renderer_Feed_Atom
* Zend_Feed_Writer_Renderer_Entry_Rss
* Zend_Feed_Writer_Renderer_Entry_Atom
* Zend_Feed_Writer_Renderer_Extension_FeedAbstract
* Zend_Feed_Writer_Renderer_Extension_EntryAbstract
Others may be determined during development
{zone-data}

{zone-data:use-cases}

Full working example:

{code}<?php

$writer = new Zend_Feed_Writer;
/**
* Set Feed level data
*/
$writer->setTitle('Joe Speaks About Many Things');
$writer->setLink('http://blog.example.com/joe');
$writer->setDescription('Joe Bloggs, Average Citizen, Earth.');
$writer->setFeedLink('http://blog.example.com/joe/atom', 'atom');
$writer->setFeedLink('http://blog.example.com/joe/rss', 'rss');
$writer->setDateModified(time());
/**
* Add a single Entry
*/
$entry = $writer->createEntry();
$entry->setTitle('This is my new blog');
$entry->setLink('http://blog.example.com/joe/entry/1');
$entry->setDescription('Post about my new blog');
$entry->setContent('Do you like my new blog? Let me know!');
$entry->setDateCreated(time());
$entry->setDateModified(time());
$entry->addAuthor('Joe', 'joe@example.com');
$writer->addEntry($entry); // manual addition post-creation
/**
* Render, export and print the feed XML
*/
echo $feed->export('atom');{code}

Output from the above:

{code}<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">Joe Speaks About Many Things</title>
<subtitle type="text">Joe Bloggs, Average Citizen, Earth.</subtitle>
<updated>2009-07-27T21:01:17+01:00</updated>
<generator uri="http://framework.zend.com" version="1.9.0a1">Zend_Feed_Writer</generator>
<link rel="alternate" type="text/html" href="http://blog.example.com/joe"/>
<link rel="self" type="application/atom+xml" href="http://blog.example.com/joe/atom"/>
<link rel="self" type="application/rss+xml" href="http://blog.example.com/joe/rss"/>
<id>http://blog.example.com/joe</id>
<entry>
<title type="text">This is my new blog</title>
<summary type="text">Post about my new blog</summary>
<updated>2009-07-27T21:01:17+01:00</updated>
<published>2009-07-27T21:01:17+01:00</published>
<link rel="alternate" type="text/html" href="http://blog.example.com/joe/entry/1"/>
<id>http://blog.example.com/joe/entry/1</id>
<author>
<name>Joe</name>
<email>joe@example.com</email>
</author>
<content type="text/html">Do you like my new blog? Let me know!</content>
</entry>
</feed>
{code}

{zone-data}

{zone-data:skeletons}
{code}<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic dot brady at yahoo dot com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Feed_Writer
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/**
* @see Zend_Date
*/
require_once 'Zend/Date.php';

/**
* @see Zend_Date
*/
require_once 'Zend/Uri.php';

/**
* @see Zend_Feed_Writer_Entry
*/
require_once 'Zend/Feed/Writer/Entry.php';

/**
* @category Zend
* @package Zend_Feed_Writer
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Feed_Writer implements Iterator, Countable
{
/**
* Namespace constants
*/
const NAMESPACE_ATOM_03 = 'http://purl.org/atom/ns#';
const NAMESPACE_ATOM_10 = 'http://www.w3.org/2005/Atom';
const NAMESPACE_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
const NAMESPACE_RSS_090 = 'http://my.netscape.com/rdf/simple/0.9/';
const NAMESPACE_RSS_10 = 'http://purl.org/rss/1.0/';

/**
* Feed type constants
*/
const TYPE_ANY = 'any';
const TYPE_ATOM_03 = 'atom-03';
const TYPE_ATOM_10 = 'atom-10';
const TYPE_ATOM_ANY = 'atom';
const TYPE_RSS_090 = 'rss-090';
const TYPE_RSS_091 = 'rss-091';
const TYPE_RSS_091_NETSCAPE = 'rss-091n';
const TYPE_RSS_091_USERLAND = 'rss-091u';
const TYPE_RSS_092 = 'rss-092';
const TYPE_RSS_093 = 'rss-093';
const TYPE_RSS_094 = 'rss-094';
const TYPE_RSS_10 = 'rss-10';
const TYPE_RSS_20 = 'rss-20';
const TYPE_RSS_ANY = 'rss';

/**
* Contains all Feed level date to append in feed output
*
* @var array
*/
protected $_data = array();

/**
* Contains all entry objects
*
* @var array
*/
protected $_entries = array();

/**
* A pointer for the iterator to keep track of the entries array
*
* @var int
*/
protected $_entriesKey = 0;

/**
* Set a single author
*
* @param int $index
* @return string|null
*/
public function addAuthor($name, $email = null, $uri = null);

/**
* Set an array with feed authors
*
* @return array
*/
public function addAuthors(array $authors);

/**
* Set the copyright entry
*
* @return string|null
*/
public function setCopyright($copyright);

/**
* Set the feed creation date
*
* @return string|null
*/
public function setDateCreated($date = null);
/**
* Set the feed modification date
*
* @return string|null
*/
public function setDateModified($date = null);
/**
* Set the feed description
*
* @return string|null
*/
public function setDescription($description);

/**
* Set the feed generator entry
*
* @return string|null
*/
public function setGenerator($name, $version = null, $uri = null);

/**
* Set the feed ID - URI or URN (via PCRE pattern) supported
*
* @return string|null
*/
public function setId($id);

/**
* Set the feed language
*
* @return string|null
*/
public function setLanguage($language);

/**
* Set a link to the HTML source
*
* @return string|null
*/
public function setLink($link);

/**
* Set a link to an XML feed for any feed type/version
*
* @return string|null
*/
public function setFeedLink($link, $type);

/**
* Set the feed title
*
* @return string|null
*/
public function setTitle($title);

/**
* Set the feed character encoding
*
* @return string|null
*/
public function setEncoding($encoding);

/**
* Get a single author
*
* @param int $index
* @return string|null
*/
public function getAuthor($index = 0);
}
}

/**
* Get an array with feed authors
*
* @return array
*/
public function getAuthors();

/**
* Get the copyright entry
*
* @return string|null
*/
public function getCopyright();

/**
* Get the feed creation date
*
* @return string|null
*/
public function getDateCreated();

/**
* Get the feed modification date
*
* @return string|null
*/
public function getDateModified();

/**
* Get the feed description
*
* @return string|null
*/
public function getDescription();

/**
* Get the feed generator entry
*
* @return string|null
*/
public function getGenerator();

/**
* Get the feed ID
*
* @return string|null
*/
public function getId();

/**
* Get the feed language
*
* @return string|null
*/
public function getLanguage();

/**
* Get a link to the HTML source
*
* @return string|null
*/
public function getLink();

/**
* Get a link to the XML feed
*
* @return string|null
*/
public function getFeedLinks();

/**
* Get the feed title
*
* @return string|null
*/
public function getTitle();

/**
* Get the feed character encoding
*
* @return string|null
*/
public function getEncoding();

/**
* Resets the instance and deletes all data
*
*/
public function reset();

public function createEntry();
}

public function addEntry(Zend_Feed_Writer_Entry $entry);

public function removeEntry($index);

public function getEntry($index = 0);

public function orderByDate();

/**
* Get the number of feed entries.
* Required by the Iterator interface.
*
* @return int
*/
public function count();

/**
* Return the current entry
*
* @return Zend_Feed_Reader_Entry_Interface
*/
public function current();

/**
* Return the current feed key
*
* @return unknown
*/
public function key();

/**
* Move the feed pointer forward
*
*/
public function next();

/**
* Reset the pointer in the feed object
*
*/
public function rewind();

/**
* Check to see if the iterator is still valid
*
* @return boolean
*/
public function valid();

/**
* Attempt to build and return the feed resulting from the data set
*
* @param $type The feed type "rss" or "atom" to export as
* @return string
*/
public function export($type, $ignoreExceptions = false);


}{code}
{code}<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic dot brady at yahoo dot com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Feed_Writer_Entry
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/**
* @see Zend_Date
*/
require_once 'Zend/Date.php';

/**
* @see Zend_Date
*/
require_once 'Zend/Uri.php';

/**
* @category Zend
* @package Zend_Feed_Writer_Entry
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Feed_Writer_Entry
{

protected $_data = array();

/**
* Set a single author
*
* @param int $index
* @return string|null
*/
public function addAuthor($name, $email = null, $uri = null);

/**
* Set an array with feed authors
*
* @return array
*/
public function addAuthors(array $authors);

/**
* Set the copyright entry
*
* @return string|null
*/
public function setCopyright($copyright);

/**
* Set the entry's content
*
* @return string|null
*/
public function setContent($content);

/**
* Set the feed creation date
*
* @return string|null
*/
public function setDateCreated($date = null);

/**
* Set the feed modification date
*
* @return string|null
*/
public function setDateModified($date = null);
/**
* Set the feed description
*
* @return string|null
*/
public function setDescription($description);

/**
* Set the feed ID
*
* @return string|null
*/
public function setId($id);

/**
* Set a link to the HTML source of this entry
*
* @return string|null
*/
public function setLink($link);

/**
* Set a permalink to the source of this entry
*
* @return string|null
*/
public function setPermalink($link);

/**
* Set the number of comments associated with this entry
*
* @return string|null
*/
public function setCommentCount($count);

/**
* Set a link to a HTML page containing comments associated with this entry
*
* @return string|null
*/
public function setCommentLink($link);

/**
* Set a link to an XML feed for any comments associated with this entry
*
* @return string|null
*/
public function setCommentFeedLink($link);

/**
* Set the feed title
*
* @return string|null
*/
public function setTitle($title);

/**
* Set the feed character encoding
*
* @return string|null
*/
public function setEncoding($encoding);
/**
* Get the feed character encoding
*
* @return string|null
*/
public function getEncoding();

/**
* Get the specified author
*
* @param int $index
* @return string|null
*/
public function getAuthor($index = 0);

/**
* Get an array with feed authors
*
* @return array
*/
public function getAuthors();

/**
* Get the entry content
*
* @return string
*/
public function getContent();

/**
* Get the entry copyright information
*
* @return string
*/
public function getCopyright();

/**
* Get the entry creation date
*
* @return string
*/
public function getDateCreated();

/**
* Get the entry modification date
*
* @return string
*/
public function getDateModified();

/**
* Get the entry description
*
* @return string
*/
public function getDescription();

/**
* Get the entry ID
*
* @return string
*/
public function getId();

/**
* Get a link to the HTML source
*
* @return string|null
*/
public function getLink();

/**
* Get all links
*
* @return array
*/
public function getLinks();

/**
* Get a permalink to the entry
*
* @return string
*/
public function getPermalink();

/**
* Get the entry title
*
* @return string
*/
public function getTitle();
/**
* Get the number of comments/replies for current entry
*
* @return integer
*/
public function getCommentCount();

/**
* Returns a URI pointing to the HTML page where comments can be made on this entry
*
* @return string
*/
public function getCommentLink();
/**
* Returns a URI pointing to a feed of all comments for this entry
*
* @return string
*/
public function getCommentFeedLink();

}{code}
{code}<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic dot brady at yahoo dot com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Feed_Writer_RendererInterface
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/**
* @category Zend
* @package Zend_Feed_Writer_RendererInterface
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
interface Zend_Feed_Writer_RendererInterface
{

public function render();

public function saveXml();

public function getDomDocument();

public function getElement();

public function getDataContainer();

public function ignoreExceptions();

}{code}
{code}<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic dot brady at yahoo dot com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Feed_Writer_Renderer_Feed_Atom
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

require_once 'Zend/Feed/Writer.php';

require_once 'Zend/Version.php';

require_once 'Zend/Feed/Writer/RendererInterface.php';

require_once 'Zend/Feed/Writer/Renderer/Entry/Atom.php';

/**
* @category Zend
* @package Zend_Feed_Writer_Renderer_Feed_Atom
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Feed_Writer_Renderer_Feed_Atom implements Zend_Feed_Writer_RendererInterface
{

protected $_container = null;

protected $_dom = null;

protected $_ignoreExceptions = false;

protected $_exceptions = array();

public function __construct (Zend_Feed_Writer $container);

public function render();

public function saveXml();

public function getDomDocument();

public function getElement();

public function getDataContainer();

public function ignoreExceptions($bool = true);

public function getExceptions();

}{code}
{code}<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic dot brady at yahoo dot com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Feed_Writer_Entry_Atom
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

require_once 'Zend/Feed/Writer.php';

require_once 'Zend/Version.php';

/**
* @category Zend
* @package Zend_Feed_Writer_Entry_Atom
* @copyright Copyright (c) 2009 Padraic Brady
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Feed_Writer_Renderer_Entry_Atom implements Zend_Feed_Writer_RendererInterface
{

protected $_container = null;

protected $_dom = null;

protected $_ignoreExceptions = false;

protected $_exceptions = array();

public function __construct (Zend_Feed_Writer_Entry $container);

public function render();

public function saveXml();

public function getDomDocument();

public function getElement();

public function getDataContainer();

public function ignoreExceptions($bool = true);

public function getExceptions();

}{code}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>