Stars: 135
Forks: 51
Pull Requests: 106
Issues: 107
Watchers: 7
Last Updated: 2022-06-21 20:43:30
RSS and Atom Bundle for Symfony
License: MIT License
Languages: PHP
RssAtomBundle is a Bundle for Symfony made to easily access and deliver JSON / RSS / Atom feeds. It is built on top of feed-io and features:
Keep informed about new releases and incoming features : http://debril.org/category/rss-atom-bundle
You can try rss-atom-bundle through its Demo.
As a Symfony Bundle, RssAtomBundle must be installed using Composer. If you do not know Composer, please refer to its website: http://getcomposer.org/
Activate Symfony's contrib recipes and use Composer to require the bundle :
composer config extra.symfony.allow-contrib true
composer require debril/rss-atom-bundle
That's it. To check the installation, you can start your application and hit http://localhost:8000/rss in your browser. You should see a mock RSS stream.
Install the bundle using Composer :
composer require debril/rss-atom-bundle
Add the bundle's routing configuration in app/config/routing.yml :
rssatom:
resource: "@DebrilRssAtomBundle/Resources/config/routing.yml"
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(),
rss-atom-bundle is designed to read feeds across the internet and to publish your own using feed-io feed-io provides two interfaces, each one being dedicated to feed's consuming and publishing :
You can fetch a FeedIo\FeedIo
instance through dependency injection or the service container.
namespace App\Feed;
class Consumer
{
/**
* @type \FeedIo\FeedIo
*/
private $feedIo;
public function __construct(FeedIo $feedIo)
{
$this->feedIo = $feedIo;
}
}
Wherever you have access to the service container :
<?php
// get feedio
$feedIo = $this->container->get('feedio');
To read a feed you need to use the feedio
service which provides two methods for that : read()
and readSince()
. This service is based upon FeedIo.
read()
is designed to give a brand new Feed instance or any object of your own, as long as it implements the FeedInterface interface. It takes three arguments :
$url
: URL of the RSS/Atom feed you want to read (eg: http://php.net/feed.atom)$feed
(optional) : a FeedInterface instance. The default is a new \FeedIo\Feed
instance$modifiedSince
(optional) : the last time you read this feed. This is useful to fetch only the articles which were published after your last hit.With a \FeedIo\FeedIo
instance called $feedIo
:
<?php
// this date is used to fetch only the latest items
$modifiedSince = new \DateTime($date);
// the feed you want to read
$url = 'http://host.tld/feed';
// now fetch its (fresh) content
$feed = $feedIo->read($url, new \Acme\Entity\Feed, $modifiedSince)->getFeed();
foreach ( $feed as $item ) {
echo "item title : {$item->getTitle()} \n ";
// getMedias() returns enclosures if any
$medias = $item->getMedias();
}
?>
read()
fetches the feed hosted at $url
and removes items prior to $modifiedSince
. 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 $modifiedSince
is reached, it justs performs one hit and filters the response to keep only the fresh articles.
readSince()
helps you get a \FeedIo\Feed
without creating its instance :
<?php
// get feedio
$feedIo = $this->container->get('feedio');
// this date is used to fetch only the latest items
$modifiedSince = new \DateTime($date);
// the feed you want to read
$url = 'http://host.tld/feed';
// now fetch its (fresh) content
$feed = $feedIo->readSince($url, $modifiedSince)->getFeed();
?>
RssAtomBundle offers the ability to provide JSON/RSS/Atom feeds. The route will match the following pattern : /{format}/{contentId}
The request will be handled by StreamController
, according to the following steps :
Options
instance holding the request's parameters (contentId if it exists)getFeedContent(Options $options)
methodYou must give to RssAtomBundle the content you want it to display in the feed. For that, two steps :
FeedProviderInterface
. This class that we call a 'provider' will be in charge of building the feed.Your class just needs to implement the Debril\RssAtomBundle\Provider\FeedProviderInterface
interface, for instance :
<?php
# src/Feed/Provider.php
namespace App\Feed;
use FeedIo\Feed;
use FeedIo\FeedInterface;
use FeedIo\Feed\Item;
use Debril\RssAtomBundle\Provider\FeedContentProviderInterface;
class Provider implements FeedProviderInterface
{
/**
* @param array $options
* @return \FeedIo\FeedInterface
* @throws \Debril\RssAtomBundle\Exception\FeedNotFoundException
*/
public function getFeed(Request $request) : FeedInterface
{
// build the feed the way you want
$feed = new Feed();
$feed->setTitle('your title');
foreach($this->getItems() as $item ) {
$feed->add($item);
}
return $feed;
}
protected function getItems()
{
foreach($this->fetchFromStorage() as $storedItem) {
$item = new Item;
$item->setTitle($storedItem->getTitle());
// ...
yield $item;
}
}
protected function fetchFromStorage()
{
// query the database to fetch items
}
}
StreamController expects the getFeed()'s return value to be a FeedIo\FeedInterface
instance. It can be a FeedIo\Feed
or a class of your own and if so, your class MUST implement \FeedIo\FeedInterface
.
You can also start from this class to save some time : App\Feed\Provider.php
<?php
interface FeedInterface extends \Iterator, NodeInterface
{
/**
* This method MUST return the feed's full URL
* @return string
*/
public function getUrl();
/**
* @param string $url
* @return FeedInterface
*/
public function setUrl($url);
// Full source can be read in the repository .......
?>
Now, you need to configure the debril.rss_atom.provider
service with the provider's class in your project's services.yml :
# config/services.yaml
parameters:
debril.rss_atom.provider.class: 'App\Feed\Provider'
Or, if you need to build it with arguments, you can override debril.rss_atom.provider
's declaration :
# config/services.yaml
services:
debril.rss_atom.provider:
class: App\Feed\Provider
arguments: ["@logger", "@doctrine"]
That's it. Go to http://localhost:8000/atom, it should display your feed.
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 \FeedIo\FeedInterface
instance. Then, the controller properly turns the 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
The HTTP cache handling can be annoying during development process, you can skip it through configuration in your app/config/config.yml file :
# config/packages/rss_atom.yaml
debril_rss_atom:
force_refresh: true
This way, the StreamController
will always display your feed's content and return a 200 HTTP code.
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 Symfony AppCache or Varnish).
You can do so by setting private
parameter to true
in config:
# config/packages/rss_atom.yaml
debril_rss_atom:
private: true
Some feeds use date formats which are not compliant with the specifications. You can fix this by adding the format in your configuration
# config/packages/rss_atom.yaml
debril_rss_atom:
date_formats:
- 'Y/M/d'
If you need to customize the Content-Type header of your feed you can specify the value in your configuration
# config/packages/rss_atom.yaml
debril_rss_atom:
content_type_json: application/json
content_type_xml: application/xhtml+xml
Starting from version 4.1 rss-atom-bundle comes with feed-io 4 if your application depends on PHP 7.1+. If you need to use feed-io 3 instead for some reason, you can do it in composer.json
:
"debril/rss-atom-bundle": "^4.1",
"debril/feed-io": "~3.0",
It could happen that according to the order of the bundles registered in AppKernel
, this override procedures do not work properly. This happens when a bundle is registered before rss-atom-bundle
.
In this case, you should use the Symfony CompilerPass
as reported in the documentation.
Vendor/Bundle/VendorBundle.php
:
use Vendor\Bundle\DependencyInjection\Compiler\OverrideRssAtomBundleProviderCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class VendorBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new OverrideRssAtomBundleProviderCompilerPass());
}
}
and Vendor/Bundle/DependencyInjection/Compiler/OverrideRssAtomBundleProviderCompilerPass.php
:
use Vendor\Bundle\Provider\FeedProvider;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class OverrideRssAtomBundleProviderCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('debril.rss_atom.provider');
$definition->setClass(FeedProvider::class);
$definition->addArgument(new Reference('my.service1'));
$definition->addArgument(new Reference('my.service2'));
}
}
You can follow either services.xml
or CompilerPass
but with services, you have to pay attention to bundles registration order.
Do this if you want to contribute (and you're welcome to do so):
git clone https://github.com/alexdebril/rss-atom-bundle.git
composer.phar install --dev
You can run the unit test suites using the following command in the Bundle's source director:
bin/phpunit