Implementación de WebSockets en WordPress para Aplicaciones en Tiempo Real
Introducción: El salto de WordPress a lo reactivo
WordPress, durante años, ha sido el rey del contenido estático y las arquitecturas petición-respuesta. Sin embargo, el ecosistema moderno demanda experiencias fluidas: notificaciones que aparecen sin recargar, chats de soporte instantáneos, dashboards de analíticas que se actualizan solos o subastas en vivo. Para lograr esto, necesitamos WebSockets.
La implementación de WebSockets en WordPress no es trivial. El CMS está construido sobre PHP, un lenguaje tradicionalmente sin estado, y sobre MySQL, que no gestiona bien las conexiones persistentes. Pero con las herramientas adecuadas —Ratchet, ReactPHP y un plugin personalizado— podemos convertir cualquier sitio en una plataforma en tiempo real.
Este artículo te guiará paso a paso en la arquitectura, instalación y puesta en producción de un sistema de WebSockets para WordPress, incluyendo ejemplos prácticos como un chat en vivo o un contador de usuarios online.
¿Por qué WebSockets y no AJAX o Long Polling?
Antes de ensuciarnos las manos, entendamos por qué merece la pena el esfuerzo.
- AJAX tradicional: El cliente pregunta cada X segundos. Ineficiente, genera latencia y sobrecarga en el servidor.
- Long Polling: Mantiene la conexión abierta, pero sigue siendo una simulación de tiempo real. Consume muchos recursos.
- WebSockets: Conexión bidireccional y persistente. El servidor empuja datos al instante. Ideal para aplicaciones que requieren baja latencia.
[INFO] WordPress por sí solo no soporta WebSockets. Necesitas un servidor externo (basado en PHP con Ratchet o en Node.js) que gestione las conexiones. Aquí nos centraremos en la solución 100% PHP con Ratchet y ReactPHP, ideal para entornos donde no quieras introducir Node.js.
Requisitos previos y stack tecnológico
Para seguir este artículo necesitarás:
- Un servidor con PHP 8.0+ y extensión
pcntl,posixysocketshabilitadas. - Acceso SSH al servidor (para ejecutar el proceso en segundo plano).
- Composer instalado globalmente.
- Un WordPress funcional (preferiblemente con un plugin personalizado para la comunicación).
Nuestro stack será:
| Componente | Función |
|---|---|
| Ratchet | Biblioteca PHP para WebSockets (basada en ReactPHP) |
| ReactPHP | Bucle de eventos asíncrono para PHP |
| Plugin personalizado | Punto de entrada desde WordPress al servidor WebSocket |
| Redis (opcional) | Para escalar horizontalmente y compartir estado entre workers |
Instalación del servidor WebSocket con Ratchet
1. Preparar el entorno
Conéctate por SSH a tu servidor y crea un directorio fuera del public_html (por seguridad):
mkdir /opt/websocket-server
cd /opt/websocket-server
composer require cboden/ratchet
Esto instalará Ratchet y ReactPHP como dependencias.
2. Crear el servidor WebSocket básico
Crea un archivo server.php:
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
class ChatServer implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
echo "Servidor WebSocket iniciado.\n";
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "Nueva conexión: {$conn->resourceId}\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$data = json_decode($msg, true);
// Aquí procesaremos el mensaje y lo reenviaremos
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send(json_encode([
'type' => 'chat',
'user' => $data['user'],
'message' => $data['message'],
'timestamp' => time()
]));
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Conexión cerrada: {$conn->resourceId}\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "Error: {$e->getMessage()}\n";
$conn->close();
}
}
$server = IoServer::factory(
new HttpServer(
new WsServer(
new ChatServer()
)
),
8080 // Puerto
);
$server->run();
[TIP] Usa un puerto alto (>1024) para evitar conflictos con Apache/Nginx. En producción, redirige desde el puerto 80/443 mediante proxy inverso.
3. Ejecutar el servidor
php server.php
Deberías ver: Servidor WebSocket iniciado. Para mantenerlo corriendo en segundo plano, usa nohup o screen:
nohup php server.php > /var/log/websocket.log 2>&1 &
Integración con WordPress: El plugin puente
Ahora necesitamos que WordPress se comunique con nuestro servidor WebSocket. Crearemos un plugin sencillo.
1. Estructura del plugin
Crea en wp-content/plugins/ws-integration/:
ws-integration/
├── ws-integration.php
├── includes/
│ └── class-ws-handler.php
└── assets/
└── ws-client.js
2. Código principal del plugin (ws-integration.php)
<?php
/**
* Plugin Name: WebSockets Integration
* Description: Conecta WordPress con el servidor Ratchet.
* Version: 1.0
*/
defined('ABSPATH') or die('Acceso directo no permitido.');
// Hook para cargar el JavaScript en el frontend
add_action('wp_enqueue_scripts', 'ws_enqueue_assets');
function ws_enqueue_assets() {
wp_enqueue_script('ws-client', plugin_dir_url(__FILE__) . 'assets/ws-client.js', array(), '1.0', true);
wp_localize_script('ws-client', 'ws_config', array(
'server_url' => 'ws://tu-servidor.com:8080', // Cambia por tu dominio/puerto
'user_id' => get_current_user_id(),
'nonce' => wp_create_nonce('ws_nonce')
));
}
// Endpoint AJAX para enviar mensajes desde WordPress al servidor WebSocket
add_action('wp_ajax_ws_send_message', 'ws_send_message_handler');
function ws_send_message_handler() {
check_ajax_referer('ws_nonce', 'nonce');
$user_id = get_current_user_id();
$message = sanitize_text_field($_POST['message']);
// Enviar al servidor WebSocket mediante socket TCP
$socket = @fsockopen('127.0.0.1', 8080, $errno, $errstr, 5);
if (!$socket) {
wp_send_json_error('No se pudo conectar al servidor WebSocket');
}
$data = json_encode([
'type' => 'chat',
'user' => get_userdata($user_id)->display_name,
'message' => $message,
'timestamp' => time()
]);
// Enviar trama WebSocket (versión simplificada)
$frame = chr(129) . chr(strlen($data)) . $data;
fwrite($socket, $frame);
fclose($socket);
wp_send_json_success('Mensaje enviado');
}
[WARNING] Este ejemplo envía tramas WebSocket "a pelo". En producción, usa una librería como
Textalk/websocket-phppara manejar el handshake y enmascarado correctamente.
3. Cliente JavaScript (ws-client.js)
(function($) {
'use strict';
let ws;
const serverUrl = ws_config.server_url;
function connectWebSocket() {
ws = new WebSocket(serverUrl);
ws.onopen = function() {
console.log('Conectado al servidor WebSocket');
// Enviar autenticación
ws.send(JSON.stringify({
type: 'auth',
user_id: ws_config.user_id,
token: ws_config.nonce
}));
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
handleMessage(data);
};
ws.onclose = function() {
console.log('Desconectado. Reconectando en 5 segundos...');
setTimeout(connectWebSocket, 5000);
};
ws.onerror = function(error) {
console.error('Error WebSocket:', error);
};
}
function handleMessage(data) {
// Ejemplo: mostrar en un chat
if (data.type === 'chat') {
const chatBox = $('#chat-messages');
chatBox.append(`<p><strong>${data.user}:</strong> ${data.message}</p>`);
chatBox.scrollTop(chatBox[0].scrollHeight);
}
// Disparar evento personalizado para otros scripts
$(document).trigger('ws_message', [data]);
}
// Iniciar conexión
$(document).ready(function() {
connectWebSocket();
// Enviar mensaje desde formulario
$('#chat-form').on('submit', function(e) {
e.preventDefault();
const message = $('#chat-input').val();
$.post(ajaxurl, {
action: 'ws_send_message',
message: message,
nonce: ws_config.nonce
});
$('#chat-input').val('');
});
});
})(jQuery);
Caso práctico: Chat en vivo en WordPress
Con el servidor corriendo y el plugin activo, ya podemos implementar un chat en tiempo real.
1. Shortcode para mostrar el chat
Añade esto a tu plugin:
add_shortcode('ws_chat', 'ws_chat_shortcode');
function ws_chat_shortcode() {
ob_start();
?>
<div id="chat-container">
<div id="chat-messages" style="height:300px; overflow-y:scroll; border:1px solid #ccc; padding:10px;">
<!-- Los mensajes aparecerán aquí -->
</div>
<form id="chat-form">
<input type="text" id="chat-input" placeholder="Escribe tu mensaje..." style="width:80%;" />
<button type="submit">Enviar</button>
</form>
</div>
<?php
return ob_get_clean();
}
Ahora puedes insertar [ws_chat] en cualquier página o entrada.
2. Mejora: Autenticación y rooms
Para un chat más avanzado, modifica el servidor para manejar "rooms" (canales). Cada room es un array de conexiones. Así puedes tener chats privados, grupos, etc.
// En el servidor Ratchet
protected $rooms = [];
public function onMessage(ConnectionInterface $from, $msg) {
$data = json_decode($msg, true);
switch($data['type']) {
case 'join':
$room = $data['room'];
$this->rooms[$room][] = $from;
break;
case 'chat':
$room = $data['room'];
foreach ($this->rooms[$room] as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
break;
}
}
Escalado y producción: Redis, Supervisord y proxy inverso
Un solo proceso PHP no escala bien. Para producción necesitas:
1. Múltiples workers con Redis
Instala Redis y usa la biblioteca predis/predis. Cada worker se suscribe a un canal Redis. Cuando un worker recibe un mensaje, lo publica en Redis y todos los workers lo reenvían a sus clientes.
composer require predis/predis
Ejemplo de worker con Redis:
use Predis\Async\Client as RedisClient;
$redis = new RedisClient('tcp://127.0.0.1:6379');
$redis->pubSubLoop('chat_messages', function ($event) use ($clients) {
foreach ($clients as $client) {
$client->send($event->payload);
}
});
2. Supervisord para gestión de procesos
Crea /etc/supervisor/conf.d/websocket.conf:
[program:websocket]
command=php /opt/websocket-server/server.php
user=www-data
autostart=true
autorestart=true
stderr_logfile=/var/log/websocket.err.log
stdout_logfile=/var/log/websocket.out.log
numprocs=4
process_name=%(program_name)s_%(process_num)02d
Esto lanza 4 workers. Cada uno escucha en un puerto diferente o comparten el mismo puerto mediante un balanceador.
3. Proxy inverso con Nginx
Para que los clientes se conecten a wss://tudominio.com/ws (WebSocket seguro), configura Nginx:
upstream websocket {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
server 127.0.0.1:8083;
}
server {
listen 443 ssl;
server_name tudominio.com;
# SSL config...
location /ws/ {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400;
}
}
Ahora los clientes se conectarán a wss://tudominio.com/ws/.
[TIP] Siempre usa WSS (WebSocket sobre TLS) en producción. Los navegadores modernos bloquean conexiones WS desde páginas HTTPS.
Consideraciones de seguridad
No puedes lanzar un servidor WebSocket sin pensar en seguridad:
- Autenticación: Valida tokens JWT o nonces de WordPress en cada conexión. El servidor debe rechazar conexiones no autorizadas.
- Rate limiting: Limita mensajes por segundo por IP/usuario para evitar spam.
- Validación de datos: Nunca confíes en el cliente. Sanitiza todo en el servidor.
- CORS: Configura orígenes permitidos en el servidor WebSocket.
- Aislamiento de procesos: Ejecuta el servidor con un usuario sin privilegios (nunca root).
Alternativas: Node.js vs Ratchet
Si tu equipo está más cómodo con JavaScript, puedes usar Socket.IO con Node.js. La integración con WordPress se haría mediante una API REST personalizada. Las ventajas de Ratchet son:
- Todo en PHP, sin cambiar de stack.
- Fácil integración con funciones nativas de WordPress (wpdb, wp_users, etc.).
- Bajo consumo de memoria en procesos ligeros.
Las desventajas: la comunidad PHP en tiempo real es más pequeña, y el rendimiento puro es inferior a Node.js en conexiones masivas (>10k simultáneas).
Monitorización y debugging
Para ver qué ocurre en tu servidor WebSocket:
# Ver logs en tiempo real
tail -f /var/log/websocket.out.log
# Probar conexión con wscat (instalar con npm)
wscat -c ws://localhost:8080
> {"type":"chat","user":"test","message":"hola"}
< {"type":"chat","user":"test","message":"hola","timestamp":...}
También puedes usar herramientas como WebSocket King (extensión Chrome) para inspeccionar tráfico.
Conclusión: ¿Merece la pena?
Implementar WebSockets en WordPress con Ratchet y ReactPHP no es un paseo por el parque, pero los beneficios son enormes:
- Experiencia de usuario instantánea.
- Reducción de carga en el servidor (eliminas polling).
