Project Overview

A Real-Time Chat Application enables users to communicate instantly over the web. This project covers creating a chat system using PHP in combination with WebSockets, allowing for real-time message exchange without page reloads. This functionality is essential for modern web applications, including customer support systems, social platforms, and collaborative tools.

Prerequisites

Ensure you have the following:

  • Web Server: Apache (using XAMPP, WAMP, or MAMP)
  • PHP: Version 7.4 or higher
  • MySQL: For database management
  • Code Editor: VS Code, Sublime Text, PHPStorm, etc.
  • Composer: For dependency management
  • Basic Understanding of JavaScript, AJAX, and WebSockets

Step-by-Step Procedure

1. Setting Up the Development Environment

  1. Install XAMPP:
    • Download from XAMPP Official Website.
    • Follow the installation wizard and install it in the default directory.
  2. Start Apache and MySQL:
    • Open the XAMPP Control Panel.
    • Start the Apache and MySQL modules.
  3. Install Composer:

2. Creating the Database

  1. Access phpMyAdmin:
    • Navigate to http://localhost/phpmyadmin/ in your browser.
  2. Create a New Database:
    • Click on “New” in the left sidebar.
    • Name the database chat_app.
    • Choose “utf8mb4_unicode_ci” as the collation.
    • Click “Create”.
  3. Create a users Table:
    • Select the chat_app database.
    • Click on “New” to create a table.
    • Define the table with the following fields:
    Field Type Null Key Default Extra id INT NO PRI NULL AUTO_INCREMENT username VARCHAR(50) NO UNI NULL password VARCHAR(255) NO NULL created_at TIMESTAMP NO CURRENT_TIMESTAMP
    • Click “Save”.
  4. Create a messages Table: Field Type Null Key Default Extra id INT NO PRI NULL AUTO_INCREMENT user_id INT NO MUL NULL message TEXT NO NULL created_at TIMESTAMP NO CURRENT_TIMESTAMP
    • Click “Save”.
  5. Establish Foreign Keys:
    • messages.user_id references users.id with ON DELETE CASCADE.

3. Installing Dependencies via Composer

To implement WebSockets in PHP, we’ll use the Ratchet library.

  1. Navigate to Project Directory:
    • Open your terminal or command prompt.
    • Navigate to the chat-app directory.
  2. Initialize Composer:composer init
    • Follow the prompts to set up your composer.json file.
  3. Install Ratchet: composer require cboden/ratchet

4. Project Structure

Organize your project files as follows:

chat-app/
├── vendor/             # Composer dependencies
├── config/
│   └── db.php
├── public/
│   ├── index.php
│   ├── login.php
│   ├── register.php
│   ├── chat.php
│   ├── logout.php
│   ├── assets/
│   │   ├── css/
│   │   │   └── styles.css
│   │   └── js/
│   │       └── chat.js
├── src/
│   └── Chat.php
├── server.php
├── composer.json
├── README.md

5. Configuration

a. Database Connection (config/db.php)

Create a file named db.php inside the config directory to handle database connections.

<?php
// config/db.php

$host = 'localhost';
$db   = 'chat_app';
$user = 'root'; // Default XAMPP MySQL user
$pass = '';     // Default XAMPP MySQL password is empty
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // Enable exceptions
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // Fetch associative arrays
    PDO::ATTR_EMULATE_PREPARES   => false,                  // Disable emulation
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
    // Handle connection errors
    http_response_code(500);
    echo "Database connection failed.";
    exit;
}
?>

6. Creating the Chat Server (server.php)

Create a server.php file in the root directory to handle WebSocket connections.

<?php
// server.php

require __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/config/db.php';
require_once __DIR__ . '/src/Chat.php';

use Ratchet\Http\HttpServer;
use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;

$chatApp = new Chat($pdo);

$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            $chatApp
        )
    ),
    8080
);

echo "WebSocket server started on port 8080\n";

$server->run();
?>
b. Chat Class (src/Chat.php)

Create a Chat.php file inside the src directory to manage chat functionalities.

<?php
// src/Chat.php

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface {
    protected $clients;
    protected $pdo;
    protected $users;

    public function __construct($pdo) {
        $this->clients = new \SplObjectStorage;
        $this->pdo = $pdo;
        $this->users = [];
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection
        $this->clients->attach($conn);
        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        // Decode the incoming message
        $data = json_decode($msg, true);
        if (!$data) {
            return;
        }

        // Handle user authentication
        if ($data['type'] === 'auth') {
            $username = trim($data['username']);
            $password = trim($data['password']);

            // Authenticate user
            $stmt = $this->pdo->prepare('SELECT * FROM users WHERE username = ?');
            $stmt->execute([$username]);
            $user = $stmt->fetch();

            if ($user && password_verify($password, $user['password'])) {
                $this->users[$from->resourceId] = $user['username'];
                // Notify the user of successful authentication
                $from->send(json_encode(['type' => 'auth', 'status' => 'success']));
                // Notify others
                $this->broadcast(json_encode(['type' => 'system', 'message' => "{$username} has joined the chat."]), $from);
            } else {
                // Authentication failed
                $from->send(json_encode(['type' => 'auth', 'status' => 'fail', 'message' => 'Invalid credentials.']));
            }
            return;
        }

        // Handle chat messages
        if ($data['type'] === 'message') {
            if (!isset($this->users[$from->resourceId])) {
                $from->send(json_encode(['type' => 'error', 'message' => 'You must authenticate first.']));
                return;
            }

            $username = $this->users[$from->resourceId];
            $message = htmlspecialchars(trim($data['message']));

            if (empty($message)) {
                return;
            }

            // Save message to database
            $stmt = $this->pdo->prepare('INSERT INTO messages (user_id, message) VALUES (?, ?)');
            $stmt->execute([$user['id'], $message]);

            // Broadcast the message to all clients
            $this->broadcast(json_encode(['type' => 'message', 'username' => $username, 'message' => $message, 'timestamp' => date("Y-m-d H:i:s")]));
        }
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it
        $this->clients->detach($conn);

        if (isset($this->users[$conn->resourceId])) {
            $username = $this->users[$conn->resourceId];
            unset($this->users[$conn->resourceId]);
            // Notify others
            $this->broadcast(json_encode(['type' => 'system', 'message' => "{$username} has left the chat."]));
        }

        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 broadcast($msg, $exclude = null) {
        foreach ($this->clients as $client) {
            if ($client !== $exclude) {
                $client->send($msg);
            }
        }
    }
}
?>
c. Creating the Chat Interface (public/chat.php)

Create a chat.php file inside the public directory to provide the chat interface.

<?php
// public/chat.php

require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../templates/header.php';
?>

<h2>Real-Time Chat</h2>

<div id="chat-container">
    <div id="chat-box" style="height:400px; border:1px solid #ccc; overflow-y:scroll; padding:10px;">
        <!-- Messages will appear here -->
    </div>
    <form id="chat-form">
        <input type="text" id="message-input" placeholder="Type your message here..." autocomplete="off" required style="width:80%;">
        <button type="submit">Send</button>
    </form>
</div>

<script>
// public/assets/js/chat.js

let conn;
const chatBox = document.getElementById('chat-box');
const chatForm = document.getElementById('chat-form');
const messageInput = document.getElementById('message-input');

// Prompt user for credentials
const username = prompt("Enter your username:");
const password = prompt("Enter your password:");

// Establish WebSocket connection
conn = new WebSocket('ws://localhost:8080');

conn.onopen = function(e) {
    console.log("Connection established!");
    // Send authentication data
    conn.send(JSON.stringify({
        type: 'auth',
        username: username,
        password: password
    }));
};

conn.onmessage = function(e) {
    const data = JSON.parse(e.data);

    if (data.type === 'auth') {
        if (data.status === 'success') {
            appendMessage("System", "You have joined the chat.");
        } else {
            alert(data.message);
            conn.close();
        }
    } else if (data.type === 'message') {
        appendMessage(data.username, data.message, data.timestamp);
    } else if (data.type === 'system') {
        appendMessage("System", data.message);
    } else if (data.type === 'error') {
        appendMessage("Error", data.message);
    }
};

conn.onclose = function(e) {
    console.log("Connection closed.");
    appendMessage("System", "You have been disconnected.");
};

conn.onerror = function(e) {
    console.log("Connection error:", e);
};

// Handle form submission
chatForm.addEventListener('submit', function(e) {
    e.preventDefault();
    const message = messageInput.value.trim();
    if (message === '') return;
    conn.send(JSON.stringify({
        type: 'message',
        message: message
    }));
    messageInput.value = '';
});

// Function to append messages to the chat box
function appendMessage(sender, message, timestamp = null) {
    const messageElement = document.createElement('div');
    if (timestamp) {
        const time = new Date(timestamp).toLocaleTimeString();
        messageElement.innerHTML = `<strong>[${time}] ${sender}:</strong> ${message}`;
    } else {
        messageElement.innerHTML = `<strong>${sender}:</strong> ${message}`;
    }
    chatBox.appendChild(messageElement);
    chatBox.scrollTop = chatBox.scrollHeight;
}
</script>

<?php
require_once __DIR__ . '/../templates/footer.php';
?>
d. Creating the Chat Page (public/index.php)

Update the index.php file inside the public directory to link to the chat interface.

<?php
// public/index.php

require_once __DIR__ . '/../templates/header.php';
?>

<h2>Welcome to the Real-Time Chat Application</h2>
<p><a href="chat.php">Join the Chat</a></p>

<?php
require_once __DIR__ . '/../templates/footer.php';
?>

9. Running the WebSocket Server

  1. Start the WebSocket Server:
    • Open your terminal or command prompt.
    • Navigate to the project root directory (chat-app).
    • Run the server using PHP: php server.php
    • You should see the message: WebSocket server started on port 8080

10. Testing the Chat Application

  1. Register Users:
    • Navigate to http://localhost/chat-app/public/register.php.
    • Register multiple users by filling out the registration form.
  2. Login and Access Chat:
    • Navigate to http://localhost/chat-app/public/login.php.
    • Log in with the registered credentials.
    • After logging in, go to http://localhost/chat-app/public/chat.php.
  3. Interact in Chat:
    • Open multiple browser windows or tabs and log in with different users.
    • Send messages and observe real-time updates across all connected clients.
  4. Verify Message Persistence:
    • Messages are stored in the messages table.
    • Implement functionality to load previous messages on connecting if desired.

11. Deployment Considerations

When deploying your Real-Time Chat Application to a live server, consider the following:

  • WebSocket Server Deployment:
    • Deploy the WebSocket server on a separate server or use services that support WebSockets.
    • Ensure that the server is accessible over the internet and secured properly.
  • Security:
    • Implement authentication and authorization to secure the chat system.
    • Use HTTPS and WSS (WebSocket Secure) protocols to encrypt data transmission.
  • Scalability:
    • Plan for scaling the WebSocket server to handle multiple concurrent connections.
    • Consider load balancing and horizontal scaling strategies.
  • Input Sanitization:
    • Sanitize all user inputs to prevent XSS and injection attacks.
  • CORS Configuration:
    • Configure Cross-Origin Resource Sharing (CORS) appropriately to restrict unauthorized access.
  • Error Handling:
    • Implement comprehensive error handling and logging for debugging and monitoring purposes.
  • Session Management:
    • Secure session handling to prevent hijacking and fixation attacks.

12. Enhancements and Best Practices

  • Message History:
    • Load previous chat messages when a user connects to provide context.
  • User Presence Indicators:
    • Show online/offline status of users in the chat.
  • Private Messaging:
    • Implement functionality for users to send private messages to each other.
  • Typing Indicators:
    • Show when a user is typing a message.
  • File Sharing:
    • Allow users to share files, images, or media within the chat.
  • Notifications:
    • Implement desktop or browser notifications for new messages.
  • User Avatars:
    • Allow users to upload profile pictures displayed in the chat.
  • Admin Controls:
    • Provide admins with the ability to moderate the chat, such as deleting inappropriate messages or banning users.
  • Mobile Responsiveness:
    • Ensure that the chat interface is responsive and works well on mobile devices.
  • Integration with Frontend Frameworks:
    • Enhance the user interface using frontend frameworks like React, Vue.js, or Angular for a more dynamic experience.
  • Database Optimization:
    • Optimize database queries and indexes to ensure efficient data retrieval and storage.
  • Use of Libraries and Frameworks:
    • Explore PHP frameworks like Laravel or Symfony to streamline development and leverage built-in features.
Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *