ratchet/rfc6455
A framework-agnostic RFC6455 WebSocket protocol handler for PHP, with components for both server- and client-side handshake and messaging negotiation. It performs no I/O itself; HTTP upgrade integration is handled through PSR-7 interfaces, leaving implementation-specific interpretations open.
Architecture fit. ratchet/rfc6455 is a protocol handler, not a WebSocket server. It owns RFC6455 framing, the opening handshake (server and client side), message assembly, UTF-8 validation, close-code semantics, and permessage-deflate — and it deliberately owns no I/O. That single design decision determines the whole integration shape: it cannot run inside Laravel's request/response lifecycle under PHP-FPM, because WebSocket connections are persistent and FPM workers are not. Any adoption therefore implies a two-tier architecture: Laravel remains the system of record (auth, authorization, business rules, persistence), and a separate long-running event-loop process becomes the real-time edge. The two tiers talk over HTTP, a queue, or Redis pub/sub.
This is a good fit if the requirement is protocol-level control without a vendor dependency. It is a poor fit if the expectation is "composer require and add a Laravel feature" — nothing about channels, presence, broadcasting auth, or Laravel Echo semantics ships here. As a TPM, the framing question is not "is this package good" (it is competent and Autobahn-conformant) but "are we buying a protocol library and funding a real-time service, or are we buying a real-time service?"
Integration feasibility. The composer require is trivial; the assembly is the project. A production slice requires, at minimum:
| Component | What we must supply | Notes |
|---|---|---|
| Event loop / socket layer | react/socket or Amp |
Not in this package; also the team's biggest skill gap risk |
| PSR-7 / PSR-17 | nyholm/psr7, guzzlehttp/psr7, etc. |
0.4.0 replaced Guzzle objects with PSR-17 factories |
| Handshake route | Laravel route → PSR-7 → ServerHandshake → PSR-7 response |
Feasible via Symfony's PSR HTTP bridge |
| Auth bridge | Signed short-lived connection tickets | Avoid per-connection HTTP round trips to Laravel |
| Supervision & deploy | systemd / Supervisor / container orchestration | Long-running process, graceful drain required |
| Fanout | Redis pub/sub or NATS | Required for any multi-node deployment |
Indicative shaping for planning (assumes a team unfamiliar with ReactPHP):
| Milestone | Scope | Effort |
|---|---|---|
| Spike | Handshake + echo + one authenticated message | 1–2 eng-weeks |
| Vertical slice | One real feature, internal users, single node | 3–5 eng-weeks |
| Production single-node | Auth tickets, limits, observability, deploy/drain | 6–10 eng-weeks |
| Horizontal scale | Pub/sub fanout, presence, LB tuning, cutover | +4–8 eng-weeks |
The mandatory build-vs-buy comparison is Laravel Reverb (first-party, Laravel 11+, same ReactPHP lineage, ships the channel/presence/authorization layer) versus a managed service (Pusher, Ably, Soketi). This package only wins on a specific, articulable requirement: custom subprotocol negotiation, strict control over frame and compression policy, client-side handshake (rare in a Laravel context), or embedding into an event loop that isn't ReactPHP's. Without one of those, recommending Reverb is the defensible default and this package is a distraction.
Technical risk: Medium.
Positives: Autobahn Testsuite passing (meaningful, independent conformance evidence), MIT license, clean PSR-7/17 abstractions, explicit frame and buffer size limits since 0.2.6, PHP 8.5 compatibility work in 0.4.1, active maintenance across 0.2 → 0.4.
Negatives to put in front of a steering group:
ratchet/ratchet is the primary consumer — but it means we cannot lean on a large public corpus of Laravel-specific prior art when we hit problems.Key questions to resolve before committing budget:
Stack fit. Laravel 10/11 on PHP 8.1+ comfortably clears the 7.4 floor. PSR-7/17 maps cleanly onto Symfony's PSR HTTP message bridge, so the opening handshake can be driven from a Laravel route: convert the incoming request to PSR-7, delegate to ServerHandshake, translate the PSR-7 response back — then hand the live connection to the socket process. Redis is the natural companion for presence and pub/sub fanout; the relational database stays the durable store. There is no service provider, facade, config file, or artisan command — that glue is ours to write, and worth packaging internally as a reusable Laravel bridge if more than one project will need it.
Migration path. Incremental, feature-flagged, with a cheap reversal point early:
ServerHandshake against real PSR-7 objects, decode with Frame::fromBuffer(), assemble with MessageBuffer, echo a message end to end. Decide the event loop here — this is the cheapest moment to reverse course to Reverb.Compatibility. No native Laravel coupling means no framework-upgrade hazard from this package — a genuine advantage, and worth stating explicitly since it's unusual. Watch two things: (a) conflicts when multiple PSR-7/17 implementations coexist in the Composer graph, and (b) the Octane story — coexistence is fine precisely because the socket process is separate from FPM/Octane workers, but that only holds if nobody tries to run the socket server inside an Octane worker. Document that anti-pattern.
The real compatibility gap is semantic, not technical: if the product currently relies on Laravel Echo's channel authorization, private/presence channel semantics, and broadcast event shape, none of that comes with this package. Broadcasting auth, subscription management, and presence are all reimplementations. On comparable projects this gap is the single largest source of estimate slippage — budget for it explicitly rather than discovering it in the vertical slice.
Sequencing and de-risking. Three rules:
Maintenance. We own everything above the frame: connection registry, heartbeats, reconnect/resume semantics, backpressure, graceful shutdown, TLS termination, and the internal bridge. Upstream is pre-1.0, so budget roughly 1–2 engineer-days per quarter for version review and upgrades, plus a security-patch SLA. Pin exact versions, read changelogs before bumping, and add the dependency to the GitHub security advisory watchlist — there is no vendor advisory channel. Note that the PHP 8.5 fixes in 0.4.1 are a good signal; the project is tracking modern PHP rather than ossifying.
Support. No commercial support and a small community mean internal expertise is the entire safety net. This is the sharpest contrast with a managed service, where the failure surface belongs to someone else. Mitigations: designate one or two named owners, require a written runbook before the first production feature, and treat "the engineer who understands the event loop is on holiday" as a genuine operational risk rather than a joke. Cross-train at least one backup.
Scaling. Capacity planning is memory-per-connection plus event-loop budget, not FPM worker counts — the cost curve favors fewer, larger instances, which changes the infrastructure cost model and the auto-scaling story relative to the rest of the Laravel estate. Horizontal scale requires Redis or NATS fanout and a presence registry, both of which we build. Infrastructure prerequisites that routinely bite:
proxy_read_timeout, proxy_http_version 1.1, and explicit Upgrade/Connection header passthrough.Plan single-region before multi-region. Cross-region fanout is a materially harder problem and should not be in the first roadmap.
Failure modes to design against.
MessageBuffer::$maxBufferLength explicitly (the default is ~2 MB) and map overflow to close code 1009 rather than letting the process OOM. A single unbounded client can take down a node hosting thousands of healthy ones — this is the highest-severity failure mode and the cheapest to prevent.memory_limit and the number of concurrent connections. Enforce an application-level message size cap well below the theoretical ceiling.Ramp-up. Expect 2–3 weeks before a competent Laravel engineer is productive in an async, event-loop, connection-stateful programming model — it is a genuinely different mental model from request/response, and the failure modes (blocking the loop, leaking listeners, unhandled promise rejections) are unfamiliar. The protocol layer is the small part; the async runtime discipline is the large part. Factor this into the estimate and into the hiring/training plan, and consider pairing the spike with someone who has prior ReactPHP or Swoole experience.
How can I help you explore Laravel packages today?