Skip to content
← Back to Functions
Code

Rate Limit Gate

Tracks request attempts by key and determines whether a request should be allowed or delayed.

Function signature

ogRateLimitGate(attempts, max_attempts = 5, window_seconds = 300, current_time = 0)

Categories

  • Security

Parameters

attemptsPrior Unix timestamps for this identity key.max_attemptsMaximum attempts inside the window.window_secondsWindow size in seconds.current_timeOptional current Unix timestamp.

Return value

Short public-safe status message.

  • allowed
  • retry_after
  • recent_attempts
  • attempt_count
  • max_attempts
  • window_seconds

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Evaluates whether a request should pass a fixed-window rate limit.
 *
 * The caller owns persistence. Pass recent attempt timestamps for the identity
 * key being checked, then store the returned attempts array after recording a
 * denied or accepted attempt according to the controller policy.
 *
 * @param array $attempts Prior Unix timestamps for this identity key.
 * @param int $max_attempts Maximum attempts inside the window.
 * @param int $window_seconds Window size in seconds.
 * @param int $current_time Optional current Unix timestamp.
 * @return array Rate-limit decision and retained attempt timestamps.
 */
function ogRateLimitGate($attempts, $max_attempts = 5, $window_seconds = 300, $current_time = 0) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($attempts)) {
		$attempts = array();
	}

	$max_attempts = (int)$max_attempts;
	$window_seconds = (int)$window_seconds;
	$current_time = (int)$current_time;

	if ($current_time <= 0) {
		$current_time = time();
	}

	if ($max_attempts < 1) {
		$max_attempts = 1;
	}

	if ($window_seconds < 60) {
		$window_seconds = 60;
	}

	$recent_attempts = array();
	foreach ($attempts as $attempt_time) {
		$attempt_time = (int)$attempt_time;
		if ($attempt_time >= ($current_time - $window_seconds) && $attempt_time <= $current_time) {
			$recent_attempts[] = $attempt_time;
		}
	}

	sort($recent_attempts);
	$allowed = true;
	$retry_after = 0;

	if (count($recent_attempts) >= $max_attempts) {
		$allowed = false;
		$oldest = reset($recent_attempts);
		$retry_after = ($oldest + $window_seconds) - $current_time;
		if ($retry_after < 0) {
			$retry_after = 0;
		}
	}

	$result['success'] = true;
	$result['message'] = 'Rate limit evaluated.';
	$result['data'] = array(
		'allowed' => $allowed,
		'retry_after' => $retry_after,
		'recent_attempts' => $recent_attempts,
		'attempt_count' => count($recent_attempts),
		'max_attempts' => $max_attempts,
		'window_seconds' => $window_seconds
	);

	return $result;
}