Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ws Symfony Bundle Laravel Package

kraken-collective/ws-symfony-bundle

Symfony bundle that integrates WebSocket support into Symfony applications, providing configuration and service wiring for real-time features. Simplifies adding WebSocket endpoints and managing connections alongside your existing Symfony services.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the reality check. This is a Symfony bundle (an AppKernel/bundles.php-registered bundle), not a Laravel package. Score 0.06 with zero dependents and a last release of 2016-11-26 means you should treat it as a reference implementation, not a dependency. It wraps Ratchet with Symfony DI glue (a server command plus tagged handler services). You can read it in an afternoon and reimplement the useful parts in a Laravel service provider.

Install / compatibility probe (5 minutes, do this before anything else):

composer require kraken-collective/ws-symfony-bundle --dry-run

Expect a conflict wall. The 2016 release will pin symfony/framework-bundle/console to 2.8–3.x and probably php >=5.5. On PHP 8.x / Laravel 10–11 you will get platform and version conflicts. If you are determined, --ignore-platform-reqs with "replace" entries in composer.json may install, but you are now maintaining a fork of an abandoned package. Budget accordingly.

Where to look first, in this order:

  1. Command/ — the long-running server command (ws:server or similar). This is the entry point and shows you the socket setup, WsServer + Ratchet\Server\IoServer, and the port/config it reads.
  2. DependencyInjection/ — how handlers are collected (a compiler pass gathering services tagged e.g. ws.handler). This is the bundle's entire value-add.
  3. The handler/topic interface(s) — the per-message onMessage / onOpen / onClose contract your classes must implement.
  4. Resources/config/ — the default port, host, and any loop/timer configuration.

First use case — an echo server. Write one handler class that stores ConnectionInterface objects in a SplObjectStorage and broadcasts incoming JSON back to all of them. Run the server command in the foreground, connect with wscat -c ws://localhost:8080, and confirm messages round-trip. That validates the whole stack (socket bind, handshake, DI wiring) before you invest in auth, channels, or persistence.

For Laravel specifically: skip the bundle registration entirely. composer require cboden/ratchet and copy the ~150 lines you actually need — a console command (php artisan ws:serve), a broadcast handler, and a SplObjectStorage registry — into app/Console/Commands and app/WebSockets. You get the same result with zero abandoned-code risk, and Laravel's command signature/config system is a drop-in for Symfony's.

Implementation Patterns

Run it as a supervised process, never in the request cycle. The server is a blocking IoServer::run() loop. Manage it with Supervisor or systemd:

[program:ws]
command=php /var/www/artisan ws:serve
autostart=true
autorestart=true
numprocs=1
user=www-data

Do not put it behind PHP-FPM, and never exec() it from a controller. One process, one port.

Bridge Laravel events → WS server via Redis pub/sub. This is the pattern that makes the package useful at all. Your WS process cannot receive HTTP webhooks, so you cannot call it directly from a Laravel controller. Instead:

  • Laravel publishes domain events to a Redis channel (App\Events\OrderShipped → PUBLISH ws:broadcast '{"channel":"orders.42",...}').
  • The WS process subscribes to ws:broadcast via Predis\Async or a React\EventLoop timer poll, and fans out to matching ConnectionInterface objects.

Write it as a small BroadcastBridge class that owns both the Redis subscription and the connection registry. Everything else (auth, channels, presence) hangs off that.

Authenticate at handshake, not at message time. The WS upgrade request carries cookies and query params. In your handshake handler, pull ?token= or the session cookie, resolve the user once, and stash the user id on the connection using a decorated ConnectionInterface (Ratchet lets you implement the interface and delegate — a UserAwareConnection wrapper is the clean way). Then onMessage can trust $conn->getUserId() without a DB hit per message.

// pseudocode: decorate, don't monkey-patch
$conn = new UserAwareConnection($conn, $userId);
$registry->attach($conn);

Tag every message with a type and a channel. Flat string messages force you to parse at every handler. A consistent envelope — {"type":"subscribe","channel":"orders.42"} / {"type":"event","channel":"orders.42","payload":{...}} — lets you route with a single match and lets clients write one dispatcher.

Heartbeats. Dead TCP connections linger for minutes. Register a periodic timer on the loop ($loop->addPeriodicTimer(30, ...)) that pings every connection and drops any that haven't ponged in 2 intervals. Without this your registry grows unbounded and broadcasts hit closed sockets.

Extension points worth knowing: the handler interface (swap in a router handler that delegates to per-channel handlers), the connection registry (replace the in-memory SplObjectStorage with a Redis set of connection ids plus a pub/sub fan-out — the only way to scale past one process), and the server command's options (host/port/verbose) which you can override in your own command subclass.

Gotchas and Tips

Eight years unmaintained is the headline gotcha. No security patches, and the transitive Ratchet version it pins predates PHP 7.4/8.x cleanups. Concretely: each() removal, ReflectionType::__toString() deprecation, and dynamic-property warnings will surface on modern PHP. If it runs at all, it runs with a deprecation storm. Run it in a container with a pinned PHP version, or fork it.

Symfony DI assumptions will bite you in Laravel. The bundle expects ContainerAwareCommand, service tags, and a kernel bundle registration. Laravel's container resolves by type-hint, not by tag, so the "handlers are auto-collected" magic is exactly the part you must rewrite. Don't expect $this->get('ws.handler.x') to work.

Memory leaks in long-running processes. Any reference held in the registry survives the connection. Attach handlers to the connection, not vice versa; null out onClose; and if you ever touch the DB from a handler, reconnect or DB::purge() periodically — an 8-hour-old PDO connection will drop and take the socket server with it if the exception isn't caught. Wrap onMessage in try/catch and log; an uncaught throwable kills the whole server.

TLS is not handled. Ratchet needs an explicit SecureServer wrapper, or terminate TLS at nginx/HAProxy and proxy to ws://127.0.0.1:8080 with Upgrade/Connection headers forwarded. Behind a load balancer, enable sticky sessions if you're single-process-per-node — otherwise a client's subscribe and its broadcast land on different backends.

Debugging silent drops. The most common failure is a message that produces no error and no response: the handler matched nothing, or the connection wasn't in the registry under the channel key the client thought it was. Add a null-handler log line at the top of onMessage printing the raw frame, and a --verbose/-vvv flag on the command. Log every onOpen and onClose with connection id and user id — this alone resolves 80% of "it doesn't work" reports. Also confirm the port is actually open (ss -ltnp | grep 8080); IoServer binds silently and a firewall drop looks identical to a code bug from the client side.

Do not reuse the request-scoped container or an ORM entity manager across connections. If a handler touches Eloquent, clear the model's state per message or, better, keep the WS process DB-free and push all writes back through a Redis queue to a normal Laravel worker.

Verdict for day-to-day use: if you need WebSockets in Laravel today, use Laravel Reverb (first-party, maintained, integrates with Echo/broadcasting out of the box) or Ratchet directly. Reach for this bundle only as a compact worked example of "Symfony command + tagged handlers + Ratchet loop" — its DI pattern is genuinely instructive, but its code should not be in your production composer.json.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console