<?php

/** 
 * RSS Feed Class
 * @author franzz
 * @version 1.1
 * 
 * Input: 
 * - $asDesc: Array. Description of the feed: fields 'title', 'link' (optional), 'copyright', 'description', 'language', 'webmaster_mail'
 * - $asItems: Array. Feed item data: fields 'title', 'description', 'author', 'link', 'pub_date', 'guid'
 */
class Feed extends PhpObject {
	
	private $asDesc;
	private $asItems;
	
	public function __construct($asDesc, $asItems=array())
	{
		parent::__construct();
		if(!array_key_exists('link', $asDesc))
		{
			$asDesc['link'] = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'/rss';
		}
		$this->asDesc = $asDesc;
		array_walk($asItems, array($this, 'addItem'));
	}
	
	public function addItem($asItem)
	{
		$this->asItems[] = $asItem;
		return count($this->asItems) - 1;
	}
	
	public function removeItem($iItemId)
	{
		$bExist = array_key_exists($iItemId, $this->asItems);
		if($bExist) unset($this->asItems[$iItemId]);
		return $bExist;
	}
	
	private function getGlobalPubDate()
	{
		$iGlobalPubDate = 0;
		foreach($this->asItems as $asItem)
		{
			$iItemPubDate = strtotime($asItem['pub_date']);
			if($iItemPubDate>$iGlobalPubDate)
			{
				$iGlobalPubDate = $iItemPubDate;
			}
		}
		return self::cleanRss(self::getDate($iGlobalPubDate));
	}
	
	public function getFeed()
	{
			
		//feed header
		$sRssFeedHeader  = self::getHtml($this->asDesc['title'], 'title');
		$sRssFeedHeader .= self::getHtml($this->asDesc['link'], 'link');
		$sRssFeedHeader .= self::getHtml($this->asDesc['copyright'], 'copyright');
		$sRssFeedHeader .= self::getHtml($this->asDesc['description'], 'description');
		$sRssFeedHeader .= self::getHtml('', 'atom:link', '', '', array('href'=>$this->asDesc['link'], 'rel'=>'self', 'type'=>'application/atom+xml'), true);
		$sRssFeedHeader .= self::getHtml($this->asDesc['language'], 'language');
		$sRssFeedHeader .= self::getHtml($this->getGlobalPubDate(), 'pubDate');
		$sRssFeedHeader .= self::getHtml('Lutran.fr RSS Feed Generator', 'generator');
		$sRssFeedHeader .= self::getHtml($this->asDesc['webmaster_mail'].' (Webmaster)', 'webMaster');
		
		//feed items
		$asSortedItems = $this->rSortTimeMatrix($this->asItems, 'pub_date');
		$sItems = implode("\n", array_map(array($this, 'buildItem'), $asSortedItems));

		//Global Feed
		$sFeed = '<?xml version="1.0" encoding="ISO-8859-1" ?>';
		$sFeed .= self::getHtml(self::getHtml($sRssFeedHeader.$sItems, 'channel'), 'rss', '', '', array('version'=>'2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
		
		return $sFeed;
	}
	
	private static function getDate($sDate)
	{
		if(!is_numeric($sDate))
		{
			$sDate = strtotime($sDate);
		}
		return date('r', $sDate);
	}
	
	private function buildItem($asItem)
	{
		$sRssItem  = self::getHtml(self::cleanRss($asItem['title']), 'title');
		$sRssItem .= self::getHtml(self::cleanRss($asItem['author']), 'author');
		$sRssItem .= self::getHtml($asItem['link'], 'link');
		$sRssItem .= self::getHtml($asItem['category'], 'category');
		$sRssItem .= self::getHtml(self::cleanRss($asItem['description'].'.'), 'description');
		$sRssItem .= self::getHtml(self::getDate($asItem['pub_date']), 'pubDate');
		$sRssItem .= self::getHtml($asItem['guid'], 'guid', '', '', array('isPermaLink'=>'true'));
		return self::getHtml($sRssItem, 'item');
	}
	
	private static function getHtml($oText, $sTag, $sClass='', $sStyle='', $asExtraAttr=array(), $bAutoClose=false, $sInter='')
	{
		$sHtmlAttr = '';
		if($sClass!='')
		{
			$asExtraAttr['class'] = $sClass;
		}
		if($sStyle!='')
		{
			$asExtraAttr['style'] = $sStyle;
		}
		foreach($asExtraAttr as $sAttrName=>$sAttrValue)
		{
			$sHtmlAttr .= ' '.$sAttrName.'="'.$sAttrValue.'"';
		}
		if($bAutoClose)
		{
			$sHtml = self::encapsulate('', "\n".'<'.$sTag.$sHtmlAttr, ' />');
		}
		else
		{
			$sHtml = self::encapsulate($oText, "\n".'<'.$sTag.$sHtmlAttr.'>', '</'.$sTag.'>', $sInter);
		}
		return $sHtml;
	}
	
	private static function cleanRss($oText)
	{
		$asForbiddenChars = array('&', '<', '>', '"', "'");
		$asReplacementCode = array('&amp;', '&lt;', '&gt;', '&quot;', '&apos;');
		if(!is_array($oText))
		{
			return str_replace($asForbiddenChars, $asReplacementCode, $oText);
		}
		elseif(count($oText)>0)
		{
			$oTextKeys = array_map(array($this, 'cleanRss'), array_keys($oText));
			$oTextValues = array_map(array($this, 'cleanRss'), array_values($oText));
			return array_combine($oTextKeys, $oTextValues);
		}
		else
		{
			return $oText;
		}
	}
	
	private static function encapsulate($oText, $sPre='', $sPost=false, $sInter='')
	{
		if($sPost===false)
		{
			$sPost = $sPre;
		}
		if(is_array($oText))
		{
			$oText = implode($sPost.$sInter.$sPre, $oText);
		}
		return $sPre.$oText.$sPost;
	}
	
	private static function rSortTimeMatrix($asMatrix, $sTimeCol)
	{
		$asResult = array();
		foreach($asMatrix as $iRowId=>$asLine) $asKeys[$iRowId] = strtotime($asLine[$sTimeCol]);
		arsort($asKeys);
		foreach($asKeys as $iRowId=>$iTimeStamp) $asResult[$iRowId] = $asMatrix[$iRowId];
		return $asResult;
	}
}
?>