RSS 2.0 Rendering Klasse
RSS 2.0 rendering class
It can be so simple to generate RSS 2.0 feeds in PHP. The Rss2Feed
class allows to render
one or more channels in one RSS feed. All you have to do is to "feed" it with you data by
overloading the onLoad()
method. The results can be saved in a .rss file or directly sent
to the Browser. The example show how:
Es kann recht einfach sein, in PHP RSS 2.0 Feeds zu erstellen - mit der Klasse Rss2Feed
.
Diese kann einen oder mehrere Feed-Channels in ein RSS-Feed rendern. Dazu muss sie lediglich
durch Überladen der Methode onLoad()
mit den verfügbaren Daten gefüttert werden. Die XML-Ausgabe
kann entweder in einer .rss-Datei gespeichert oder direkt zum Browser gesendet werden. Das
Beispiel zeigt, wie es geht:
Sample source code
Anwendungsbeispiel
<?php
include_once('swlib/swlib.class.php');
use sw\Rss2Feed;
use sw\OutputBuffer;
use sw\Tracer;
//
// Our class to customize the feed channel
//
class MyRss2Feed extends Rss2Feed {
// That's the method we have to overload:
protected function onLoad($identifier = '', $numOfItems = '') {
// Instead of loading items from a database, we generate
// a static array for this example:
return array(
'title' => 'CHANNEL TITLE',
'description' => 'Channel description',
'link' => '', // will be replaced with your $_SERVER[HTTP_HOST]
'items' => array(
array(
'title' => 'Item 1 title',
'description' => 'Item 1 description',
'link' => 'http://item1.link/',
),
array(
'title' => 'Item 2 title &', // this will be escaped ("&")
'description' => 'Item 2 description',
'link' => 'http://item2.link/',
),
array(
'title' => 'Item 2 title with an "ü"',
'description' => '', // No content
'link' => '', // link will be replaced
),
)
);
}
}
//
// Generate the feed with 100 items, only the default channel
//
try {
$rss = new MyRss2Feed();
$xml = $rss->renderFeed(array(), 100);
} catch (Exception $e) {
$exception = $e;
}
// Script output
if (!isset($exception) && isset($_GET['rss'])) {
// View the RSS in a reader
OutputBuffer::purge();
Tracer::disable();
print $xml;
} else {
// View the RSS XML code in the browser
print '<html><body><pre>';
print htmlspecialchars($xml);
print '</pre></body></html>';
}
Output
Ausgabe
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
<title>CHANNEL TITLE</title>
<link>http://wp.localhost/</link>
<description></description>
<generator>swlib</generator>
<ttl>60</ttl>
<item>
<title>Item 1 title</title>
<description>Item 1 description</description>
<link>http://item1.link/</link>
</item>
<item>
<title><![CDATA[Item 2 title &]]></title>
<description>Item 2 description</description>
<link>http://item2.link/</link>
</item>
<item>
<title><![CDATA[Item 2 title ü]]></title>
<description></description>
<link>http://wp.localhost/</link>
</item>
</channel>
</rss>
Class source code
Klassen-Quelltext
<?php
/**
* Provides rendering one or more RSS channels into a RSS XML text.
* @gpackage de.atwillys.sw.php.swLib
* @author Stefan Wilhelm
* @copyright Stefan Wilhelm, 2010
* @license GPL
* @version 1.0
*/
namespace sw;
class Rss2Feed {
/**
* That's how the channel specfication looks like:
* @staticvar array
*/
private static $dataTemplate = array(
'title' => '', // Main title if the feed, should contain the website url
'link' => '', // Link to the main web page of the channel
'description' => '', // Text description of the feed
'category' => '', // One or more category names
'language' => '', // Channel language
'copyright' => '', // Copyright information
'generator' => 'swlib', // Feed generator
'managingeditor' => '', // Email address of he managing editor
'webMaster' => '', // Email address of the admin
'pubDate' => '', // RFC822 time when the content was published
'lastbuilddate' => '', // RFC822 time when the channel was modified
'docs' => '', // RSS specification documentation link
'cloud' => '', // Cloud service interface provider
'ttl' => '60', // Time to live
'image' => array(// Channel image:
'url' => '', // Url to GIF, JPEG or PNG
'title' => '', // Like alt tag in HTML
'link' => '', // Page link (use link of the channel itself)
'width' => '', // Optional width, mad 144
'height' => '', // Optional height, mad 400
'description' => '', // Optional like HTML title=""
),
'rating' => '', //
'textinput' => array(// Input text box:
'title' => '', // The label of the Submit button in the text input area.
'description' => '', // Explains the text input area
'name' => '', // The name of the text object in the text input area
'link' => '' // The URL of the CGI script that processes text input requests
),
'skiphours' => '', // Hours to skip update: int 0 to 23
'skipdays' => '', // Days to skip update: Monday, Tuesday, Wednesday, ...
'items' => array(), // THESE ARE THE ITEMS OF THE CHANNEL
);
/**
* That's how the item specfication looks like:
* @staticvar array
*/
private static $itemTemplate = array(
'title' => '', // Title of the Item
'link' => '', // Link to the related HTML page
'description' => '', // Text description
'author' => '', // Author of the item
'category' => '', // Item category
'comments' => '', // URI to item comments
'source' => '', // RSS channel where the content of this item is taken from
'enclosure' => array(// Attached media data (array contents are XML attributes)
'url' => '', // Link to the resource
'type' => '', // MIME type
'length' => '' // Content length in bytes
),
'guid' => '', // Global unique identifier
'pubDate' => '', // RFC822 time when the item was published
);
/**
* The text encoding of the feed
* @var string
*/
private $encoding = "UTF-8";
/**
* The channel data retrieved from the database or other sources
* @var array
*/
private $data = array();
/**
* Items, extracted form the data array returned by onLoad.
* @var array
*/
public $items = array();
/**
* Loads the data which are necessary to render an RSS feed.
* OVERLOAD THIS FUNCTION TO CUSTOMIZE YOUR RSS CHANNELS.
* Returns a reference to the result array, which must contain the
* keys "title", "description" and "item"
*
* @param mixed $identifier
* @param int $numOfItems
* @return array
*/
protected function onLoad($identifier='', $numOfItems=0) {
return array();
}
/**
* XML text escaping
* @param string $text
* @return string
*/
private static function xmlEscape($text) {
if (empty($text) || trim($text) == '') { // !empty for array()/null
return '';
} else if (!preg_match('/([^\x01-\x7f]|[&<>])/', $text)) {
return $text;
} if (strpos($text, ']]>') === false) {
return "<![CDATA[$text]]>";
} else {
return str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $text);
}
}
/**
* Constructor
* @param string $encoding
*/
public function __construct($encoding="UTF-8") {
$this->encoding = $encoding;
}
/**
* Data access get
* @param string $name
* @return mixed
*/
public function & __get($name) {
$name = strtolower($name);
return isset($this->data[$name]) ? $this->data[$name] : '';
}
/**
* Data access set
* @param string $name
* @param mixed $value
*/
public function __set($name, $value) {
$name = strtolower($name);
if (!isset($this->data[$name])) {
throw new LException('No such RSS property to set');
} else {
$this->data[$name] = $value;
}
}
/**
* Loads the data using the overloadable onLoad() method.
* @param mixed $identifier
* @param int $numOfItems
*/
public function load($identifier='', $numOfItems=0) {
$this->items = null;
$this->data = array_change_key_case(array_merge(self::$dataTemplate, $this->onLoad($identifier, $numOfItems)), CASE_LOWER);
$this->items = &$this->data['items'];
unset($this->data['items']);
if ($this->title == '') {
throw new LException('RSS channel title must be specified');
}
if (empty($this->link)) {
$this->link = "http://{$_SERVER['HTTP_HOST']}/";
}
foreach ($this->items as $key => $item) {
$this->items[$key] = array_change_key_case($item, CASE_LOWER);
if (empty($item['title'])) {
Tracer::trace("RSS item $key removed, no title");
$this->items[$key] = false;
} else if (empty($item['link'])) {
Tracer::trace("RSS item $key ({$item['title']}) added channel link");
$this->items[$key]['link'] = $this->link;
}
}
if (count($this->items) == 0) {
throw new LException('No RSS channel items defined');
}
}
/**
* Renders the channel
* @return string
*/
public function renderChannel($numOfItems=0) {
$o = " <channel>\n";
foreach ($this->data as $tag => $value) {
if (!empty($value) || $tag == 'title' || $tag == 'description' || $tag == 'link') {
if (!is_array($value)) {
$o .= " <$tag>" . self::xmlEscape($value) . "</$tag>\n";
} else if (strlen(trim(implode('', $value), " \n\r\t")) > 0) {
$o .= " <$tag>";
foreach ($value as $itag => $ivalue) {
$o .= " <$itag>" . self::xmlEscape($ivalue) . "</$itag>\n";
}
$o .= " </$tag>";
}
}
}
$count = 0;
foreach ($this->items as $item) {
if ($numOfItems > 0 && ++$count > $numOfItems) {
break;
} else {
$o .= " <item>\n";
foreach ($item as $tag => $value) {
if (!is_array($value)) {
$o .= " <$tag>" . self::xmlEscape($value) . "</$tag>\n";
} else if (strlen(trim(implode('', $value), " \n\r\t")) > 0) {
if ($tag == 'enclosure') {
$o .= "<enclosure url=\"{$value['url']}\" length=\"{$value['length']}\" type=\"{$value['type']}\" />";
} else if (!empty($value) || $tag == 'title' || $tag == 'description' || $tag == 'link') {
$o .= " <$tag>";
foreach ($value as $itag => $ivalue) {
$o .= " <$itag>" . self::xmlEscape($ivalue) . "</$itag>\n";
}
$o .= " </$tag>\n";
}
}
}
$o .= " </item>\n";
}
}
$o .= " </channel>\n";
return $o;
}
/**
* Renders a list of channels in one feed
* @param array $channelIdentifiers
* @param int $numOfItems
* @return string
*/
public function renderFeed(array $channelIdentifiers=array(), $numOfItems=0) {
$o = '<?xml version="1.0" encoding="' . $this->encoding . '" ?>'
. "\n" . '<rss version="2.0"'
. "\n" . ' xmlns:content="http://purl.org/rss/1.0/modules/content/"'
. "\n" . ' xmlns:wfw="http://wellformedweb.org/CommentAPI/"'
. "\n" . ' xmlns:dc="http://purl.org/dc/elements/1.1/"'
. "\n" . ' xmlns:atom="http://www.w3.org/2005/Atom"'
. "\n" . ' xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"'
. "\n" . ' xmlns:slash="http://purl.org/rss/1.0/modules/slash/"'
. "\n" . " >\n";
foreach ($channelIdentifiers as $identifier) {
try {
$this->load($identifier, $numOfItems);
$o .= $this->renderChannel($numOfItems);
} catch (\Exception $e) {
Tracer::traceLException($e);
}
}
$o .= '</rss>';
return $o;
}
}