roumen/feed
A Laravel package for generating RSS and Atom feeds from application data. Build custom feeds with a fluent API, caching, and clean XML output for blogs, news, podcasts, and other dynamic content.
Architecture fit. roumen/feed remains a presentation-layer utility, not a domain component. The core is framework-agnostic with an optional Laravel service provider and facade, so it slots in as a leaf dependency with no assumptions about persistence, ORM, or event model. We own the mapping from Eloquent models to feed items; the package owns serialization and views. That seam is unchanged by v1.0.3, which touches only the serializer's escaping path. It can still be introduced behind a single controller or console command and removed without touching domain code.
Integration feasibility. Low effort. Composer install, optional facade alias, publish config and Blade views, one route and one item-mapping loop. A first working feed is 1–3 days for a Laravel developer, most of it spent on view markup and content formatting. The real cost remains downstream: item schema, HTML sanitization, and cache invalidation. v1.0.3 adds roughly an hour of work: bump the constraint, deploy, re-validate output, and confirm no ]]>-bearing content was previously producing truncated or injected XML.
Technical risk. Moderate-low, and the profile shifted slightly with this release — in a favorable direction on responsiveness, and with one concrete confirmation about the escaping model.
What v1.0.3 changes. The only substantive change is a fix for CDATA terminator escaping ("prevent XML injection", PR #95); the remaining six commits are Dependabot bumps to actions/checkout, codecov-action, and the codecov action. Three implications:
<![CDATA[...]]>, which means raw HTML pass-through is by design. That makes our sanitization obligation non-optional and non-delegable: the package escapes the CDATA delimiter but does not sanitize the payload. It also means the earlier open question about entity encoding vs. CDATA is resolved in the direction of "we must sanitize before handing content over."]]> in item content allowed a consumer to break out of the CDATA section and inject arbitrary XML into the document. Exposure for us is bounded: only feeds that carry user-generated or third-party-authored content (comments, forum posts, aggregated partner copy, imported descriptions) are realistically attackable. Feeds sourced from editorially controlled CMS fields are low risk. We should not treat this as theoretical for any feed with an untrusted content field.What is still unverified. The fix addresses the CDATA delimiter specifically. It does not evidence handling of invalid UTF-8, raw control characters, or other XML-illegal code points — still an open question. The dependents count, the CI matrix for PHP 8.2+/Laravel 11–12, and the streaming-vs-buffering behavior are all untouched by this release. Governance signals are unchanged: single maintainer, MIT, community-only support.
Version-line caveat. The release is tagged v1.0.3, while the current assessment describes a 4.x line and a laravelium/feed predecessor. Reconcile this before pinning: confirm whether v1.0.x is the current major for the package we intend to consume, or whether this tag belongs to a parallel/renamed line. A wrong-major pin here is a silent-failure risk, not a loud one.
Key questions for the maintainer/spike (updated).
media:/iTunes podcast tags, or JSON Feed exist?1.0.x or 4.x — and what is the relationship between the two tags?guid/id values, or is that entirely our responsibility?Stack fit. Unchanged. Laravel 10–12 on PHP 8.1+ with Blade as the templating engine is the happy path; Symfony and plain-PHP use the same core with a different view adapter. Coexists fine with spatie/laravel-feed, but running both is unjustified.
Migration path. Two distinct moves, now ordered by risk:
^1.0.3, not ^1.0) so no environment can resolve a vulnerable version. This is a patch release with no API surface change described, so it is a routine bump — but because it is a security fix, treat it as a scheduled deploy rather than an auto-merge, and re-validate generated XML after. Where a lockfile pins an older patch, refresh it explicitly. If we have any feed with user-generated content, add a targeted check for ]]> sequences in stored content before and after.roumen/feed or laravelium/feed, expect namespace and config-shape changes; budget half a day and diff the generated XML byte-for-byte against old output. From spatie/laravel-feed, it is mostly re-pointing views and rebuilding item mapping — spatie auto-discovers feeds via config, this package gives explicit control.Compatibility. The coupling surface is the service provider and Blade view, so Laravel majors are unlikely to break it. Pin the major (^1.0.3 or the correct 4.x equivalent once reconciled) and let Renovate/Dependabot surface bumps for review rather than auto-merging — precisely the policy that would have caught this one.
Sequencing.
FeedItem DTO and a single mapper. Enforce sanitization and timezone normalization in the mapper, so no caller can bypass it — this is the durable fix for the class of bug v1.0.3 patched.]]>, invalid UTF-8, and control characters in the content field.composer audit into CI so a malformed feed or known-vulnerable dependency fails the build. Roll out remaining feeds.Maintenance. Low ongoing code churn; bus factor still one, but the v1.0.3 response is the first real evidence that the single maintainer ships security fixes promptly and links the PR for review. Keep the existing mitigations: pinned major, mapping logic in our own DTO so an exit is a day's work, named internal owner. Add one: subscribe to GitHub security advisories for the repo so the next fix is inbound rather than discovered.
Support. Community-only — GitHub issues, no SLA, no commercial backing. Unchanged, and unchanged in acceptability: fine for a non-critical output surface; not acceptable for contractual distribution (podcast distribution, partner syndication) without a documented fallback. The absence of a published advisory for a security fix is a mild negative on process maturity even though the fix itself shipped.
Scaling. Feed generation is cheap but O(n) in item count, and readers consume only the head. Cap output at 20–50 items with a hard limit on the query — still the single most effective scaling and safety control. Cache every feed; Redis with tag-based invalidation over file cache in multi-node deployments, where file cache drifts per node. With caching and a capped item count, feed endpoints are effectively free.
Failure modes to instrument.
]]>.Cache::remember with a lock or jittered TTL.Ramp-up. Shallow. A mid-level Laravel developer can own this in under a week. The learning curve is feed-spec literacy — GUID stability, RFC 822 vs. ISO 8601 dates, enclosure semantics, sanitization rules — not the package API. v1.0.3 sharpens that note: the CDATA/escaping model is the first thing to explain to a new owner, because it is the one place where the package's correctness depends on our input hygiene.
How can I help you explore Laravel packages today?