What are the best practices for implementing a time-based user lockout system in PHP to prevent brute force attacks?

To prevent brute force attacks, it is important to implement a time-based user lockout system in PHP. This system should track the number of failed login attempts for a user within a certain time frame and lock them out temporarily if they exceed a certain threshold. This helps to thwart automated attacks by forcing attackers to wait before attempting additional logins.

// Define constants for lockout settings
define('MAX_ATTEMPTS', 5);
define('LOCKOUT_TIME', 300); // 5 minutes

// Check if user has exceeded maximum login attempts
function checkLoginAttempts($username) {
    $attempts = $_SESSION['login_attempts'][$username] ?? 0;
    
    if ($attempts >= MAX_ATTEMPTS) {
        if ($_SESSION['lockout_time'][$username] + LOCKOUT_TIME > time()) {
            // User is still locked out
            return true;
        } else {
            // Reset login attempts and lockout time
            $_SESSION['login_attempts'][$username] = 0;
            unset($_SESSION['lockout_time'][$username]);
        }
    }
    
    return false;
}

// Increment login attempts for a user
function incrementLoginAttempts($username) {
    $_SESSION['login_attempts'][$username] = ($_SESSION['login_attempts'][$username] ?? 0) + 1;
    
    if ($_SESSION['login_attempts'][$username] >= MAX_ATTEMPTS) {
        // Set lockout time
        $_SESSION['lockout_time'][$username] = time();
    }
}

// Usage example
$username = 'example_user';

if (checkLoginAttempts($username)) {
    echo 'User is locked out. Please try again later.';
} else {
    // Perform login attempt
    if (login($username, $password)) {
        // Successful login
        // Reset login attempts for the user
        $_SESSION['login_attempts'][$username] = 0;
    } else {
        // Failed login
        incrementLoginAttempts($username);
        echo 'Invalid username or password.';
    }
}