WebSocket Chat App

Aplicación de chat en tiempo real utilizando Ratchet WebSocket con Prisma PHP.

Overview

Ratchet WebSocket junto con Prisma PHP permiten construir aplicaciones de chat en tiempo real de forma sencilla. Ratchet se encarga de la comunicación bidireccional entre clientes y servidores usando WebSockets.

Prisma PHP automatiza la creación del servidor y añade auto-refresh, mejorando el flujo de desarrollo y evitando configuraciones manuales repetitivas.

Chat Application (Frontend)

Esta interfaz utiliza TailwindCSS para el diseño y WebSocket para la comunicación en tiempo real.

Crea tu ruta personalizada en: ./src/app/chatapp/index.php y pega el siguiente código:

<div class="grid place-items-center w-screen h-screen">
    <div class="space-y-2">
        <ul id="messages" class="max-h-96 overflow-y-scroll p-4 list-disc space-y-2">
            <!-- Messages will be appended here -->
        </ul>

        <form id="messageForm" class="flex flex-col gap-2">
            <label id="userId">User ID</label>

            <input class="border rounded-lg p-3" type="text" id="recipientInput" placeholder="Recipient ID...">

            <div>
                <input class="border rounded-lg p-3" type="text" id="messageInput" placeholder="Type a message...">
                <button type="submit" class="p-3 border bg-blue-500 rounded-lg text-white">Send</button>
            </div>
        </form>
    </div>
</div>
    

WebSocket Frontend Script

<script>
let userId = null;

ws.onopen = function(event) {
    console.log("Connected to the WebSocket server");
};

ws.onmessage = function(event) {
    try {
        const data = JSON.parse(event.data);

        switch (data.type) {
            case "init":
                userId = data.senderId.toString();
                document.getElementById('userId').textContent = `User ID: ${userId}`;
                break;

            case "broadcast":
                displayMessage(data.message, 'received', data.senderId);
                break;

            case "private":
                if (data.recipientId && data.recipientId.toString() === userId) {
                    displayMessage(data.message, 'received', data.senderId);
                }
                break;
        }
    } catch (e) {
        console.error("Error parsing JSON:", e.message);
    }
};

function displayMessage(message, type, senderId = "System") {
    const messages = document.getElementById('messages');
    const messageElement = document.createElement('li');

    const textContent =
        type === 'received'
        ? `From ${senderId}: ${message}`
        : `You: ${message}`;

    messageElement.textContent = textContent;

    messageElement.className =
        type === 'sent'
        ? 'bg-green-500 text-white p-2 rounded-lg'
        : 'bg-blue-500 text-white p-2 rounded-lg';

    messages.appendChild(messageElement);
    messages.scrollTop = messages.scrollHeight;
}

document.getElementById('messageForm').addEventListener('submit', function(event) {
    event.preventDefault();

    const recipientInput = document.getElementById('recipientInput').value.trim();
    const messageInput = document.getElementById('messageInput').value.trim();

    if (!messageInput) return;

    const messageType = recipientInput ? 'private' : 'broadcast';

    const messagePayload = JSON.stringify({
        recipientId: recipientInput,
        message: messageInput,
        type: messageType
    });

    ws.send(messagePayload);
    displayMessage(messageInput, 'sent');
    document.getElementById('messageInput').value = '';
});
</script>
    

Connection Manager

Ubicación: ./src/lib/websocket/ConnectionManager.php

<?php

namespace Lib\Websocket;

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Exception;
use SplObjectStorage;

class ConnectionManager implements MessageComponentInterface

    protected SplObjectStorage $clients;

    public function __construct()
    
        $this->clients = new SplObjectStorage;
    

    public function onOpen(ConnectionInterface $conn)
    
        $this->clients->attach($conn);

        $welcomeMessage = json_encode([
            'type' => 'init',
            'message' => 'Welcome to the WebSocket server!',
            'senderId' => $conn->resourceId
        ]);

        $conn->send($welcomeMessage);
    

    public function onMessage(ConnectionInterface $from, $msg)
    
        $data = json_decode($msg, true);

        $recipientId = $data['recipientId'] ?? null;
        $message = $data['message'] ?? '';

        if (!empty($recipientId)) 
            foreach ($this->clients as $client) 
                if ((string) $client->resourceId === (string) $recipientId) 
                    $client->send(json_encode([
                        'senderId' => $from->resourceId,
                        'recipientId' => $recipientId,
                        'message' => $message,
                        'type' => 'private'
                    ]));
                    return;
                
            
         else 
            foreach ($this->clients as $client) 
                if ($from !== $client) 
                    $client->send(json_encode([
                        'senderId' => $from->resourceId,
                        'message' => $message,
                        'type' => 'broadcast'
                    ]));
                
            
        
    

    public function onClose(ConnectionInterface $conn)
    
        $this->clients->detach($conn);
    

    public function onError(ConnectionInterface $conn, Exception $e)
    
        $conn->close();