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

Websocket Laravel Package

morozovsk/websocket

A lightweight PHP WebSocket server and client library for real-time, bidirectional apps. Supports RFC 6455 handshakes, SSL, text/binary frames, and event-driven handling with minimal dependencies—ideal for chat, live notifications, and custom WebSocket services.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer and treat this as a CLI daemon, not a web request handler:

composer require morozovsk/websocket

The fastest way to understand the API is to read the demos/ directory in the repo before writing anything. Each demo is a complete, runnable server (echo, chat, and a few others) plus a small HTML/JS client — clone the repo, run a demo with php demos/chat/server.php, and open the matching client page to see the wire protocol end-to-end.

A minimal server is a subclass of the package's Server class that overrides the lifecycle hooks, instantiated with host/port and started with run():

<?php
require __DIR__ . '/vendor/autoload.php';

use Morozovsk\WebSocket\Server as WebSocketServer;

class EchoServer extends WebSocketServer
{
    // Called once per connecting client
    protected function connected($user) {}

    // Called for every decoded text/binary frame
    protected function process($user, $message)
    {
        $this->send($user, 'echo: ' . $message);
    }

    // Called when a client disconnects
    protected function closed($user) {}
}

$server = new EchoServer('0.0.0.0', 8080);
$server->run();

Run it from the terminal (php server.php). It blocks forever servicing stream_select(), so the first real use case is almost always a browser chat or notification widget where you need push without polling. Verify connectivity from wscat (npx wscat -c ws://localhost:8080) or the browser console (new WebSocket('ws://localhost:8080')) before wiring up any front-end framework.

Implementation Patterns

Extend, don't fork. Nearly all customization happens in three hooks: connected($user), process($user, $message), closed($user). Put your protocol logic in process() and keep the hooks fast — they run inline in the event loop, so a slow DB write inside process() stalls every connected client.

Broadcast via the user collection. The base server tracks connections for you; iterate the collection to fan out messages:

protected function process($user, $message)
{
    foreach ($this->userCollection->getUsers() as $recipient) {
        $this->send($recipient, $message);
    }
}

Use JSON envelopes for anything non-trivial. A tiny router keeps process() readable and makes it trivial to add message types later:

protected function process($user, $message)
{
    $msg = json_decode($message, true);
    if (!is_array($msg)) {
        return $this->send($user, json_encode(['error' => 'bad payload']));
    }

    switch ($msg['type'] ?? '') {
        case 'join':
            $user->room = $msg['room'] ?? 'lobby';
            break;
        case 'chat':
            $this->broadcastToRoom($user->room, [
                'type' => 'chat',
                'from' => $user->id,
                'text' => $msg['text'] ?? '',
            ]);
            break;
    }
}

Rooms are just state you attach to the User object; the package has no concept of channels, so filtering by $u->room in your loop is the whole implementation.

Yii / Yii2 integration. The repo ships yii/ and yii2/ folders with glue code rather than a Composer-installable module — copy the relevant files into your app. For Yii2 that means a console controller you invoke as php yii websocket/start (or similar, per the included controller) which wraps the server loop so it can share your app's config, autoloading, and DB components. Treat it as starter scaffolding: you'll still extend the server class and register it in the console controller.

Run it under a supervisor. Never launch with nohup in production. Use systemd or Supervisor with Restart=always, a working directory set to the app root, and logs redirected to a file. Pair it with nginx as a reverse proxy so the client connects over wss://:

location /ws {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
}

Push from the rest of the app. The server process is isolated, so anything your web app does that needs to reach connected clients has to cross a process boundary. The pragmatic pattern is a Redis pub/sub (or a tiny local TCP/UDP socket) channel that the websocket daemon subscribes to on a timer, then rebroadcasts. Don't try to share PHP objects between the FPM pool and the daemon.

Gotchas and Tips

Single-threaded and blocking. There is no worker pool and no async I/O. One slow database query, one sleep(), one large synchronous file read inside a hook and every client freezes. Push heavy work to a queue; keep hooks to parse-dispatch-send.

No TLS in the server itself. It speaks plain ws://. You must terminate TLS at nginx/Apache/stunnel and proxy the upgrade. Browsers will refuse ws:// from an https:// page, so this is not optional in production.

No heartbeat built in. Dead TCP connections (laptop lids, mobile networks) linger until the OS notices. Implement an application-level ping: record $user->lastSeen on every message, and use a periodic tick — the package's loop typically exposes a timeout/callback you can hook, or you can lower the stream_select timeout and sweep the collection, closing clients idle for >N seconds. Without this you'll accumulate ghost users and broadcast to nowhere.

All state is in memory. Rooms, user metadata, message history — gone on restart. If you need persistence, write it on closed($user) and rehydrate on connect; don't assume the daemon is durable.

Unmaintained since 2017. PHP 8 will mostly work but expect deprecation noise (dynamic properties, implicit nullable params) and no security patches. Pin your PHP version, run it in a container, and keep it off the public internet except through the proxy. Audit the handshake/frame decoder if you accept untrusted input at scale — the implementation is simple but predates a lot of hardening conventions.

Frame handling is minimal. Large messages, fragmented frames, and binary payloads are the areas most likely to surprise you. Test with payloads well past 64 KB early; if you hit truncation, chunk on the client and reassemble in process() rather than patching the decoder.

Debugging is echo and files. There's no logger. Add fwrite(STDERR, ...) in your hooks and tail the supervisor log; for wire-level issues, use browser DevTools → Network → WS frames, which shows the actual text frames in both directions and is far faster than instrumenting PHP.

Extension points worth knowing. The base class's handshake, frame decode, and send methods are protected, so you can override them for custom subprotocol negotiation, auth tokens in the query string (validate in connected() via $user->request or the socket headers), or origin checks. Do auth in connected() and immediately close unauthorized users — the handshake happens before your code runs.

Restarting drops everyone. Plan for it: clients should implement reconnect-with-backoff, and your server should tolerate the reconnect storm. A closed() hook that cleans up room membership is what makes that safe.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels