p2/ratchet-bundle
A Symfony bundle that integrates the Ratchet WebSocket library, helping PHP developers add real-time, bidirectional communication to applications. It provides configuration and integration points for running WebSocket servers alongside Symfony.
composer require p2/ratchet-bundle). It pulls in cboden/ratchet and expects a Symfony app that still registers bundles in a kernel (this is a Symfony 2/3-era package; on Symfony 4+ you may need config/bundles.php plus Flex-style config in config/packages/).new P2\Bundle\RatchetBundle\P2RatchetBundle() in AppKernel::registerBundles() (or config/bundles.php on newer Symfony).bind/port entry under the bundle's config key). Start with the loopback or a local dev port and only expose it behind a proxy later.-vvv so you can see connection/chat traffic in your terminal.WebSocket client connects to ws://localhost:<port>, sends a JSON message, and a tagged handler service in your app receives it and pushes a response back. This validates the whole chain (container → handler → connection) before you add auth, rooms, or broadcasting.Where to look first:
DependencyInjection/ folder to see the exact config keys and service IDs it registers.Resources/config/services.yml to learn which tags make a class a "message handler."Chat/Hello handler) — copy it verbatim into src/ and get it working before customizing.Handler services for message logic. The typical pattern is: you write a service that implements the bundle's message-handler interface, tag it (or implement the marker interface), and the bundle routes decoded client messages to it. Keep these handlers thin — parse the payload, call a domain service, return the response. Don't put business rules in the handler itself.
JSON envelope convention. Adopt a small message envelope like {"type": "...", "payload": {...}} and a single dispatcher handler that switches on type. This avoids one tagged handler per message and makes the client protocol explicit and versionable.
Broadcasting and rooms. For anything multi-user, keep an in-memory registry of connections → room/topic (an array or SplObjectStorage keyed by connection). Broadcast by iterating the room's connections and calling send(). For multi-process scaling you'll need a pub/sub backend (Redis) or a dedicated broker — the bundle itself only knows about connections in its own process.
Injecting Symfony services + resetting Doctrine. Handlers run inside the server process with the real container, so you can inject repositories and the EntityManager. In the handler, wrap DB work in try/finally and call $em->clear() (or reset the manager) after each message — the process is long-lived, so the identity map and stale entity state will otherwise cause subtle bugs.
Auth/session integration. The common approach is to read the Symfony session cookie during the WebSocket handshake ($conn->httpRequest / request headers), look up the session in the configured handler, and attach the resolved user to the connection object. Do the auth check once at handshake, not on every message, and keep the user ID on the connection for authorization decisions later.
Lifecycle hooks. Use the server's connection onOpen/onClose events for presence tracking, audit logging, and cleanup, and a periodic timer if you need heartbeats/ping-pong (proxies and browsers will drop idle sockets). Wire those through the bundle's server factory/event extension points rather than subclassing the whole server.
Running it like a daemon. In production, run the command under systemd or Supervisor with restart-on-crash, bind to 127.0.0.1, and terminate TLS/WSS at nginx (with Upgrade/Connection headers and a long proxy_read_timeout). Emit logs to stdout and let the supervisor collect them. Health-check by having the app connect to itself periodically.
Integration with the rest of Symfony. Treat the WebSocket server as a separate long-running worker of the same app: share the container and config, but push outbound notifications into it from HTTP requests via a queue or Redis pub/sub and have the server subscribe. Don't try to make the HTTP kernel call into the WS process directly.
cboden/ratchet version pulled in is PHP-compatible with your runtime before committing.Configuration.php — mismatches are usually silent (defaults apply), so you can spend an hour wondering why your port/bind change did nothing. Dump the resolved container (bin/console debug:container) to verify services and parameters.ws:// endpoint, and mixed-content errors are opaque. If the JS client fails with no server-side log, suspect the proxy upgrade headers or the mixed-content block first.-vvv, add logging at the handler entry point, and use bin/console interactively. For connection issues, test with wscat or websocat before involving the browser so you can separate transport problems from client-code problems.composer will revert your patches.composer.lock, and if you need production-grade features (presence at scale, redis fan-out, HTTP push) consider comparing against Mercure or a maintained Swoole/RoadRunner stack before building deep on top of this bundle.How can I help you explore Laravel packages today?