Developed with love by KnpLabs Hire us for your project!


by alexdebril

RSS and Atom Bundle for Symfony 2

RssAtomBundle - Read and Build Atom/RSS feeds

Latest Stable Version
Build Status
Scrutinizer Code Quality
Code Coverage

RssAtomBundle is a Bundle for Symfony 2 made to easily access and deliver RSS / Atom feeds. It features:

  • Detection of the feed format (RSS / Atom)
  • enclosures support
  • A generic StreamController built to write all your feeds. This controller is able to send a 304 HTTP Code if the feed didn't change since the last visit
  • HTTP Headers support when reading feeds in order to save network traffic
  • Content filtering to fetch only the newest items
  • multiple feeds writing
  • Ability to use doctrine as a data source

Keep informed about about new releases and incoming features :

All classes are heavily tested using PHPUnit.



As a Symfony 2 Bundle, RssAtomBundle must be installed using Composer. If you do not know Composer, please refer to its website:

Installation in a Symfony 2 project

This is the most common way if you want to add RssAtomBundle into an existing project.
Edit composer.json and add the following line in the "require" section:

"debril/rss-atom-bundle": "1.6"

Ask Composer to install it:

composer.phar update debril/rss-atom-bundle

Edit your app/AppKernel.php to register the bundle in the registerBundles() method as above:

class AppKernel extends Kernel

    public function registerBundles()
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            // ...
            // register the bundle here
            new Debril\RssAtomBundle\DebrilRssAtomBundle(),

Then add the bundle's routing configuration in app/config/routing.yml :

    resource: @DebrilRssAtomBundle/Resources/config/routing.xml

Fetching the repository

Do this if you want to contribute (and you're welcome to do so):

git clone

composer.phar install --dev

Unit Testing

You can run the unit test suites using the following command in the Bundle's source director:



rss-atom-bundle is designed to read feeds across the internet and to publish your own. It provides two sets of interfaces, each one being dedicated to feed's consuming or publishing :

Feed Reading

To read a feed you need to use the debril.reader service which provides two methods for that : getFeedContent() and readFeed(). This service is based upon the FeedReader class.

using getFeedContent()

getFeedContent() is designed to give a brand new FeedContent instance or any object of your own, as long as it implements the FeedInInterface interface. It takes two arguments :

  • $url : URL of the RSS/Atom feed you want to read (eg:
  • $date : the last time you read this feed. This is useful to fetch only the articles which were published after your last hit.

Wherever you have access to the service container :
// fetch the FeedReader
$reader = $this->container->get('debril.reader');

// this date is used to fetch only the latest items
$date = new \DateTime($unmodifiedSince);

// the feed you want to read
$url = 'http://host.tld/feed';

// now fetch its (fresh) content
$feed = $reader->getFeedContent($url, $date);

// the $content object contains as many Item instances as you have fresh articles in the feed
$items = $feed->getItems();

foreach ( $items as $item ) {
    // getMedias() returns enclosures if any
    $medias = $item->getMedias();

getFeedContent()fetches the feed hosted at$urland removes items prior to$date. If it is the first time you read this feed, then you must specify a date far enough in the past to keep all the items. This method does not loop until the$date` is reached, it justs performs one hit and filters the response to keep only the fresh articles.

If you need more information, please visit the Reading Feeds section on the wiki

Providing feeds

RssAtomBundle offers the ability to provide RSS/Atom feeds. The route will match the following pattern : /{format}/{contentId}

  • {format} must be "rss" or "atom" (or whatever you want if you add the good routing rule in routing.yml)
  • {contentId} is an optional argument. Use it you have several feeds

The request will be handled by StreamController, according to the following steps :

  • 1 : grabs the ModifiedSince header if it exists
  • 2 : creates an Options instance holding the request's parameters (contentId if it exists)
  • 3 : gets the provider defined in services.xml and calls the getFeedContent(Options $options) method
  • 4 : compare the feed's LastModified property with the ModifiedSince header
  • 5 : if LastModified is prior or equal to ModifiedSince then the response contains only a "NotModified" header and the 304 code. Otherwise, the stream is built and sent to the client

StreamController expects the getFeedContent()'s return value to be a FeedOutInterface instance. It can be a Debril\RssAtomBundle\Protocol\Parser\FeedContent or a class you wrote and if so, your class MUST implement the FeedOutInterface interface.

interface FeedOutInterface

     * Atom : feed.updated <feed><updated>
     * Rss  : <rss><channel><lastBuildDate>
     * @return \DateTime
    public function getLastModified();

     * Atom : feed.title <feed><title>
     * Rss  : <rss><channel><title>
     * @return string
    public function getTitle();

    // Full source can be read in the repository .......

Now, how to plug the StreamController with the provider of your choice ? The easiest way is to override the debril.provider.default service with your own in services.xml :

<service id="debril.provider.default" class="Namespace\Of\Your\Class">
    <argument type="service" id="doctrine" />

Your class just needs to implement the FeedContentProviderInterface interface :

interface FeedContentProviderInterface
     * @param \Symfony\Component\OptionsResolver $params
     * @return \Debril\RssAtomBundle\Protocol\FeedOutInterface
     * @throws \Debril\RssAtomBundle\Protocol\FeedNotFoundException
    public function getFeedContent(Options $options);

If the reclaimed feed does not exist, you just need to throw a FeedNotFoundException to make the StreamController answer with a 404 error. Otherwise, getFeedContent(Options $options) must return a FeedContent instance, which will return an array of Item objects through getItems(). Then, the controller uses a FeedFormatter object to properly turn your FeedContent object into a XML stream.

More information on the FeedContentProviderInterface interface and how to interface rss-atom-bundle directly with doctrine can be found in the Providing Feeds section

Useful Tips

Skipping 304 HTTP Code

The HTTP cache handling can be annoying during development process, you can skip it through configuration in your app/config/parameters.yml file :

    force_refresh:     true

This way, the StreamController will always display your feed's content and return a 200 HTTP code.

Choosing your own provider

Need to keep the existing routes and add one mapped to a different FeedProvider ? add it own in your routing file :

    <route id="your_route_name" pattern="/your/route/{contentId}">
        <default key="_controller">DebrilRssAtomBundle:Stream:index</default>
        <default key="format">rss</default>
        <default key="source">your.provider.service</default>

The source parameter must contain a valid service name defined in your application.

Private feeds

You may have private feeds, user-specific or behind some authentication.

In that case, you don't want to Cache-Control: public header to be added, not to have your feed cached by a reverse-proxy (such as Symfony2 AppCache or Varnish).

You can do so by setting private parameter to false in config:

    private: true


RSS and Atom support for Symfony 2
Copyright (C) 2013 Alexandre Debril

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <>.

For full license : <>

# Change cache headers so the RSS feed is not cached by public caches (like reverse-proxies...).
private: false
date_formats: []
  • Merge pull request #75 from romaricdrigon/feat-private
    By alexdebril, 2 years ago
  • Supporting private feeds
    By romaricdrigon, 2 years ago
  • issue #72 : remove get/setLenght (now I'll remember it's length)
    By alexdebril, 2 years ago
  • Merge pull request #74 from Soullivaneuh/code-cleanup
    By alexdebril, 2 years ago
  • Merge pull request #73 from romaricdrigon/feat-typo
    By alexdebril, 2 years ago
  • Merge pull request #72 from romaricdrigon/feat-media
    By alexdebril, 2 years ago
  • Merge pull request #71 from Soullivaneuh/patch-1
    By alexdebril, 2 years ago
  • Fixed typo (2)
    By romaricdrigon, 2 years ago
  • Code and phpdoc cleanup
    By Soullivaneuh, 2 years ago
  • Fixed typo in routing file path
    By romaricdrigon, 2 years ago
  • Fixed: using PHP 5.3 array notation
    By romaricdrigon, 2 years ago
  • Removed useless check
    By romaricdrigon, 2 years ago
  • Dumping medias as enclosure links to Atom Feed
    By romaricdrigon, 2 years ago
  • Dumping medias as enclosure to RSS Feed
    By romaricdrigon, 2 years ago
  • Added accessors fixing a typo
    By romaricdrigon, 2 years ago
  • Packagist branch-alias option
    By Soullivaneuh, 2 years ago
  • Merge pull request #66 from Soullivaneuh/exceptions-namespaces
    By alexdebril, 2 years ago
  • Merge branch 'issue-67'
    By alexdebril, 2 years ago
  • restored test configuration
    By alexdebril, 2 years ago
  • remove unused routing annotations
    By alexdebril, 2 years ago
  • issue #67 : the enclosure URL must be detected because of non-standards feeds
    By alexdebril, 2 years ago
  • added routing configuration
    By alexdebril, 2 years ago
  • use bundle's configuration
    By alexdebril, 2 years ago
  • Move all exceptions on Exception namespace
    By Soullivaneuh, 2 years ago
  • removed migration notice
    By alexdebril, 2 years ago
  • Removed use statement
    By alexdebril, 2 years ago
  • Symfony 3.0 compatbility
    By alexdebril, 2 years ago
  • Merge pull request #64 from Soullivaneuh/exception
    By alexdebril, 2 years ago
  • Common exception classes
    By Soullivaneuh, 2 years ago
  • Added SensioLabs insight
    By alexdebril, 2 years ago