cydrickn/swoole-websocket-bundle
Install the Bundle:
composer require cydrickn/swoole-websocket-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Cydrickn\SwooleWebsocketBundle\SwooleWebsocketBundle::class => ['all' => true],
];
Configure the Bundle (optional):
Override default settings in config/packages/swoole_websocket.yaml:
swoole_websocket:
host: '0.0.0.0' # Expose to LAN
port: 8080 # Match client JS port
workers: 4 # Adjust based on CPU cores
First Use Case: Basic Echo Server Create an event subscriber to handle messages:
php bin/console make:subscriber WebsocketMessageSubscriber
Implement MessageEvent handling:
// src/EventSubscriber/WebsocketMessageSubscriber.php
namespace App\EventSubscriber;
use Cydrickn\SwooleWebsocketBundle\Event\MessageEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class WebsocketMessageSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
MessageEvent::class => 'onMessage',
];
}
public function onMessage(MessageEvent $event): void
{
$fd = $event->getFd(); // Client connection ID
$message = $event->getData();
$event->getServer()->push($fd, "Echo: $message"); // Send back
}
}
Run the Server:
php bin/console websocket:server
Test with the provided JavaScript snippet (update port to 8080).
Event-Driven Architecture
OpenEvent, MessageEvent, and CloseEvent for lifecycle management.OpenEvent and clean up in CloseEvent.
public function onOpen(OpenEvent $event): void
{
$this->connectionManager->addConnection($event->getFd());
}
Broadcasting to Clients
$event->getServer()->push($fd, $data) for one-to-one messaging.$event->getServer()->push($fd, $data) in a loop (store FDs in a service).task workers for heavy processing:
$event->getServer()->task([$this, 'processMessage'], $message);
Authentication & Authorization
OpenEvent:
public function onOpen(OpenEvent $event): void
{
$token = $event->getGet()['token'] ?? null;
if (!$this->authService->validate($token)) {
$event->getServer()->close($event->getFd());
return;
}
// Proceed if valid
}
Integration with Symfony Services
MessageBus, EntityManager):
public function __construct(private MessageBus $bus) {}
DependencyInjection to configure the bundle (e.g., override workers count).Graceful Shutdown
CloseEvent to log disconnections or trigger cleanup:
public function onClose(CloseEvent $event): void
{
$this->connectionManager->removeConnection($event->getFd());
}
Room-Based Messaging
Room service to group connections (e.g., by user ID or channel).// In MessageEvent
$roomId = $this->roomService->getRoomFor($fd);
$this->roomService->broadcast($roomId, $data);
Rate Limiting
onHandshake to throttle connections:
$event->getServer()->on('handshake', function ($request, $response) {
if ($this->rateLimiter->isOverLimit($request->fd)) {
$response->end();
return;
}
});
Hybrid HTTP/WebSocket
swoole_serve middleware).Connection Leaks
$server->close($fd) in CloseEvent or when done.lsof -i :8080 for open connections.Event Dispatcher Order
EventDispatcher::DISPATCHER_PRIORITY_HIGH for critical logic.Serialization Quirks
push() expects strings. JSON-encode data:
$server->push($fd, json_encode(['type' => 'update']));
JSON.parse(event.data).Worker Isolation
$server) is not thread-safe across workers.Swoole\Table or Redis for shared state.Port Conflicts
8000 may clash with other services.config/packages/:
php bin/console websocket:server --port=9090
Swoole Logs
config/packages/swoole_websocket.yaml:
swoole_websocket:
log_file: '%kernel.logs_dir%/swoole.log'
log_level: 7 # DEBUG
Xdebug with Swoole
swoole_set_process_name() to identify workers in Xdebug:
$server->on('start', function () {
swoole_set_process_name("websocket-worker");
});
Connection Inspection
public function onOpen(OpenEvent $event): void
{
$connections = $event->getServer()->connections;
file_put_contents('connections.log', print_r($connections, true));
}
Custom Handshake
handshake event to validate headers:
$server->on('handshake', function ($request, $response) {
if (!$this->validateHandshake($request->header)) {
$response->end();
}
});
Protocol Upgrades
Swoole\WebSocket\Frame for custom framing:
$frame = new \Swoole\WebSocket\Frame($data, \Swoole\WebSocket::OP_BINARY);
$server->push($fd, $frame);
Metrics Integration
CloseEvent:
public function onClose(CloseEvent $event): void
{
$this->metrics->inc('websocket.connections.closed');
}
Load Testing
autobahn-testsuite to validate compliance:
npm install -g autobahn-testsuite
websocket -m f64 -c 100 ws://localhost:8080
How can I help you explore Laravel packages today?