Handling RSS Feeds with PHP using Zend_Feed

Zend Framework is becoming a very comprehensive set of widely needed components for PHP development. As other frameworks offer similar components, one of Zend’s Framework greatest strengthens is the fact that you can use its components as stand alone components and not only as part of the MVC structure. In this post I will show how you can easily use it’s Zend_Feed component to merge feeds.

Recently I though of making one combines RSS feed of both of my blogs and my twitter updates. There are some RSS merging services out there, and there’s also Yahoo pipes which seems that it’s most useful ability is to do various RSS tweaking tasks. As part of my playing around with the Zend Framework, I’ve decided to make this merged RSS feed using the Zend_Feed component. Actually at the end I’ve realized that this merged feed idea is quite useless, but at least this post came out of it :-)

Most of the basic actions, like importing an RSS feed, creating an RSS feed and more are covered in the Zend Framework manual. In this post I will elaborate more on the more advanced topics, like sorting and merging RSS feeds but I will also go briefly over the more basic stuff as well.

So let’s begin.

First of all we need to load the RSS feed, this is being done quite easily:

   1: function loadFeed ($url) {
   2:     try {
   3:         $feed = Zend_Feed::import($url);
   4:     } catch (Zend_Feed_Exception $e) {
   5:         // feed import failed
   6:         return null;
   7:     }
   8:     return $feed;    
   9: }

Now that we have a feed we can read its properties, for example let’s print out the title:

   1: $feed = loadFeed ('http://www.arikfr.com/blog/feed/');
   2: echo $feed->title();

Or iterate over all of the feed entries and print their titles:

   1: foreach ($feed as $entry) {
   2:     echo $entry->title();
   3: }

(the Zend_Feed_Abstract, which both the Zend_Feed_Rss and Zend_Feed_Atom implement, is implementing the Iterator interface so we can use it in an foreach loop).

Now that we covered the basics let’s start constructing a new feed. You can make an RSS or Atom feed using Zend_Feed. We will create an RSS feed. Creating the feed is being done by constructing an array that will hold the new feed properties. There are mandatory fields and some optional fields. In this example I will cover all the mandatory ones (you can find the complete structure here):

   1: $merged_feed = array (
   2:     'title'     => 'ArikFr.com merged Feed',
   3:     'link'         => 'http://www.arikfr.com/merged_feed.php',
   4:     'charset'   => 'UTF-8', 
   5:     'entries'     => array (),
   6:     );

I’m leaving the entries field empty, and we will fill it later with entries from the feeds we want to merge. Now that we have the basic array read , we can create a Zend_Feed_Rss object from it:

   1: $rssFeedFromArray = Zend_Feed::importArray($merged_feed, 'rss');

And we can output it to the browser simple by calling the send method of the Zend_Feed_Rss object:

   1: $rssFeedFromArray->send();

Now all the browser will get is an empty RSS feed:

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
   3:   <channel>
   4:     <title><![CDATA[ArikFr.com merged Feed]]></title>
   5:     <link>http://www.arikfr.com/merged_feed.php</link>
   6:     <description></description>
   7:     <pubDate>Mon, 25 Feb 2008 20:55:37 +0000</pubDate>
   8:     <generator>Zend_Feed</generator>
   9:     <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  10:   </channel>
  11: </rss>

By now all we have done was quite simple and very basic. Now let’s start importing the entries from the feeds we want to merge:

   1: function getEntriesAsArray ($feed) {
   2:     $entries = array();
   3:     
   4:     foreach ($feed as $entry) {    
   5:         $entries[] = array (
   6:             'title' => $entry->title(),
   7:             'link' => $entry->link(),
   8:             'guid' => $entry->guid(),
   9:             'lastUpdate' =>strtotime($entry->pubDate()),
  10:             'description' => $entry->description(),
  11:             'pubDate' => $entry->pubDate(),
  12:             );
  13:     }
  14:     
  15:     return $entries;
  16: }

All what this function does is iterate over all of the entries of the given feed and return them as an array. I don’t really like this approach and it has some flaws, because not all feeds are having the same fields and sometimes it would be better just to copy the original node element to the new feed. As far as I know, it can’t be done (easily at least) with Zend_Feed (anyway I didn’t find a way, so if you know different - please drop me a line). Anyhow, the code sample above creates a feed entry with the most basic (and mandatory) fields.

Also notice that I convert the publish date of each RSS item to a timestamp using the strtotime function. This function can take a time or date values in various formatting and converts them back to timestamp - very useful.

Now if we just use it as is, meaning:

   1: $merged_feed['entries'] = array_merge (
   2:     getEntriesAsArray ($feed1), 
   3:     getEntriesAsArray ($feed2));

We will have all the entries from both blogs in the new feed, but they won’t be sorted. The basic array sort functions of PHP can easily sort numbers or strings, but if we want to sort something more complex we need to use the usort function. If you’re unfamiliar with the usort - it’s an array sorting function that takes a custom function for comparison of the items (see the manual page for more info). So let’s write our comparison function:

   1: function cmpEntries ($a , $b) {
   2:     $a_time = $a['lastUpdate'];
   3:     $b_time = $b['lastUpdate'];
   4:             
   5:     if ($a_time == $b_time) {
   6:         return 0;
   7:     }
   8:     return ($a_time > $b_time) ? -1 : 1;
   9: }

This function will sort the entries’ timestamp in a descending order - like the entries should be. Now that we have the comparison function, sorting the RSS entries is quite easy:

   1: usort ($merged_feed['entries'], 'cmpEntries');

And now we’re practically finished. The image below shows how the merged feed looks before the sort (left) and after the sort (right):

Before and after the sort

(I’m sorry that the time stamps are in Hebrew - that’s because of my computer’s locale settings. Just notice the time and dates).

There are some things we can improve in this example, like defining the number of entries we take from each post, adding to each post title the original blog name and maybe caching the entries from the feeds so we don’t have to read it each time the RSS readers pings the page (using Zend_Cahce). If anyone interested, I will cover this issues in next posts (just leave a comment).

It came out longer than I imagined, I guess that’s because of all the source code examples. Any comments, ideas and better practices are mostly welcomed!

Arik

Tags: , , , ,

Comments to “Handling RSS Feeds with PHP using Zend_Feed”

  1. אריק מתבטא » Blog Archive » טיפול ב-RSS בעזרת PHP ו - Zend_Feed Says:

    [...] שזה מעניין אותו - בבלוג באנגלית, כתבתי מדריך על איך למזג מס’ RSSים לכדי אחד בעזרת PHP וה-Zend Framework. //OBSTART:do_NOT_remove_this_comment [...]

  2. sole Says:

    Have you thought of using SimplePie? Or was this a Zend exercise? :)

  3. Arik Says:

    While making the tests for this post, I’ve tested various PHP RSS wrappers. I tried some from PEAR, and I remmeberd that I once hear of a very good PHP RSS library called something-pie. So I’ve stumbled on Magpie, which is very simple. So I did thought of SimplePie, but I forgot the right name :-( I will look into it. Thanks !
    (And it was also a Zend exercise so it’s not that bad)

  4. sole Says:

    Yeah, SimplePie is very good. I always recommend it. Zend exercising is always good too :-)

  5. Arik Says:

    I’ve just looked into their tutorials and it seems that it’s possible to do with Simplepie what wasn’t possible with others, which is outputting the original node content. I will try it later, and post an update on this post. Thanks again ! :-)

  6. Au Pair Says:

    Hello from Turkey! My Hebrew is not good but is seem like a very nice web site, thanks

  7. IT-Republik-php Says:

    RSS für Vortgeschrittene: Zend_Feed-Tutorial…

    RSS-Feeds zu allen möglichen Informationen, für den eigenen Blog oder die …

  8. Zend Framework Tutorials « PHP::Impact ( [str blog] ) Says:

    [...] Framework Zend_Feed classes - By Alex Netkachov Parsing tags with Zend Feed - By Henrik Sarvell Using Zend_Feed to Merge Multiple RSS Feeds - By Arik [...]

  9. Zend Framework教程大全 Says:

    [...] Framework Zend_Feed classes - By Alex Netkachov Parsing tags with Zend Feed - By Henrik Sarvell Using Zend_Feed to Merge Multiple RSS Feeds - By Arik Fraimovich Zend_Form Simple Zend_Form Example - By Rob Allen Simple Zend_Form File Upload [...]

  10. Murat Says:

    can u explain to zend_cache use ?

  11. Arik Says:

    Murat - did you try looking into the manual?

  12. Murat Says:

    yes ı looked out all tutorial but may be you can explain more specific

  13. Recopilacion de tutoriales Zend Framework | Stekl Says:

    [...] Framework Zend_Feed classes - Por Alex NetkachovParsing tags with Zend Feed - Por Henrik SarvellUsing Zend_Feed to Merge Multiple RSS Feeds - Por Arik [...]

  14. ronny stalker Says:

    Hi i came across your article whilst trying to work out how to ensure that my feeds are converted to latin 1. Is there a method in zend feed to set the character encoding of the output?

    I’m getting some weird characters appearing in my ISO 8859-1 (Latin-1) output because the original source is in UTF-8. any tips?

  15. Arik Says:

    You can use the iconv function to change encoding of the text. See more here: http://il2.php.net/manual/en/function.iconv.php

  16. typografia Says:

    You forgot to mention, that layout and view rendering should be disabled first:
    $this->_helper->layout->disableLayout();
    $this->getHelper('viewRenderer')->setNoRender(true);

  17. Arik Fraimovich Says:

    I only showed how to use Zend_Feed - it doesn't have to be used inside of a controller, and that's why I didn't mention the need to disable the layout.

Leave a Reply