![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.quebec/private_html/php-migration/websocket/ |
<?php
/**
* WebSocket Chat Server
* Real-time messaging system
*/
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../models/Message.php';
require_once __DIR__ . '/../models/ChatRoom.php';
require_once __DIR__ . '/../models/User.php';
require_once __DIR__ . '/../config/database.php';
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\WebSocket\WsServer;
use Ratchet\Http\HttpServer;
use Ratchet\Server\IoServer;
class ChatServer implements MessageComponentInterface {
protected $clients;
protected $users;
protected $database;
protected $messageModel;
protected $chatRoomModel;
protected $userModel;
public function __construct() {
$this->clients = new \SplObjectStorage;
$this->users = [];
// Initialize database connection
$db = new Database();
$this->database = $db->getConnection();
$this->messageModel = new Message($this->database);
$this->chatRoomModel = new ChatRoom($this->database);
$this->userModel = new User($this->database);
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$data = json_decode($msg, true);
if (!$data || !isset($data['type'])) {
return;
}
switch ($data['type']) {
case 'auth':
$this->handleAuth($from, $data);
break;
case 'join_room':
$this->handleJoinRoom($from, $data);
break;
case 'leave_room':
$this->handleLeaveRoom($from, $data);
break;
case 'message':
$this->handleMessage($from, $data);
break;
case 'typing':
$this->handleTyping($from, $data);
break;
case 'stop_typing':
$this->handleStopTyping($from, $data);
break;
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
// Remove user from users array
foreach ($this->users as $userId => $userConn) {
if ($userConn === $conn) {
unset($this->users[$userId]);
break;
}
}
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
private function handleAuth(ConnectionInterface $conn, $data) {
$token = $data['token'] ?? '';
// Verify token and get user
$user = $this->verifyToken($token);
if ($user) {
$this->users[$user['id']] = $conn;
$conn->user = $user;
$conn->send(json_encode([
'type' => 'auth_success',
'user' => $user
]));
echo "User {$user['name']} authenticated\n";
} else {
$conn->send(json_encode([
'type' => 'auth_error',
'message' => 'Invalid token'
]));
}
}
private function handleJoinRoom(ConnectionInterface $conn, $data) {
if (!isset($conn->user)) {
return;
}
$roomId = $data['roomId'] ?? '';
// Verify user has access to room
if ($this->hasRoomAccess($conn->user['id'], $roomId)) {
$conn->roomId = $roomId;
// Send recent messages
$messages = $this->messageModel->getRecentByChatRoomId($roomId, 50);
$conn->send(json_encode([
'type' => 'room_joined',
'roomId' => $roomId,
'messages' => $messages
]));
echo "User {$conn->user['name']} joined room {$roomId}\n";
}
}
private function handleLeaveRoom(ConnectionInterface $conn, $data) {
if (isset($conn->roomId)) {
unset($conn->roomId);
echo "User {$conn->user['name']} left room\n";
}
}
private function handleMessage(ConnectionInterface $conn, $data) {
if (!isset($conn->user) || !isset($conn->roomId)) {
return;
}
$content = $data['content'] ?? '';
$messageType = $data['messageType'] ?? 'TEXT';
$replyToId = $data['replyToId'] ?? null;
if (empty($content) && $messageType === 'TEXT') {
return;
}
// Create message in database
$this->messageModel->content = $content;
$this->messageModel->type = $messageType;
$this->messageModel->chatRoomId = $conn->roomId;
$this->messageModel->userId = $conn->user['id'];
$this->messageModel->isEdited = 0;
$this->messageModel->isDeleted = 0;
$this->messageModel->replyToId = $replyToId;
$this->messageModel->fileUrl = $data['fileUrl'] ?? null;
$this->messageModel->fileName = $data['fileName'] ?? null;
$this->messageModel->fileSize = $data['fileSize'] ?? null;
$this->messageModel->fileType = $data['fileType'] ?? null;
if ($this->messageModel->create()) {
// Get the created message with user info
$this->messageModel->findById($this->messageModel->id);
$messageData = [
'type' => 'new_message',
'message' => [
'id' => $this->messageModel->id,
'content' => $this->messageModel->content,
'type' => $this->messageModel->type,
'chatRoomId' => $this->messageModel->chatRoomId,
'userId' => $this->messageModel->userId,
'userName' => $conn->user['name'],
'userRole' => $conn->user['role'],
'profilePicture' => $conn->user['profilePicture'],
'createdAt' => $this->messageModel->createdAt,
'isEdited' => $this->messageModel->isEdited,
'replyToId' => $this->messageModel->replyToId,
'fileUrl' => $this->messageModel->fileUrl,
'fileName' => $this->messageModel->fileName,
'fileSize' => $this->messageModel->fileSize,
'fileType' => $this->messageModel->fileType
]
];
// Broadcast to all clients in the room
$this->broadcastToRoom($conn->roomId, $messageData, $conn);
echo "Message sent in room {$conn->roomId} by {$conn->user['name']}\n";
}
}
private function handleTyping(ConnectionInterface $conn, $data) {
if (!isset($conn->user) || !isset($conn->roomId)) {
return;
}
$this->broadcastToRoom($conn->roomId, [
'type' => 'user_typing',
'userId' => $conn->user['id'],
'userName' => $conn->user['name'],
'roomId' => $conn->roomId
], $conn);
}
private function handleStopTyping(ConnectionInterface $conn, $data) {
if (!isset($conn->user) || !isset($conn->roomId)) {
return;
}
$this->broadcastToRoom($conn->roomId, [
'type' => 'user_stop_typing',
'userId' => $conn->user['id'],
'userName' => $conn->user['name'],
'roomId' => $conn->roomId
], $conn);
}
private function broadcastToRoom($roomId, $data, $exclude = null) {
foreach ($this->clients as $client) {
if ($client !== $exclude &&
isset($client->roomId) &&
$client->roomId === $roomId) {
$client->send(json_encode($data));
}
}
}
private function verifyToken($token) {
// Simple token verification - in production, use JWT
if (empty($token)) {
return false;
}
// For now, we'll use a simple approach
// In production, implement proper JWT verification
$query = "SELECT id, name, email, role, profilePicture FROM User WHERE id = :token LIMIT 1";
$stmt = $this->database->prepare($query);
$stmt->bindParam(':token', $token);
$stmt->execute();
if ($stmt->rowCount() > 0) {
return $stmt->fetch(PDO::FETCH_ASSOC);
}
return false;
}
private function hasRoomAccess($userId, $roomId) {
// Check if user is participant in the room
$query = "SELECT COUNT(*) as count FROM ChatParticipant
WHERE userId = :userId AND chatRoomId = :roomId";
$stmt = $this->database->prepare($query);
$stmt->bindParam(':userId', $userId);
$stmt->bindParam(':roomId', $roomId);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['count'] > 0;
}
}
// Start the server
$server = IoServer::factory(
new HttpServer(
new WsServer(
new ChatServer()
)
),
8080
);
echo "Chat server started on port 8080\n";
$server->run();
?>