Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

475 lines (435 sloc) 12.679 kb
<?php
/**
* EFeed Class file
* @author Antonio Ramirez
* @link http://www.ramirezcobos.com
*
*
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* EFeed is the RSS writer
*
* @author Antonio Ramirez <ramirez.cobos@gmail.com>
* @package rss
* @uses CComponent, CUrlValidator
* @throws CException
*/
class EFeed extends CComponent{
/**
*
* supported Feed formats
*
* @var string RSS1
* @var string RSS2
* @var string ATOM
*/
const RSS1 = 'RSS1';
const RSS2 = 'RSS2';
const ATOM = 'Atom';
/**
*
* Holds all information and elements
* to generate the feed
* @var CMap $feedElements
*/
private $feedElements;
/**
*
* Holds stylesheet associated to the feed
* http://www.w3.org/TR/xml-stylesheet/#dt-xml-stylesheet
*/
private $stylesheets = array();
/**
*
* Type of Feed Format
* @var string $type
*/
private $type;
/**
* Constructor
*
* @param constant the type constant (RSS1/RSS2/ATOM).
*/
function __construct($type = self::RSS2)
{
if( $type != self::RSS1 && $type != self::RSS2 && $type != self::ATOM )
throw new CException( Yii::t('EFeed', 'Feed version not supported') );
$this->type = $type;
// Initiate Feed holder
$this->feedElements = new CMap();
// Setting default value for essential channel elements
$this->addChannelTag('title', $this->type. ' Feed');
$this->addChannelTag('link', 'http://www.ramirezcobos.com/' );
// Tag elements that we need to CDATA encode
$this->feedElements->add('CDATAEncoded', array('description', 'content:encoded', 'summary') );
}
/**
*
* Adds stylesheet support
* @param array $htmlOptions
*/
public function addStylesheetTag( $htmlOptions )
{
if( !is_array( $htmlOptions ))
throw new CException( Yii::t( 'EFeed', __FUNCTION__.' parameter must be an array.' ) );
$this->stylesheets[] = '<?xml-stylesheet '.CHtml::renderAttributes($htmlOptions). ' ?>';
}
/**
*
* Adds a channel element
* @param string $tag name of the element
* @param string $content of the element
*/
public function addChannelTag( $tag, $content ){
if( null === $this->feedElements->itemAt('channels') )
$this->feedElements->add('channels', new CMap());
$this->feedElements->itemAt('channels')->add( $tag, $content );
}
/**
*
* Adds an array of channel elements
* They should be on the format:
* <pre>
* array('tagname'=>'tagcontent')
* </pre>
* @param unknown_type $tags
* @throws CException
*/
public function addChannelTagsArray( $tags ){
if( !is_array( $tags ) )
throw new CException( Yii::t( 'EFeed', __FUNCTION__.' parameter must be an array.' ) );
foreach( $tags as $tag=>$content ){
$this->addChannelTag($tag, $content);
}
}
/**
* RSS1, RSS2 or ATOM
* @return EFeedItemAbstract Item
*/
public function createNewItem( ){
// create EFeedItem based on selected version type
$class = "EFeedItem".$this->type;
return new $class;
}
/**
* Property setter the 'title' channel element
*
* @param string value of 'title' channel tag
*/
public function setTitle($title)
{
$this->addChannelTag('title', $title);
}
/**
*
* Property getter 'title'
*
* @return value of title channel tag | null
*/
public function getTitle( ){
if( null !== $this->feedElements->itemAt('channels')){
return $this->feedElements->itemAt('channels')->itemAt('title');
}
return null;
}
/**
* Property setter 'description' channel element
*
* @param string value of 'description' channel tag
*/
public function setDescription($description)
{
$this->addChannelTag('description', $description);
}
/**
*
* Property getter 'description'
*
* @return value of description channel tag | null
*/
public function getDescription( ){
if( null !== $this->feedElements->itemAt('channels')){
return $this->feedElements->itemAt('channels')->itemAt('description');
}
return null;
}
/**
* Property setter 'link' channel element
*
* @param string value of 'link' channel tag
* @throws CException
*/
public function setLink($link)
{
$validator = new CUrlValidator();
if(!$validator->validateValue($link))
throw new CException( Yii::t('EFeed', $link. ' does not seem to be a valid URL') );
$this->addChannelTag('link', $link);
}
/**
*
* Property getter 'link'
*
* @return value of link channel tag | null
*/
public function getLink( ){
if( null !== $this->feedElements->itemAt('channels')){
return $this->feedElements->itemAt('channels')->itemAt('link');
}
return null;
}
/**
* Set the 'image' channel element
*
* Cannot be used as property setter
*
* @param string title of image
* @param string link url of the image
* @param string path url of the image
*/
public function setImage($title, $link, $url)
{
$validator = new CUrlValidator();
if(!$validator->validateValue($link))
throw new CException( Yii::t('EFeed', $link. ' does not seem to be a valid URL') );
$this->addChannelTag('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
}
/**
*
* Property getter image
* @return value of the image channel tag | null
*/
public function getImage(){
if( null !== $this->feedElements->itemAt('channels'))
return $this->feedElements->itemAt('channels')->itemAt('image');
return null;
}
/**
*
* Property setter the 'about' RSS 1.0 channel element
*
* @param string value of 'about' channel tag
*/
public function setRSS1ChannelAbout($url)
{
$validator = new CUrlValidator();
if(!$validator->validateValue($url))
throw new CException( Yii::t('EFeed', $url. ' does not seem to be a valid URL') );
$this->addChannelTag('ChannelAbout', $url);
}
/**
*
* Property getter the 'about' RSS 1.0 channel
* @return value of 'about' channel tag | null
*/
public function getRSS1ChannelAbout(){
if( null !== $this->feedElements->itemAt('channels'))
return $this->feedElements->itemAt('channels')->itemAt('ChannelAbout');
return null;
}
/**
*
* Add a FeedItem to the main class
*
* @param object instance of EFeedItemAbstract class
*/
public function addItem(EFeedItemAbstract $item)
{
if( null === $this->feedElements->itemAt('items') )
$this->feedElements->add( 'items', new CTypedList('EFeedItemAbstract') );
$this->feedElements->itemAt('items')->add( $item );
}
/**
* Generates an UUID
*
* @author Anis uddin Ahmad <admin@ajaxray.com>
* @param string an optional prefix
* @return string the formated uuid
*/
public static function uuid($key = null, $prefix = '')
{
$key = ($key == null)? uniqid(rand()) : $key;
$chars = md5($key);
$uuid = substr($chars,0,8) . '-';
$uuid .= substr($chars,8,4) . '-';
$uuid .= substr($chars,12,4) . '-';
$uuid .= substr($chars,16,4) . '-';
$uuid .= substr($chars,20,12);
return $prefix . $uuid;
}
/**
*
* Generates the Feed
*/
public function generateFeed(){
header("Content-type: text/xml; charset=utf-8");
$this->renderHead();
$this->renderChannels();
$this->renderItems();
$this->renderBottom();
}
/**
*
* Prints the xml and rss namespace
*
*/
private function renderHead()
{
$head = '<?xml version="1.0" encoding="utf-8"?>' . PHP_EOL;
if(!empty($this->stylesheets))
$head .= implode (PHP_EOL, $this->stylesheets);
if($this->type == self::RSS2)
{
$head .= CHtml::openTag('rss',array(
"version"=>"2.0",
"xmlns:content"=>"http://purl.org/rss/1.0/modules/content/",
"xmlns:wfw"=>"http://wellformedweb.org/CommentAPI/",
"xmlns:atom"=>"http://www.w3.org/2005/Atom")).PHP_EOL;
}
elseif($this->type == self::RSS1)
{
$head .= CHtml::openTag('rdf:RDF',array(
"xmlns:rdf"=>"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"xmlns"=>"http://purl.org/rss/1.0/",
"xmlns:dc"=>"http://purl.org/dc/elements/1.1/",
"xmlns:atom"=>"http://www.w3.org/2005/Atom"
)).PHP_EOL;
}
else if($this->type == self::ATOM)
{
$head .= CHtml::openTag('feed', array("xmlns"=>"http://www.w3.org/2005/Atom")).PHP_EOL;
}
echo $head;
}
/**
*
* Prints the xml closing tags
*
*/
private function renderBottom()
{
if($this->type == self::RSS2)
{
echo CHtml::closeTag('channel');
echo CHtml::closeTag('rss');
}
elseif($this->type == self::RSS1)
{
echo CHtml::closeTag('rdf:RDF');
}
else if($this->type == self::ATOM)
{
echo CHtml::closeTag('feed');
}
}
/**
*
* Prints the channels of the xml document
* @throws CException
*/
private function renderChannels(){
switch ($this->type)
{
case self::RSS2:
echo '<channel>' . PHP_EOL;
break;
case self::RSS1:
if(null !== $this->RSS1ChannelAbout )
echo CHtml::openTag('channel',array('rdf:about'=>$this->RSS1ChannelAbout )) . PHP_EOL;
else
echo CHtml::openTag('channel',array('rdf:about'=>$this->link)) . PHP_EOL;
break;
}
// Printing channel items
foreach ($this->feedElements->itemAt('channels') as $key => $value)
{
if($this->type == self::ATOM && $key == 'link')
{
// ATOM prints link element as href attribute
echo $this->makeNode($key,'',array('href'=>$value));
// And add the id for ATOM
echo $this->makeNode('id',$this->uuid($value,'urn:uuid:'));
}
elseif ($this->type == self::RSS2 && $key == 'atom:link')
{
echo $this->makeNode($key,'',array('rel'=>'self', 'href'=>$value, 'type'=>'application/rss+xml'));
}
else
{
echo $this->makeNode($key, $value);
}
}
// RSS 1.0 have special tag <rdf:Seq> with channel
if($this->type == self::RSS1)
{
if( null === $this->feedElements->itemAt('items') )
throw new CException( Yii::t('EFeed', 'No items have been set') );
echo "<items>" . PHP_EOL . "<rdf:Seq>" . PHP_EOL;
foreach ($this->feedElements->itemAt('items') as $item)
{
$tag = $item->link;
if(null === $tag )
throw new CException( Yii::t('EFeed', 'For RSS 1.0 specifications link element should be add per item') );
echo CHtml::tag('rdf:li',array('resource'=>$tag->content ),true).PHP_EOL;
}
echo "</rdf:Seq>" . PHP_EOL . "</items>" . PHP_EOL;
echo CHtml::closeTag('channel') . PHP_EOL;
}
}
/**
*
* Prints feed items
* @throws CException
*/
private function renderItems()
{
if(null === $this->feedElements->itemAt('items'))
throw new CException( Yii::t('EFeed', 'No feed items configured') );
foreach ($this->feedElements->itemAt('items') as $item)
echo $item->getNode();
}
/**
*
* 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 = array())
{
$node = '';
if( is_array($tagContent) && $this->type == self::RSS1 )
$attributes['rdf:parseType']="Resource";
if( in_array($tagName, $this->feedElements->itemAt('CDATAEncoded')) )
{
if($this->type == self::ATOM)
$attributes['type']="html";
$node .= CHtml::openTag($tagName,$attributes). '<![CDATA[';
}
else
$node .= CHtml::openTag($tagName, $attributes);
if(is_array( $tagContent ))
{
foreach ($tagContent as $tag=>$content)
$node .= $this->makeNode($tag, $content);
}
else
$node .= in_array($tagName, $this->feedElements->itemAt('CDATAEncoded')) ? $tagContent : CHtml::encode($tagContent);
$node .= in_array($tagName, $this->feedElements->itemAt('CDATAEncoded'))? PHP_EOL.']]>' : '';
$node .= CHtml::closeTag($tagName);
return $node.PHP_EOL;
}
}
Jump to Line
Something went wrong with that request. Please try again.