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

phrity/websocket

WebSocket client and multi-connection server for PHP, supporting ws/wss, listener callbacks, close and ping/pong handling, optional deflate compression, message fragmentation, and custom opcodes.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation (v4 is a major release — pin the constraint explicitly):

    composer require phrity/websocket:^4.0
    
  2. Basic Client Usage (Request/Response):

    use WebSocket\Client;
    use WebSocket\Middleware\CloseHandler;
    use WebSocket\Middleware\PingResponder;
    
    $client = new Client("wss://echo.websocket.org/");
    $client
        ->addMiddleware(new CloseHandler())
        ->addMiddleware(new PingResponder());
    
    $client->text("Hello");
    $response = $client->receive();
    echo $response->getContent(); // "Hello"
    $client->close();
    
  3. Basic Server Usage (Continuous Listening):

    use WebSocket\Server;
    use WebSocket\Middleware\CloseHandler;
    use WebSocket\Middleware\PingResponder;
    
    $server = new Server();
    $server
        ->addMiddleware(new CloseHandler())
        ->addMiddleware(new PingResponder())
        ->onText(function ($server, $connection, $message) {
            $connection->text("Echo: " . $message->getContent());
        });
    
    $server->start();
    
  4. Smoke Test Before Building Anything — connect, send, receive, close against a throwaway endpoint (e.g. wss://echo.websocket.org/) and assert you get your payload back. Config regressions from v3 surface only at connect time, so a 20-line script saves hours later.

Migrating From v3 (read this first)

4.0.0 is not a drop-in upgrade. The release notes are explicit that the most likely conflict is Client and Server configuration (constructor options), plus the deeper-level interfaces deprecated in 3.7/3.8 are now removed. Before upgrading:

  • Read the v3→v4 migration guide — it is the authoritative source for changed constructor/configuration arguments.
  • Audit any code that touches internal/stub HTTP classes; v4 uses Nyholm PSR-7 directly instead of bundled stubs.
  • Audit any getConnections() consumer (see Gotchas — the return type changed from array to a Connections collection).
  • Audit catch blocks — the exception structure was reworked in v4 (PR #162).
  • Re-check custom middleware/identity implementations against the deprecated deeper-level interfaces (PR #163).
  • Do the upgrade on a branch and run your WebSocket integration tests against a real endpoint; config drift tends to surface only at connect time.

The quickest audit is a static analysis pass plus:

composer why-not phrity/websocket 4.0.0

First Use Case: Real-Time Chat

  • Use the Server to hold multiple connections and broadcast via the Connections collection.
  • Use the Client for outbound/side-channel connections (admin dashboard, internal service listener).
  • Keep CloseHandler + PingResponder in the middleware stack of both sides from day one — retrofitting keep-alive after you have zombie connections is painful.

Implementation Patterns

Client Patterns

  1. Request/Response Workflow:

    $client = new Client("wss://api.example.com/ws");
    $client->text(json_encode(['action' => 'fetch_data']));
    $response = $client->receive(); // Blocking
    $data = json_decode($response->getContent(), true);
    
  2. Event-Driven Subscription:

    $client = new Client("wss://stream.example.com");
    $client->onText(function ($client, $connection, $message) {
        $data = json_decode($message->getContent(), true);
        // process real-time updates
    });
    $client->start();
    
  3. Reconnection Logic (exception classes changed in v4 — catch broadly first, then narrow):

    $client = new Client("wss://reliable.example.com");
    $client->onError(function ($client, $connection, $error) {
        // v4 reworked the exception hierarchy: log the concrete class before
        // matching on it, since v3 class names may no longer exist.
        logger()->warning('ws error', ['class' => get_class($error), 'msg' => $error->getMessage()]);
        $client->reconnect();
    });
    

Server Patterns

  1. Multi-Connection Handling:

    $server = new Server();
    $server->onText(function ($server, $connection, $message) {
        $connection->text("Processed: " . $message->getContent());
    });
    $server->start();
    
  2. Connection-Specific Logic:

    $server = new Server();
    $server->onOpen(function ($server, $connection) {
        $connection->setMeta(['user_id' => 123]);
    });
    $server->onText(function ($server, $connection, $message) {
        $userId = $connection->getMeta('user_id');
        // user-scoped logic
    });
    
  3. Broadcasting via the Connections Collection (v4 change):

    $server->onText(function ($server, $connection, $message) {
        // getConnections() now returns a Connections collection, not an array
        $server->getConnections()
            ->filter(fn ($c) => $c !== $connection)
            ->each(fn ($c) => $c->text($message->getContent()));
    });
    
  4. Middleware Stack (registration order matters):

    $server
        ->addMiddleware(new CloseHandler())
        ->addMiddleware(new PingResponder())
        ->addMiddleware(new CompressionHandler());
    

Integration with Laravel

  1. Service Provider Setup:

    // app/Providers/WebSocketServiceProvider.php
    public function register()
    {
        $this->app->singleton(\WebSocket\Server::class, function ($app) {
            $server = new \WebSocket\Server();
            $server
                ->addMiddleware(new \WebSocket\Middleware\CloseHandler())
                ->addMiddleware(new \WebSocket\Middleware\PingResponder())
                ->onText([$app['websocket.handler'], 'handle']);
            return $server;
        });
    }
    

    Keep configuration in config/websocket.php (host, port, TLS context, timeouts) and pass it into the constructor — v4 changed how Client/Server are configured, so centralising it now saves you a second migration.

  2. Event-Driven Controllers / Handlers:

    public function handle($server, $connection, $message)
    {
        $data = json_decode($message->getContent(), true);
        event(new WebSocketMessageReceived($data));
    }
    
  3. Artisan Command as the Runtime:

    // app/Console/Commands/WebSocketServe.php
    protected $signature = 'websocket:serve';
    
    public function handle()
    {
        resolve(\WebSocket\Server::class)->start();
    }
    

    Run it under Supervisor/systemd, not the scheduler (the old "schedule daily" example was never a valid way to run a long-lived socket server).

  4. Laravel Echo (browser side) stays a Pusher-protocol concern; this package is your PHP socket layer, not the Echo backend. Only point Echo at this server if you front it with a Pusher-protocol-compatible broker.


Gotchas and Tips

Common Pitfalls

  1. getConnections() is no longer an array (v4 breaking change):

    • array_map, array_filter, count() and foreach ($x as $k => $v) semantics differ.
    • Fix: use collection methods (->each(), ->filter(), ->map(), ->count()). foreach still works, but keys are no longer guaranteed to be connection ids — use the connection object itself as the identity.
  2. Exception structure rework (v4 breaking change, PR #162):

    • v3 catch blocks that targeted specific classes may silently stop matching.
    • Fix: catch \Throwable (or the package base exception) first, log get_class($e), then narrow. Add a test that asserts your handlers still fire.
  3. Compression failures now throw:

    • MessageEncodingException is raised when message compression fails, instead of failing quietly.
    • Fix: wrap compressed sends in try/catch, log the payload size, and consider omitting CompressionHandler for small-payload or binary-heavy workloads.
  4. Client/Server configuration conflicts with v3:

    • Constructor signatures/options changed. Existing new Client($uri, $options)-style code may connect with different defaults (timeouts, TLS context) without erroring.
    • Fix: diff your instantiation against the migration wiki; assert timeout/context values in a smoke test.
  5. Blocking Calls:

    • Client::receive() blocks. Offload to a queue worker or use the event-driven flow.
    • Fix: never call receive() inside an HTTP request cycle.
  6. Middleware Order:

    • Middleware runs in registration order; CloseHandler first for graceful shutdown.
  7. Connection Leaks:

    try {
        $client->start();
    } finally {
        $client->close();
    }
    
  8. SSL/TLS Issues:

    • Self-signed certs fail silently; pass a stream context:
    $context = stream_context_create([
        'ssl' => ['verify_peer' => false, 'allow_self_signed' => true],
    ]);
    $client = new Client("wss://localhost", [], $context);
    
    • Verify the context argument position against v4 — it is one of the configuration points that moved.
  9. Fragmented Messages:

    • Large messages may be fragmented; check Message::isFinalFragment() before acting.
  10. Removed deprecated code (PR #163):

    • Anything you were getting away with since 3.7 (internal HTTP stubs, legacy option arrays, legacy exception names, deeper-level interfaces) is gone. A static analysis pass is the fastest way to find call sites.
  11. Nyholm PSR-7 is now a direct dependency (PR #164):

    • If you relied on the bundled stubs, composer require nyholm/psr7 and switch to real PSR-7 objects; do not mix stub factories with v4.

Debugging Tips

  1. Logging:

    $logger = new \Monolog\Logger('websocket');
    $client->setLogger($logger);
    $client->addMiddleware(new \WebSocket\Middleware\LoggingMiddleware($logger));
    
  2. Log the new exception classes explicitly — with the v4 hierarchy, $logger->error(get_class($e)) is the quickest way to learn what replaced the v3 class you used to catch.

  3. Connection Identity:

    $server->onOpen(function ($server, $connection) {
        $identity = $connection->getIdentity();
        // store in cache/db
    });
    
  4. Timeouts:

    • Set timeouts on start() to avoid hangs: $client->start(5.0);

Extension Points

  1. Custom Middleware:

    namespace App\Middleware;
    
    use WebSocket\Middleware\MiddlewareInterface;
    
    class AuthMiddleware implements MiddlewareInterface
    {
        public function __invoke($next)
        {
            return function ($client, $connection, $message) use ($next) {
                if (!$this->validateToken($message->getContent())) {
                    $connection->close(1008, "Unauthorized");
                    return;
                }
                return $next($client, $connection, $message);
            };
        }
    }
    

    Note: v4 deprecated several deeper-level interfaces — re-check that your custom middleware still implements the current contract before upgrading.

  2. PSR-7 / Nyholm:

    • v4 uses Nyholm PSR-HTTP directly rather than internal stub classes. If you injected stub messages/factories anywhere, switch to real PSR-7 objects (Nyholm\Psr7\Factory\Psr17Factory) and declare the dependency yourself:
    composer require nyholm/psr7
    
  3. Custom Identity:

    class UserIdentity implements \WebSocket\Identity\IdentityInterface
    {
        public function getId(): string { return $this->userId; }
    }
    $connection->setIdentity(new UserIdentity($userId));
    

Laravel-Specific Quirks

  1. Queue Integration — dispatch from handlers, never block the loop:

    $server->onText(function ($server, $connection, $message) {
        dispatch(new ProcessWebSocketMessage($message->getContent()));
    });
    

    Remember queue workers are separate processes: they cannot write back to $connection. Fan-out must go through the server loop (e.g. a Redis pub/sub listener inside start()).

  2. Long-running process hygiene: the socket server is a daemon. Reset stateful services (DB::reconnect() on idle, clear scoped bindings), and avoid leaking per-connection metadata — especially now that connections live in a collection you iterate frequently.

  3. Config caching: php artisan config:cache in deploy is fine, but the daemon caches config at boot — restart the daemon on every deploy, never reload in place.

  4. Route/config caching does not affect the socket server, but it does affect any HTTP endpoint that pushes into it; keep those behind a queue or a shared store rather than trying to reach the Server singleton from a web request (the singleton is per-process).

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel