<?php
|
|
namespace FeedWriter;
|
|
|
|
use \Iterator;
|
|
use \Exception;
|
|
use \stdClass;
|
|
use \ArrayIterator;
|
|
|
|
class Feed {
|
|
|
|
const RSS1 = 'RSS 1.0';
|
|
const RSS2 = 'RSS 2.0';
|
|
const ATOM = 'ATOM';
|
|
|
|
protected $_iter = null;
|
|
protected $_map = [];
|
|
protected $_views = [];
|
|
|
|
/**
|
|
* Generate one or more views of a collection from a given iterator or array.
|
|
*
|
|
* Takes a data source, an array describing a mapping from instances of the
|
|
* collection to items in the view, and a list of views to generate.
|
|
*
|
|
* This all sounds more abstract than it actually is, so let's run through
|
|
* some examples:
|
|
*
|
|
* <code>
|
|
*
|
|
* use \FeedWriter\Feed;
|
|
* use \FeedWriter\View\Atom;
|
|
*
|
|
* // Here's an array of arrays describing some blog posts. These could also
|
|
* // be objects with properties like $blog_posts->writer.
|
|
*
|
|
* $blog_posts = [
|
|
* [ 'date_created' => '2013-02-14', 'writer' => 'Brennen Bearnes', 'text' => 'Hate.' ],
|
|
* [ 'date_created' => '2013-07-04', 'writer' => 'Brennen Bearnes', 'text' => 'Explosions.' ],
|
|
* [ 'date_created' => '2013-08-31', 'writer' => 'Brennen Bearnes', 'text' => 'A feed thingy.' ],
|
|
* ];
|
|
*
|
|
* // Here's a map that explains how to find the properties we need for our
|
|
* // view:
|
|
* $entry_from_post = [
|
|
* 'date' => 'date_created',
|
|
* 'author' => 'writer',
|
|
* 'content' => 'text'
|
|
* ];
|
|
*
|
|
* $feed = new Feed($blog_posts, $entry_from_post, ['atom' => new Atom]);
|
|
*
|
|
* // If everything went ok, views are now available as properties of the feed.
|
|
* if (! $feed->error())
|
|
* echo $feed->atom->render();
|
|
* else
|
|
* echo 'error: ' . $feed->error();
|
|
*
|
|
* </code>
|
|
*
|
|
* @param $source mixed iterator or array
|
|
* @param $map array describing mapping of fields to fields
|
|
* @param $views array one or more views to generate
|
|
*/
|
|
public function __construct ($source, array $map, array $views)
|
|
{
|
|
if (is_array($source))
|
|
$iter = new ArrayIterator($source);
|
|
else
|
|
$iter = $source;
|
|
|
|
$this->setIter($iter);
|
|
$this->setViewsAndMap($views, $map);
|
|
$this->spin();
|
|
}
|
|
|
|
/**
|
|
* Explode if anybody tries to set a property directly.
|
|
*/
|
|
public function __set ($name, $view)
|
|
{
|
|
throw new Exception("You can't directly set properties on Feed. See documentation for constructor.");
|
|
}
|
|
|
|
/**
|
|
* Get a view.
|
|
*/
|
|
public function __get ($name)
|
|
{
|
|
return $this->_views[$name];
|
|
}
|
|
|
|
/**
|
|
* Is a given view set?
|
|
*/
|
|
public function __isset ($name)
|
|
{
|
|
return isset($this->_views[$name]);
|
|
}
|
|
|
|
/**
|
|
* Stash views and map, making sure that the map provides for anything
|
|
* the view explicitly requires.
|
|
*/
|
|
protected function setViewsAndMap (array $views, array $map)
|
|
{
|
|
if (! count($views))
|
|
throw new Exception('Feed requires that at least one view instance be passed in)');
|
|
|
|
if (! is_array($map))
|
|
throw new Exception('$map must be an array');
|
|
|
|
if (! count($map))
|
|
throw new Exception('$map must provide values');
|
|
|
|
// Validate map for each view:
|
|
foreach ($views as $name => $view) {
|
|
if (! isset($view->require))
|
|
continue;
|
|
|
|
foreach ($view->require as $field) {
|
|
if (! isset($map[ $field ])) {
|
|
throw new Exception('$map should provide a mapping for ' . $field);
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->_views = $views;
|
|
$this->_map = $map;
|
|
}
|
|
|
|
protected function setIter (Iterator $iter)
|
|
{
|
|
if (! ($iter instanceof Iterator))
|
|
throw new Exception('$iter must be an iterator');
|
|
$this->_iter = $iter;
|
|
}
|
|
|
|
/**
|
|
* Do the business of handing off values to views.
|
|
*/
|
|
protected function spin ()
|
|
{
|
|
$output_item = (object)[];
|
|
while ($this->_iter->valid()) {
|
|
$input_item = $this->_iter->current();
|
|
|
|
if (is_array($input_item))
|
|
$input_item = (object)$input_item;
|
|
|
|
foreach ($this->_map as $output_key => &$input_key) {
|
|
if (is_string($input_key))
|
|
{
|
|
// shorthand for "use this field on the object for the value"
|
|
if (isset($input_item->$input_key))
|
|
$output_item->$output_key = $input_item->$input_key;
|
|
else
|
|
throw new Exception("unable to access $input_key on input item: " . print_r($input_item, 1));
|
|
}
|
|
elseif (is_callable($input_key))
|
|
{
|
|
// if we have a function, pass it the item and expect it to
|
|
// return a value for our output key
|
|
$output_item->$output_key = $input_key($input_item);
|
|
}
|
|
}
|
|
|
|
foreach ($this->_views as $name => $view) {
|
|
$view->collect($output_item);
|
|
}
|
|
|
|
$this->_iter->next();
|
|
}
|
|
}
|
|
|
|
}
|