fkr/simplepie-bundle
Symfony bundle that integrates the SimplePie RSS/Atom feed parser, adding configuration and service container wiring so applications can fetch, parse, and cache feeds with minimal setup.
Still not a Laravel package. Nothing in v1.1.0 changes that: it's a Symfony bundle that registers one SimplePie service and exposes a two-knob config tree. In Laravel, use simplepie/simplepie directly (or laminas/laminas-feed, or feedio/feed-io) behind your own reader class — you'd be reimplementing the bundle's entire value proposition in about fifteen lines, so there is no reason to pull in Symfony DI scaffolding for it.
The release does matter if you're maintaining a Symfony app alongside your Laravel work, or advising a team that is. The old assessment said "Symfony2-era, likely won't resolve on Symfony 4+." That's now wrong. v1.1.0 supports Symfony 4, 5, and 6.
Minimal path on modern Symfony:
composer require fkr/simplepie-bundle. Confirm the resolved simplepie/simplepie version satisfies your PHP constraint — Symfony 6 support implies PHP 8.0+, and old SimplePie releases will not run cleanly there.DependencyInjection/Configuration.php (the whole config tree: cache location and duration) and Resources/config/services.xml (service id and class, and whether it's public or private by default). This is a very thin bundle; there is nothing else to learn.AppKernel entry. Register manually only if you've disabled Flex bundle discovery. Config goes in config/packages/fkr_simple_pie.yaml:
# confirm exact keys in Configuration.php
fkr_simple_pie:
cache_dir: '%kernel.cache_dir%/simplepie'
cache_time: 3600
services.xml for whether the id is the FQCN or a string alias) and map one feed to arrays in a command:
public function __construct(private \SimplePie $feed) {}
public function __invoke(): int
{
$this->feed->set_feed_url('https://example.com/feed.xml');
$this->feed->set_cache_duration(3600);
$this->feed->set_timeout(5);
if (!$this->feed->init()) {
$this->io->error('Feed failed: ' . $this->feed->error());
return self::FAILURE;
}
foreach ($this->feed->get_items() as $item) {
// map to array/DTO here
}
return self::SUCCESS;
}
Bottom line for a Laravel developer: the release removes the "won't install" objection but not the "wrong ecosystem" objection. Evaluate SimplePie or laminas-feed directly for Laravel; use the bundle only if you're actually inside a Symfony 4/5/6 app.
Wrap it in your own reader. Don't inject the bundle service into controllers and call SimplePie methods everywhere. Create App\Feed\FeedReader with read(string $url): array, map to a small FeedItem DTO or scalar array, and inject that. You get testability, a swap point for a maintained library later, and one place to enforce timeouts and error handling. This advice is unchanged and is the single most valuable thing in this assessment.
One instance per feed. SimplePie is stateful. The DI service is shared, so reusing it for two URLs in one request causes stale or merged state. Either construct a fresh new \SimplePie() and apply set_cache_location() / set_cache_duration() yourself (pulling those values from the bundle's config parameters), or clone carefully. Don't trust a shallow clone to isolate the cache object.
Autowire the class, not the id. On Symfony 4+ the old $this->get('fkr_simple_pie') idiom is gone — private services and no container-as-locator. Type-hint \SimplePie in the constructor and let autowiring resolve it, or alias the service in services.yaml if the bundle's id doesn't match the FQCN. Confirm which in services.xml before you write the constructor.
Merge deliberately. set_feed_url([$a, $b]) gives you a combined, date-sorted item list. If you need per-feed provenance, instantiate separately and tag the mapped items with the source URL rather than relying on SimplePie internals.
Never pass SimplePie objects to Twig. They're lazy and hold a lot of state. Map to arrays/DTOs in the controller or reader, then render. This also makes caching the rendered/mapped output trivial.
Two-layer caching. Let SimplePie handle HTTP-level feed caching, then optionally wrap your mapped output in Symfony's cache contracts keyed by URL + TTL. Cache successful parses; don't cache errors for long, or one bad fetch poisons the page for an hour.
Fetch out of band. Put feed fetching in a console command run by cron (app:feed:warm) and have controllers read warmed output. Remote feeds in the request path are a latency and availability liability — one slow origin stalls the page. With Symfony 4+ the Messenger component is now a reasonable alternative to cron for this; dispatch a WarmFeed message and let a worker handle it.
Config-driven feeds. Define feed URLs in config/services.yaml parameters, inject them into the reader, loop. Hardcoded URLs in controllers are the usual maintenance trap.
Testing with set_raw_data(). SimplePie can parse a string instead of fetching. Use fixture XML with set_raw_data(file_get_contents(...)) to test mapping without network access. Still the cleanest way to test this.
simplepie/simplepie constraint in this bundle's composer.json resolves to a version that actually runs on PHP 8 — pin it explicitly in your root composer.json rather than trusting the bundle's floor, and run your test suite before deploying.set_feed_url() twice on the injected service in one request gives surprising cache hits and merged items. Fresh instance per feed, every time. Symfony 6's container still shares this service by default.%kernel.cache_dir% is wiped by cache:clear, so your feed cache disappears on every deploy — fine for freshness, bad if you rely on it for rate limiting. Use a stable var/cache/feeds path if that matters, and make sure CLI (cron/messenger worker) and web users both have write access; mismatched users cause silent cache misses.%kernel.cache_dir% is per-environment. dev and prod have separate feed caches, so a feed that works locally can time out on first production hit. Warm it in the deploy step.init() can return false; error() returns a string. Always check and log. Malformed XML may emit PHP warnings, which Symfony 5+ converts to ErrorException by default in dev — wrap the init() call in the reader if that turns into a nuisance.set_timeout(5) and set_useragent(...) where feeds block default UAs. Never fetch synchronously from a user request without a short timeout.get_description() (usually safer) over get_content() unless you need full content. In Twig, |raw on get_content() is still an XSS decision you're making; consider Symfony's HtmlSanitizer component (5.1+) rather than trusting SimplePie's filter.get_date('U') or get_gmdate('U') and format yourself (or return \DateTimeImmutable) rather than relying on the default string.get_description(). Use get_item_tags() with the namespace URI, or get_enclosures() / get_links().$feed->error() say? Does get_permalink() return null (parsed but no channel)? Is the URL redirecting to an HTML error page? Use set_raw_data() to separate fetch failures from parse failures. In Symfony, also check bin/console debug:container SimplePie to confirm which service you actually got and that it's public/autowirable.FeedReader interface, swap the implementation to laminas/laminas-feed or feedio/feed-io. The wrapper pattern makes replacing this bundle a one-class change instead of a rewrite — and that's the whole point, since the Symfony 6 bump doesn't change the fact that SimplePie itself is the older half of this stack.How can I help you explore Laravel packages today?