WebSocket in PHP: From Theory to Industrial Real-Time Applications
The WebSocket protocol revolutionized web development by enabling full-duplex communication channels between client and server. Unlike classic AJAX (long polling, Server-Sent Events), WebSocket provides a persistent connection with minimal overhead. For a PHP developer, transitioning from basic "echo server" examples to an industrial real-time application is a serious challenge involving state management, scaling, and asynchronous processing.
In this article, we will explore advanced techniques for working with WebSocket in PHP: multi-user chat architecture, integration with Redis for horizontal scaling, background task processing, and performance optimization.
1. Architecture of an Advanced WebSocket Server with Ratchet
The Ratchet library (based on ReactPHP) remains the gold standard for WebSocket in PHP. However, for production solutions, a simple onMessage is not enough. Let's consider a component-based architecture with separation of concerns:
<?phpuse Ratchet\\MessageComponentInterface;use Ratchet\\ConnectionInterface;use React\\EventLoop\\LoopInterface;
class RealTimeHub implements MessageComponentInterface { private $clients; private $loop; private $redis; private $userRegistry;
public function __construct(LoopInterface $loop, \\Redis $redis) { $this->clients = new \\SplObjectStorage; $this->loop = $loop; $this->redis = $redis; $this->userRegistry = []; }
public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); echo "New connection: {$conn->resourceId}"; }
public function onMessage(ConnectionInterface $from, $msg) { $data = json_decode($msg, true);
switch ($data['type'] ?? '') { case 'auth': $this->handleAuth($from, $data); break; case 'message': $this->handleMessage($from, $data); break; case 'subscribe': $this->handleSubscribe($from, $data); break; default: $from->send(json_encode(['error' => 'Unknown command'])); } }
public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); // Clean up user registration if (isset($conn->userId)) { unset($this->userRegistry[$conn->userId]); } }
public function onError(ConnectionInterface $conn, \\Exception $e) { $conn->close(); }
private function handleAuth(ConnectionInterface $conn, array $data) { $token = $data['token'] ?? ''; // JWT token validation $userId = $this->validateToken($token); if ($userId) { $conn->userId = $userId; $this->userRegistry[$userId] = $conn; $conn->send(json_encode(['type' => 'auth_ok'])); } else { $conn->send(json_encode(['type' => 'auth_fail'])); } }
private function handleMessage(ConnectionInterface $from, array $data) { $targetUserId = $data['to'] ?? null; if ($targetUserId && isset($this->userRegistry[$targetUserId])) { $this->userRegistry[$targetUserId]->send(json_encode([ 'type' => 'message', 'from' => $from->userId ?? 'anonymous', 'text' => $data['text'] ])); } }
private function validateToken(string $token): ?int { // Simulating JWT validation $payload = json_decode(base64_decode(explode('.', $token)[1] ?? ''), true); return $payl