WebSocket em PHP: da teoria à aplicação industrial em tempo real
O protocolo WebSocket revolucionou o desenvolvimento web, permitindo criar canais de comunicação bidirecionais completos entre cliente e servidor. Diferentemente do AJAX clássico (long polling, Server-Sent Events), o WebSocket fornece uma conexão permanente com sobrecarga mínima. Para o desenvolvedor PHP, a transição de exemplos básicos de "servidor echo" para uma aplicação industrial em tempo real é um desafio significativo, envolvendo gerenciamento de estado, escalabilidade e processamento assíncrono.
Neste artigo, exploraremos técnicas avançadas de trabalho com WebSocket em PHP: arquitetura de chat multiusuário, integração com Redis para escalabilidade horizontal, processamento de tarefas em segundo plano e otimização de desempenho.
1. Arquitetura de servidor WebSocket avançado com Ratchet
A biblioteca Ratchet (baseada em ReactPHP) continua sendo o padrão ouro para WebSocket em PHP. No entanto, para soluções de produção, um simples onMessage não é suficiente. Vamos considerar uma arquitetura componentizada com separação de responsabilidades:
<?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 "Nova conexão: {$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); // Limpeza do registro do usuário 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'] ?? ''; // Verificação do token JWT $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 { // Simulação de verificação JWT $payload = json_decode(base64_decode(explode('.', $token)[1] ?? ''), true); return $payl
```