<?php define('RSS2', 1, true); define('JSON', 2, true); define('JSONP', 3, true); /** * Univarsel Feed Writer class * * Genarate RSS2 or JSON (original: RSS 1.0, RSS2.0 and ATOM Feed) * * Modified for FiveFilters.org's Full-Text RSS project * to allow for inclusion of hubs, JSON output. * Stripped RSS1 and ATOM support. * * @package UnivarselFeedWriter * @author Anis uddin Ahmad <anisniit@gmail.com> * @link http://www.ajaxray.com/projects/rss */ class FeedWriter { private $self = null; // self URL - http://feed2.w3.org/docs/warning/MissingAtomSelfLink.html private $hubs = array(); // PubSubHubbub hubs private $channels = array(); // Collection of channel elements private $items = array(); // Collection of items as object of FeedItem class. private $data = array(); // Store some other version wise data private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA private $xsl = null; // stylesheet to render RSS (used by Chrome) private $json = null; // JSON object private $version = null; /** * Constructor * * @param constant the version constant (RSS2 or JSON). */ function __construct($version = RSS2) { $this->version = $version; // Setting default value for assential channel elements $this->channels['title'] = $version . ' Feed'; $this->channels['link'] = 'http://www.ajaxray.com/blog'; //Tag names to encode in CDATA $this->CDATAEncoding = array('description', 'content:encoded', 'content', 'subtitle', 'summary'); } public function setFormat($format) { $this->version = $format; } // Start # public functions --------------------------------------------- /** * Set a channel element * @access public * @param srting name of the channel tag * @param string content of the channel tag * @return void */ public function setChannelElement($elementName, $content) { $this->channels[$elementName] = $content ; } /** * Set multiple channel elements from an array. Array elements * should be 'channelName' => 'channelContent' format. * * @access public * @param array array of channels * @return void */ public function setChannelElementsFromArray($elementArray) { if(! is_array($elementArray)) return; foreach ($elementArray as $elementName => $content) { $this->setChannelElement($elementName, $content); } } /** * Genarate the actual RSS/JSON file * * @access public * @return void */ public function genarateFeed() { if ($this->version == RSS2) { header('Content-type: text/xml; charset=UTF-8'); // this line prevents Chrome 20 from prompting download // used by Google: https://news.google.com/news/feeds?ned=us&topic=b&output=rss header('X-content-type-options: nosniff'); } elseif ($this->version == JSON) { header('Content-type: application/json; charset=UTF-8'); $this->json = new stdClass(); } elseif ($this->version == JSONP) { header('Content-type: application/javascript; charset=UTF-8'); $this->json = new stdClass(); } $this->printHead(); $this->printChannels(); $this->printItems(); $this->printTale(); if ($this->version == JSON || $this->version == JSONP) { echo json_encode($this->json); } } /** * Create a new FeedItem. * * @access public * @return object instance of FeedItem class */ public function createNewItem() { $Item = new FeedItem($this->version); return $Item; } /** * Add a FeedItem to the main class * * @access public * @param object instance of FeedItem class * @return void */ public function addItem($feedItem) { $this->items[] = $feedItem; } // Wrapper functions ------------------------------------------------------------------- /** * Set the 'title' channel element * * @access public * @param srting value of 'title' channel tag * @return void */ public function setTitle($title) { $this->setChannelElement('title', $title); } /** * Add a hub to the channel element * * @access public * @param string URL * @return void */ public function addHub($hub) { $this->hubs[] = $hub; } /** * Set XSL URL * * @access public * @param string URL * @return void */ public function setXsl($xsl) { $this->xsl = $xsl; } /** * Set self URL * * @access public * @param string URL * @return void */ public function setSelf($self) { $this->self = $self; } /** * Set the 'description' channel element * * @access public * @param srting value of 'description' channel tag * @return void */ public function setDescription($desciption) { $tag = ($this->version == ATOM)? 'subtitle' : 'description'; $this->setChannelElement($tag, $desciption); } /** * Set the 'link' channel element * * @access public * @param srting value of 'link' channel tag * @return void */ public function setLink($link) { $this->setChannelElement('link', $link); } /** * Set the 'image' channel element * * @access public * @param srting title of image * @param srting link url of the imahe * @param srting path url of the image * @return void */ public function setImage($title, $link, $url) { $this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url)); } // End # public functions ---------------------------------------------- // Start # private functions ---------------------------------------------- /** * Prints the xml and rss namespace * * @access private * @return void */ private function printHead() { if ($this->version == RSS2) { $out = '<?xml version="1.0" encoding="utf-8"?>'."\n"; if ($this->xsl) $out .= '<?xml-stylesheet type="text/xsl" href="'.htmlspecialchars($this->xsl).'"?>' . PHP_EOL; $out .= '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">' . PHP_EOL; echo $out; } elseif ($this->version == JSON || $this->version == JSONP) { $this->json->rss = array('@attributes' => array('version' => '2.0')); } } /** * Closes the open tags at the end of file * * @access private * @return void */ private function printTale() { if ($this->version == RSS2) { echo '</channel>',PHP_EOL,'</rss>'; } // do nothing for JSON } /** * Creates a single node as xml format * * @access private * @param string name of the tag * @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format * @param array Attributes(if any) in 'attrName' => 'attrValue' format * @return string formatted xml tag */ private function makeNode($tagName, $tagContent, $attributes = null) { if ($this->version == RSS2) { $nodeText = ''; $attrText = ''; if (is_array($attributes)) { foreach ($attributes as $key => $value) { $attrText .= " $key=\"$value\" "; } } $nodeText .= "<{$tagName}{$attrText}>"; if (is_array($tagContent)) { foreach ($tagContent as $key => $value) { $nodeText .= $this->makeNode($key, $value); } } else { //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent); $nodeText .= htmlspecialchars($tagContent); } //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>"; $nodeText .= "</$tagName>"; return $nodeText . PHP_EOL; } elseif ($this->version == JSON || $this->version == JSONP) { $tagName = (string)$tagName; $tagName = strtr($tagName, ':', '_'); $node = null; if (!$tagContent && is_array($attributes) && count($attributes)) { $node = array('@attributes' => $this->json_keys($attributes)); } else { if (is_array($tagContent)) { $node = $this->json_keys($tagContent); } else { $node = $tagContent; } } return $node; } return ''; // should not get here } private function json_keys(array $array) { $new = array(); foreach ($array as $key => $val) { if (is_string($key)) $key = strtr($key, ':', '_'); if (is_array($val)) { $new[$key] = $this->json_keys($val); } else { $new[$key] = $val; } } return $new; } /** * @desc Print channels * @access private * @return void */ private function printChannels() { //Start channel tag if ($this->version == RSS2) { echo '<channel>' . PHP_EOL; // add hubs foreach ($this->hubs as $hub) { //echo $this->makeNode('link', '', array('rel'=>'hub', 'href'=>$hub, 'xmlns'=>'http://www.w3.org/2005/Atom')); echo '<link rel="hub" href="'.htmlspecialchars($hub).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL; } // add self if (isset($this->self)) { //echo $this->makeNode('link', '', array('rel'=>'self', 'href'=>$this->self, 'xmlns'=>'http://www.w3.org/2005/Atom')); echo '<link rel="self" href="'.htmlspecialchars($this->self).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL; } //Print Items of channel foreach ($this->channels as $key => $value) { echo $this->makeNode($key, $value); } } elseif ($this->version == JSON || $this->version == JSONP) { $this->json->rss['channel'] = (object)$this->json_keys($this->channels); } } /** * Prints formatted feed items * * @access private * @return void */ private function printItems() { foreach ($this->items as $item) { $itemElements = $item->getElements(); echo $this->startItem(); if ($this->version == JSON || $this->version == JSONP) { $json_item = array(); } foreach ($itemElements as $thisElement) { foreach ($thisElement as $instance) { if ($this->version == RSS2) { echo $this->makeNode($instance['name'], $instance['content'], $instance['attributes']); } elseif ($this->version == JSON || $this->version == JSONP) { $_json_node = $this->makeNode($instance['name'], $instance['content'], $instance['attributes']); if (count($thisElement) > 1) { $json_item[strtr($instance['name'], ':', '_')][] = $_json_node; } else { $json_item[strtr($instance['name'], ':', '_')] = $_json_node; } } } } echo $this->endItem(); if ($this->version == JSON || $this->version == JSONP) { if (count($this->items) > 1) { $this->json->rss['channel']->item[] = $json_item; } else { $this->json->rss['channel']->item = $json_item; } } } } /** * Make the starting tag of channels * * @access private * @return void */ private function startItem() { if ($this->version == RSS2) { echo '<item>' . PHP_EOL; } // nothing for JSON } /** * Closes feed item tag * * @access private * @return void */ private function endItem() { if ($this->version == RSS2) { echo '</item>' . PHP_EOL; } // nothing for JSON } // End # private functions ---------------------------------------------- }