Product Decisions This Supports
- Real-time feature scoping: whether "live" UX (notification bell, activity feed, job/import progress, deploy status, AI token streaming, low-frequency dashboards) ships in the next milestone or is deferred behind polling.
- Build vs. buy vs. borrow: SSE (this package) vs. WebSockets (Laravel Reverb, Soketi, Centrifugo) vs. managed push (Pusher, Ably, Mercure hub). The package's value is removing a new piece of infrastructure from the decision — no broker, no socket daemon, no per-connection vendor bill.
- Architecture/roadmap commitment: adopting SSE implies a runtime decision — PHP-FPM pins one worker per open stream, so real concurrency targets force Octane/Swoole/RoadRunner (or a dedicated stream worker pool) onto the roadmap. That is a bigger commitment than the package itself.
- Direction of data: confirms the product only needs server→client. If chat, presence, collaborative editing, or client→server signals are on the roadmap, SSE is the wrong primitive and this package should be rejected early rather than retrofitted.
- Make/buy/fork call: the surface area is small (headers, loop, flush, heartbeat, reconnect). A PM can justify a 1–2 day spike to answer "adopt, vendor, or reimplement in ~200 LOC behind our own interface" instead of treating it as a platform dependency.
- Cost model input: zero licence cost (MIT), zero dependents today, no egress metering — useful when comparing against per-connection SaaS pricing for a low-concurrency internal or B2B feature.
- Use-case fit: strong for internal admin tools, back-office dashboards, long-running import/export progress, and LLM response streaming; weak for consumer-scale fan-out.
When to Consider This Package
Consider it when:
- The need is strictly one-way, server→client, and tolerant of HTTP/1.1 semantics.
- The stack is plain PHP, Symfony, or Laravel and you want no new daemon, broker, or vendor contract.
- Expected concurrency is modest — order of tens to low hundreds of concurrent streams per node — or the app already runs on Octane/Swoole/RoadRunner where holding a connection is cheap.
- The team has no WebSocket operational experience and the org does not want to run Reverb/Mercure.
- You want MIT-licensed, self-hosted, forkable code with no lock-in.
Look elsewhere when:
- Laravel 10.43+/11+: the framework already ships
response()->eventStream(). Prefer the first-party primitive; this package mainly adds ergonomics.
- Symfony: Mercure is the native, supported path.
- Bidirectional or high-frequency client→server traffic: use WebSockets (Reverb, Soketi, Centrifugo).
- Large fan-out (thousands+ concurrent connections): use a purpose-built edge/pub-sub service; PHP workers are the wrong unit of scale.
- Maturity, security review, or SLA is required: 3 stars, 0 dependents, single maintainer, low overall score. There is no ecosystem validation, no third-party audit, and no support path. Do not put it on a critical path un-forked.
- Shared hosting or constrained PHP-FPM pools: you will exhaust workers and take down the rest of the app.
- Strict HTTP/2 / proxy environments you don't control: buffering and idle-timeout behaviour will fight you.
How to Pitch It (Stakeholders)
To executives: This is a low-cost way to test real-time UX without buying a messaging service or standing up new infrastructure. It is not a platform bet — the code is small and MIT-licensed, so the downside is bounded to a timeboxed spike. Recommendation: fund a 1–2 day evaluation against the framework's built-in streaming support and a managed alternative, and only adopt if concurrency stays modest.
To engineering: Treat it as a thin convenience layer, not infrastructure. Wrap it behind an internal streaming interface so it can be swapped for response()->eventStream(), Mercure, or a hand-rolled loop. Budget for the real work: disabling proxy buffering (X-Accel-Buffering: no), heartbeats, ignore_user_abort/disconnect detection, per-user auth on the stream endpoint, the 6-connections-per-host browser limit under HTTP/1.1, and a worker-capacity plan (Octane/Swoole or a dedicated pool). Pin the version and vendor the source.
To product/design: Ship it where degraded behaviour is acceptable — if the stream drops, fall back to polling and the feature still works. Do not design a core interaction that fails without a live connection.
Primary risks to state plainly: bus factor of one, no dependents or ecosystem signal, and a low composite score. Mitigations are cheap — pin, vendor, isolate behind an interface, and keep a documented exit to first-party streaming.