A Simple PHP Class to Create RSS Feed

RSS (Really Simple Syndication) is the use of an XML document in order to share content across the Internet. An RSS document, aka “feed” includes full or summarized text, plus metadata such as publishing dates and authorship.

This post covers RSS version 2.0. In simple words, an rss feed consists of a CHANNEL and channel ITEMS. You can find RSS specifications at RSS 2.0 at Harvard Law or RSS Advisory Board. You can extend your feed structure using XML namespaces. Details here.

Nowadays, most website owners are sharing their site content using social media. So, many Facebook, Twitter, Google plus or other social media users are using the stream of their favorite social network to remain aware of website changes. Nevertheless, RSS remains the first choice for content syndication.

The code

Please, note that

$a_db argument is an associative array with database connection settings as following:

// database connection settings
$a_db = array(
        "db_server" => "localhost",
        "db_name" => "your_database",
        "db_user" => "your_user",
        "db_passwd" => "password",
);

$a_channel argument is an associative array with channel properties. Array keys use RSS specification terminology. If you do not want to use optional elements, just omit them. For example:

// RSS channel properties
$a_channel = array(
  "title" => "pontikis.net",
  "link" => "http://www.pontikis.net",
  "description" => "Tech blog & web labs",
  "language" => "en",  // optional
  "image_title" => "pontikis.net", // optional
  "image_link" => "http://www.pontikis.net", // optional
  "image_url" => "http://www.pontikis.net/feed/rss.png" // optional
);

Finally, you can create a full RSS feed with topic content ($full_feed = true), or a summarized one with a short description for each topic ($full_feed = false) and probably a picture.

Here is the code of the class.

<?php
/**
 * rss_feed (simple rss 2.0 feed creator php class)
 *
 * @author     Christos Pontikis http://pontikis.net
 * @copyright  Christos Pontikis
 * @license    MIT http://opensource.org/licenses/MIT
 * @version    0.1.0 (28 July 2013)
 *
 */
class rss_feed  {

        /**
         * Constructor
         *
         * @param array $a_db database settings
         * @param string $xmlns XML namespace
         * @param array $a_channel channel properties
         * @param string $site_url the URL of your site
         * @param string $site_name the name of your site
         * @param bool $full_feed flag for full feed (all topic content)
         */
        public function __construct($a_db, $xmlns, $a_channel, $site_url, $site_name, $full_feed = false) {
                // initialize
                $this->db_settings = $a_db;
                $this->xmlns = ($xmlns ? ' ' . $xmlns : '');
                $this->channel_properties = $a_channel;
                $this->site_url = $site_url;
                $this->site_name = $site_name;
                $this->full_feed = $full_feed;
        }

        /**
         * Generate RSS 2.0 feed
         *
         * @return string RSS 2.0 xml
         */
        public function create_feed() {

                $xml = '<?xml version="1.0" encoding="utf-8"?>' . "\n";

                $xml .= '<rss version="2.0"' . $this->xmlns . '>' . "\n";

                // channel required properties
                $xml .= '<channel>' . "\n";
                $xml .= '<title>' . $this->channel_properties["title"] . '</title>' . "\n";
                $xml .= '<link>' . $this->channel_properties["link"] . '</link>' . "\n";
                $xml .= '<description>' . $this->channel_properties["description"] . '</description>' . "\n";

                // channel optional properties
                if(array_key_exists("language", $this->channel_properties)) {
                        $xml .= '<language>' . $this->channel_properties["language"] . '</language>' . "\n";
                }
                if(array_key_exists("image_title", $this->channel_properties)) {
                        $xml .= '<image>' . "\n";
                        $xml .= '<title>' . $this->channel_properties["image_title"] . '</title>' . "\n";
                        $xml .= '<link>' . $this->channel_properties["image_link"] . '</link>' . "\n";
                        $xml .= '<url>' . $this->channel_properties["image_url"] . '</url>' . "\n";
                        $xml .= '</image>' . "\n";
                }

                // get RSS channel items
                $now =  date("YmdHis"); // get current time  // configure appropriately to your environment
                $rss_items = $this->get_feed_items($now);

                foreach($rss_items as $rss_item) {
                        $xml .= '<item>' . "\n";
                        $xml .= '<title>' . $rss_item['title'] . '</title>' . "\n";
                        $xml .= '<link>' . $rss_item['link'] . '</link>' . "\n";
                        $xml .= '<description>' . $rss_item['description'] . '</description>' . "\n";
                        $xml .= '<pubDate>' . $rss_item['pubDate'] . '</pubDate>' . "\n";
                        $xml .= '<category>' . $rss_item['category'] . '</category>' . "\n";
                        $xml .= '<source>' . $rss_item['source'] . '</source>' . "\n";

                        if($this->full_feed) {
                                $xml .= '<content:encoded>' . $rss_item['content'] . '</content:encoded>' . "\n";
                        }

                        $xml .= '</item>' . "\n";
                }

                $xml .= '</channel>';

                $xml .= '</rss>';

                return $xml;
        }


        /**
         * @param $rss_date
         * @param $rss_items_count
         * @internal param $rss_items
         * @return array
         */
        public function get_feed_items($rss_date, $rss_items_count = 10) {

                // connect to database
                $conn = new mysqli($this->db_settings["db_server"], $this->db_settings["db_user"], $this->db_settings["db_passwd"], $this->db_settings["db_name"]);

                // check connection
                if ($conn->connect_error) {
                        trigger_error('Database connection failed: '  . $conn->connect_error, E_USER_ERROR);
                }

                // create array with topic IDs
                $a_topic_ids = array();
                $sql = 'SELECT id FROM topics ' .
                        'WHERE date_published <= ' . "'" . $conn->real_escape_string($rss_date) . "'" .
                        'AND date_published IS NOT NULL ' .
                        'ORDER BY date_published DESC ' .
                        'LIMIT 0,' . $rss_items_count;

                $rs = $conn->query($sql);
                if($rs === false) {
                        $user_error = 'Wrong SQL: ' . $sql . '<br>' . 'Error: ' . $conn->errno . ' ' . $conn->error;
                        trigger_error($user_error, E_USER_ERROR);
                }
                $rs->data_seek(0);
                while($res = $rs->fetch_assoc()) {
                        array_push($a_topic_ids, $res['id']);
                }
                $rs->free();

                // get rss items according to http://www.rssboard.org/rss-specification
                $a_rss_items = array();
                $a_rss_item = array();
                $topic = array();
                foreach($a_topic_ids as $topic_id) {

                        // get topic properties
                        $sql='SELECT * FROM topics WHERE id=' . $topic_id;
                        $rs=$conn->query($sql);

                        if($rs === false) {
                                trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->error, E_USER_ERROR);
                        } else {
                                $rs->data_seek(0);
                                $topic = $rs->fetch_array(MYSQLI_ASSOC);
                        }

                        // title
                        $a_rss_item['title'] = $topic['title'];

                        // link
                        $a_rss_item['link'] = $this->site_url . '/' . $topic['url'];

                        // description
                        $a_rss_item['description'] = '';

                        if($topic['image']) {
                                $img_url = $this->site_url . $topic['image'];
                                $a_rss_item['description'] = '<img src="' . $img_url . '" hspace="5" vspace="5" align="left"/>';
                        }
                        $a_rss_item['description'] .= $topic['description'];

                        // pubdate -> configure appropriately to your environment
                        $date = new DateTime($topic["date_published"]);
                        $a_rss_item['pubDate'] = $date->format("D, d M Y H:i:s O");

                        // category
                        $a_rss_item['category'] = $topic["category"];

                        // source
                        $a_rss_item['source'] = $this->site_name;

                        if($this->full_feed) {
                                // content
                                $a_rss_item['content'] = '<![CDATA[' . $topic['topic_html'] .  ']]>';
                        }

                        array_push($a_rss_items, $a_rss_item);

                }

                return $a_rss_items;
        }

}

How to use the class

If your feed URL is something like http://www.your-site.com/feed, create an feed/index.php file with the following contents:

<?php
header('Content-type: application/xml');
require_once '/path/to/class/rss_feed.php'; // configure appropriately

// set more namespaces if you need them
$xmlns = '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:sy="http://purl.org/rss/1.0/modules/syndication/"';

// configure appropriately - pontikis.net is used as an example
$a_channel = array(
        "title" => "pontikis.net",
        "link" => "http://www.pontikis.net",
        "description" => "Tech blog & web labs",
        "language" => "en",
        "image_title" => "pontikis.net",
        "image_link" => "http://www.pontikis.net",
        "image_url" => "http://www.pontikis.net/feed/rss.png",
);
$site_url = 'http://www.pontikis.net'; // configure appropriately
$site_name = 'Tech blog & web labs'; // configure appropriately

$rss = new rss_feed($xmlns, $a_channel, $site_url, $site_name);
echo $rss->create_feed();
?>

Customize class to your environment

This class needs some kind of customization in order to work:

Database settings

You have to customize function get_feed_items() according to your database structure. In my case there is a table topics where site content is stored.

I am also using MySQLi, but, of course, you may use any other database.

Converting dates

I store dates as text. See this post for details. Probably, this is not your case, so you have to convert topic date according to W3C specifications for RSS (“D, d M Y H:i:s O” ).

See lines 157 – 159 in class code

        // pubdate -> configure appropriately to your environment
        $date = new DateTime($topic["date_published"]);
        $a_rss_item['pubDate'] = $date->format("D, d M Y H:i:s O");

Use memcached to reduce database usage

It is a good idea to use memcached in order to cache feed xml. So, your server does not need to execute a database query everytime rss feed is requested. To setup memcached with php, see this post.

To use memcached, you need to check if feed xml exists in your cache. Otherwise, you have to create it (once). For example:

<?php
// initialize memcached
$key = 'my_site_rss_feed';
$mc = new Memcached();
$mc->addServer("my_server_ip", 11211); // 11211 is common memcached port

// pull rss xml from memcached
$xml = $mc->get($key);

// if does not exist, create it
if(!$xml) {

        // create feed xml (see above -> How to use the class)
        $rss = new rss_feed($xmlns, $a_channel, $site_url, $site_name);
        $xml = $rss->create_feed();

        // push rss xml to memcached for later use
        $mc->set($key, $xml);
}
echo $xml;
?>

Of course, you have to delete cached rss feed, everytime your feed is updated. For example:

<?php
// initialize memcached
$key = 'my_site_rss_feed';
$mc = new Memcached();
$mc->addServer("my_server_ip", 11211); // 11211 is common memcached port

// delete rss xml from memcached
$mc->delete($key);
?>