PHP

Guide on building a real-time website chat script

Okay, here’s a comprehensive guide on building a real-time website chat script using PHP, HTML, CSS, JavaScript, and MySQL. This outlines the core components and logic involved.

Project Structure:

We will create a basic directory structure for clarity:

  • root/
    • css/
      • style.css
    • js/
      • script.js
    • php/
      • db_connect.php
      • get_messages.php
      • send_message.php
      • login.php
      • logout.php
      • register.php
      • online_users.php
    • index.php

1. Database Setup (MySQL):

First, you’ll need a database to store user information and chat messages. Create a database (e.g., chat_db) and the following tables:

  • users table:
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL, -- Store hashed passwords
    last_active TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

messages table:

CREATE TABLE messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    message TEXT NOT NULL,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

2. Database Connection (php/db_connect.php):

This file establishes a connection to your MySQL database.

<?php

$servername = "localhost";  // Or your server address
$username   = "your_username";
$password   = "your_password";
$dbname     = "chat_db";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

//Set default time zone
date_default_timezone_set('UTC');

?>

Important: Replace "your_username", "your_password", and "chat_db" with your actual database credentials. Never hardcode sensitive information like database credentials directly into public-facing code in a production environment. Use environment variables or configuration files.

3. User Authentication (Login, Registration, Logout):

  • php/register.php:
<?php
require_once 'db_connect.php';

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST["username"];
    $password = $_POST["password"];// Basic validation (add more robust validation)
if (empty($username) || empty($password)) {
    echo json_encode(['status' => 'error', 'message' => 'Username and password are required.']);
    exit;
}

// Check if username already exists
$check_sql = "SELECT id FROM users WHERE username = ?";
$check_stmt = $conn->prepare($check_sql);
$check_stmt->bind_param("s", $username);
$check_stmt->execute();
$check_stmt->store_result();

if ($check_stmt->num_rows > 0) {
    echo json_encode(['status' => 'error', 'message' => 'Username already exists.']);
    $check_stmt->close();
    exit;
}
$check_stmt->close();

// Hash the password
$hashed_password = password_hash($password, PASSWORD_DEFAULT);

// Insert the user into the database
$sql = "INSERT INTO users (username, password) VALUES (?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $username, $hashed_password);

if ($stmt->execute()) {
    echo json_encode(['status' => 'success', 'message' => 'Registration successful.']);
} else {
    echo json_encode(['status' => 'error', 'message' => 'Registration failed: ' . $conn->error]);
}

$stmt->close();
$conn->close();
} else {
    echo json_encode(['status' => 'error', 'message' => 'Invalid request.']);
}
?>

php/login.php:

<?php
require_once 'db_connect.php';
session_start();  // Start the session

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST["username"];
    $password = $_POST["password"];if (empty($username) || empty($password)) {
    echo json_encode(['status' => 'error', 'message' => 'Username and password are required.']);
    exit;
}

$sql = "SELECT id, password FROM users WHERE username = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();

if ($stmt->num_rows == 1) {
    $stmt->bind_result($user_id, $hashed_password);
    $stmt->fetch();

    if (password_verify($password, $hashed_password)) {
        // Password is correct
        $_SESSION["user_id"] = $user_id; // Store user ID in session

        //Update last_active timestamp
        $update_sql = "UPDATE users SET last_active = CURRENT_TIMESTAMP WHERE id = ?";
        $update_stmt = $conn->prepare($update_sql);
        $update_stmt->bind_param("i", $user_id);
        $update_stmt->execute();
        $update_stmt->close();


        echo json_encode(['status' => 'success', 'message' => 'Login successful.']);
    } else {
        echo json_encode(['status' => 'error', 'message' => 'Invalid username or password.']);
    }
} else {
    echo json_encode(['status' => 'error', 'message' => 'Invalid username or password.']);
}

$stmt->close();
$conn->close();
} else {
    echo json_encode(['status' => 'error', 'message' => 'Invalid request.']);
}
?>

php/logout.php:

<?php
session_start();
session_unset();
session_destroy();
echo json_encode(['status' => 'success', 'message' => 'Logged out.']);
exit;
?>

index.php (HTML – Login/Registration Form):

<!DOCTYPE html>
<html>
<head>
    <title>Chat</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div id="login-register-container">
        <div id="login-form">
            <h2>Login</h2>
            <input type="text" id="login-username" placeholder="Username"><br>
            <input type="password" id="login-password" placeholder="Password"><br>
            <button id="login-button">Login</button>
            <p id="login-message"></p>
        </div>    <div id="register-form">
        <h2>Register</h2>
        <input type="text" id="register-username" placeholder="Username"><br>
        <input type="password" id="register-password" placeholder="Password"><br>
        <button id="register-button">Register</button>
        <p id="register-message"></p>
    </div>
</div>


<div id="chat-container" style="display:none;">
    <div id="chat-messages"></div>
    <input type="text" id="message-input" placeholder="Type your message...">
    <button id="send-button">Send</button>
    <button id="logout-button">Logout</button>
    <div id="online-users"></div>
</div>

<script src="js/script.js"></script>
</body>
</html>

4. Chat Functionality (Sending, Retrieving, Displaying Messages):

  • php/send_message.php:
<?php
session_start();
require_once 'db_connect.php';

if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_SESSION["user_id"])) {
    $user_id = $_SESSION["user_id"];
    $message = $_POST["message"];if (empty($message)) {
    echo json_encode(['status' => 'error', 'message' => 'Message cannot be empty.']);
    exit;
}

$sql = "INSERT INTO messages (user_id, message) VALUES (?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("is", $user_id, $message);


if ($stmt->execute()) {
    echo json_encode(['status' => 'success', 'message' => 'Message sent.']);
} else {
    echo json_encode(['status' => 'error', 'message' => 'Failed to send message: ' . $conn->error]);
}

$stmt->close();
$conn->close();
} else {
    echo json_encode(['status' => 'error', 'message' => 'Invalid request.']);
}
?>

php/get_messages.php:

<?php
require_once 'db_connect.php';

$last_message_id = isset($_GET['last_message_id']) ? intval($_GET['last_message_id']) : 0;

$sql = "SELECT messages.id, messages.message, messages.timestamp, users.username
        FROM messages
        INNER JOIN users ON messages.user_id = users.id
        WHERE messages.id > ?
        ORDER BY messages.timestamp ASC";



$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $last_message_id);
$stmt->execute();
$result = $stmt->get_result();

$messages = array();
while ($row = $result->fetch_assoc()) {
    $messages[] = $row;
}

echo json_encode($messages);

$stmt->close();
$conn->close();
?>

php/online_users.php:

<?php
require_once 'db_connect.php';

// Set inactivity threshold (e.g., 5 minutes = 300 seconds)
$inactivity_threshold = 300;

// Calculate the timestamp for inactive users
$inactive_timestamp = date('Y-m-d H:i:s', time() - $inactivity_threshold);

// Query to get online users (last_active within the threshold)
$sql = "SELECT id, username FROM users WHERE last_active >= ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $inactive_timestamp);  // Binding as string because it it's a datetime
$stmt->execute();
$result = $stmt->get_result();


$online_users = array();
while ($row = $result->fetch_assoc()) {
    $online_users[] = $row;
}

echo json_encode($online_users);

$stmt->close();
$conn->close();
?>

5. JavaScript (js/script.js): This file handles the client-side logic, including AJAX requests to your PHP scripts.

document.addEventListener('DOMContentLoaded', function() {
    let loginForm = document.getElementById('login-form');
    let registerForm = document.getElementById('register-form');
    let chatContainer = document.getElementById('chat-container');

    let loginButton = document.getElementById('login-button');
    let registerButton = document.getElementById('register-button');
    let logoutButton = document.getElementById('logout-button');
    let sendMessageButton = document.getElementById('send-button');

    let loginMessage = document.getElementById('login-message');
    let registerMessage = document.getElementById('register-message');

    let chatMessages = document.getElementById('chat-messages');
    let messageInput = document.getElementById('message-input');
    let onlineUsersDiv = document.getElementById('online-users');

    let lastMessageId = 0; // Keep track of the last message ID


    // ---- Authentication Functions ----
    function login(username, password) {
        fetch('php/login.php', {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: `username=${username}&password=${password}`
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                loginForm.style.display = 'none';
                registerForm.style.display = 'none';
                chatContainer.style.display = 'block';
                loginMessage.textContent = '';

                //Start fetching messages and online users
                lastMessageId = 0; // Reset when logging in
                loadMessages();
                loadOnlineUsers();

                //Start periodic updates
                setInterval(loadMessages, 2000); // Refresh messages every 2 seconds
                setInterval(loadOnlineUsers, 15000); // Refresh online users every 15 seconds.

            } else {
                loginMessage.textContent = data.message;
            }
        })
        .catch(error => {
            console.error('Login error:', error);
            loginMessage.textContent = 'Login failed.';
        });
    }

    function register(username, password) {
       fetch('php/register.php', {
           method: 'POST',
           headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
           body: `username=${username}&password=${password}`
       })
       .then(response => response.json())
       .then(data => {
           registerMessage.textContent = data.message;
       })
       .catch(error => {
           console.error('Registration error: ', error);
           registerMessage.textContent = 'Registration failed.';
       });
    }

    function logout() {
        fetch('php/logout.php')
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                chatContainer.style.display = 'none';
                loginForm.style.display = 'block';
                registerForm.style.display = 'block';
                chatMessages.innerHTML = ''; // Clear messages
                onlineUsersDiv.innerHTML = '';  //Clear online users
            }
        })
        .catch(error => console.error('Logout error:', error));
    }



    // ---- Chat Functions ----

    function sendMessage(message) {
        fetch('php/send_message.php', {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: `message=${message}`
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                messageInput.value = ''; // Clear the input
                //loadMessages();  //Refresh messages  - No need with automatic loading
            } else {
                console.error('Send message error: ',  data.message);
            }
        })
        .catch(error => console.error('Send message error:', error));
    }

    function loadMessages() {
        fetch(`php/get_messages.php?last_message_id=${lastMessageId}`)
        .then(response => response.json())
        .then(data => {
            if (data.length > 0) {
                data.forEach(message => {
                    const messageDiv = document.createElement('div');
                    messageDiv.classList.add('message');
                    messageDiv.innerHTML = `<strong>${message.username}:</strong> ${message.message} <span class="timestamp">(${message.timestamp})</span>`; // Include timestamp
                    chatMessages.appendChild(messageDiv);
                    lastMessageId = message.id;  //Update the last ID
                });

                // Scroll to the bottom of chat
                chatMessages.scrollTop = chatMessages.scrollHeight;
            }
        })
        .catch(error => console.error('Load messages error:', error));
    }


    function loadOnlineUsers() {
        fetch('php/online_users.php')
        .then(response => response.json())
        .then(data => {
            onlineUsersDiv.innerHTML = '<h3>Online Users:</h3>'; //Clear previous list
            if(data.length > 0) {
                let userList = '<ul>';
                data.forEach(user => {
                    userList += `<li>${user.username}</li>`
                });
                userList += '</ul>';
                onlineUsersDiv.innerHTML += userList;
            } else {
                onlineUsersDiv.innerHTML += '<p>No users online.</p>';
            }
        })
        .catch(error => console.error('Load online users error: ', error));
    }


    // ---- Event Listeners ----

    loginButton.addEventListener('click', function() {
        login(document.getElementById('login-username').value, document.getElementById('login-password').value);
    });

    registerButton.addEventListener('click', function() {
        register(document.getElementById('register-username').value, document.getElementById('register-password').value);
    });

    logoutButton.addEventListener('click', logout);

    sendMessageButton.addEventListener('click', function() {
        sendMessage(messageInput.value);
    });

    messageInput.addEventListener('keydown', function(event) {
        if (event.key === 'Enter') {
            sendMessage(messageInput.value);
        }
    });

});

6. CSS (css/style.css):

Provide styling to make the chat interface visually appealing. Here’s a basic example:

body {
    font-family: sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
}

#login-register-container {
    width: 400px;
    margin: 50px auto;
    background-color: #fff;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

#login-form, #register-form {
    margin-bottom: 20px;
}

h2 {
    text-align: center;
    color: #333;
}

input[type="text"],
input[type="password"] {
    width: 90%;
    padding: 10px;
    margin-bottom: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}

button {
    background-color: #5cb85c;
    color: white;
    padding: 10px 15px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    width: 100%;
}

button:hover {
    background-color: #449d44;
}

#chat-container {
    width: 600px;
    margin: 50px auto;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    padding: 20px;
}

#chat-messages {
    height: 300px;
    overflow-y: scroll;
    padding: 10px;
    border: 1px solid #ddd;
    margin-bottom: 10px;
}

#chat-messages .message {
    margin-bottom: 5px;
    padding: 5px;
    border-bottom: 1px solid #eee;
}

#chat-messages .message .timestamp {
    font-size: 0.8em;
    color: #888;
    margin-left: 5px;
}

#message-input {

    width: 75%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-right: 10px;
}

#send-button, #logout-button{
    width: 20%;
}

#online-users {
    margin-top: 20px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}

#online-users ul {
    list-style-type: none;
    padding: 0;
}

#online-users li {
    margin-bottom: 5px;
}

Key Considerations and Improvements:

  • Security: Input sanitization and output encoding are crucial to prevent XSS vulnerabilities. Always sanitize user input before using it in database queries or displaying it on the page. Use prepared statements to prevent SQL injection.
  • Password Hashing: Always use password_hash() to securely store passwords and password_verify() to compare them.
  • Error Handling: Implement comprehensive error handling (try-catch blocks, logging) to catch and report unexpected issues.
  • Scalability: For high-traffic applications, consider using a more scalable solution for real-time communication, such as WebSockets (e.g., using Ratchet, Swoole or similar) or a cloud-based real-time messaging service (e.g., Pusher, Ably). Polling, as used in this example, can become inefficient with many users.
  • User Interface: Enhance the UI with features like typing indicators, message delivery confirmations, message editing, and user avatars.
  • Message Persistence: Implement message history and retrieval when a user logs back in.
  • Real-time Updates: Implement real-time updates to messages by using techniques such as long-polling or WebSockets to push updates instead of polling.
  • AJAX Error Handling: The JavaScript code should include more robust error handling for AJAX requests. Display appropriate error messages to the user if a request fails.
  • Input Validation: Add client-side and server-side validation for user input to prevent invalid data from being submitted.
  • Session Management: Implement secure session management to prevent session hijacking. Use session_regenerate_id() periodically.
  • Code Organization: Consider using a PHP framework (e.g., Laravel, Symfony) to structure your code more effectively and take advantage of built-in features.
  • Cross-Site Request Forgery (CSRF) Protection: Implement CSRF protection to prevent malicious attacks that can execute unwanted actions on behalf of an authenticated user.
  • Data Sanitization: Always sanitize user input on the server-side to prevent Cross-Site Scripting (XSS) attacks. Use functions like htmlspecialchars() or strip_tags() to escape special characters.

This detailed walkthrough provides a foundation for building a real-time website chat script. Remember to adapt and expand upon it based on your specific requirements. Remember to address the “Key Considerations and Improvements” points to create a secure, efficient, and user-friendly chat application.

Victoria

Im just a girl who hanging around with her friends ;)

Recent Posts

Building Your Next Project with wp-scripts: A Comprehensive Guide

WordPress development has evolved significantly, and modern tooling plays a crucial role in creating efficient…

6 days ago

Script for automatically informing search engines about new content on website

I. Project Overview The goal is to automate the process of notifying search engines (like…

2 weeks ago

Creating an XML sitemap script with PHP, designed for automated updates via CRON

1. Database Structure (MySQL) We'll need a database table to store information about our website's…

2 weeks ago

Comprehensive guide on building a URL shortening script

This explanation aims to provide a solid foundation for understanding the process and implementing your…

2 weeks ago

Comprehensive guide on creating a simple website analytics system

Comprehensive guide on creating a simple website analytics system using PHP, HTML, CSS, JavaScript, and…

2 weeks ago

Building a file upload and download system in PHP

I. Database Setup (MySQL) The first step is setting up a database to store file…

2 weeks ago