1. <?php
  2. /**
  3. * API Endpoint Rate Limiter for Scheduled Runs
  4. *
  5. * Reads configuration from a file to define limits and enforces them.
  6. */
  7. class RateLimiter {
  8. private $config;
  9. private $request_counts = []; // Stores request counts per endpoint and user/identifier
  10. public function __construct(string $config_file) {
  11. $this->config = $this->loadConfig($config_file);
  12. }
  13. /**
  14. * Loads the configuration from the specified file.
  15. * @param string $config_file Path to the configuration file.
  16. * @return array|null An associative array containing the configuration data.
  17. */
  18. private function loadConfig(string $config_file): ?array {
  19. if (file_exists($config_file)) {
  20. $config = json_decode(file_get_contents($config_file), true);
  21. if (json_last_error() !== JSON_ERROR_NONE) {
  22. error_log("Error decoding config file: " . json_last_error_msg());
  23. return null;
  24. }
  25. return $config;
  26. } else {
  27. error_log("Config file not found: " . $config_file);
  28. return null;
  29. }
  30. }
  31. /**
  32. * Checks if an API endpoint request is allowed based on the configuration.
  33. * @param string $endpoint The API endpoint being called.
  34. * @param string $user_identifier A unique identifier for the user/system making the request.
  35. * @return bool True if the request is allowed, false otherwise.
  36. */
  37. public function isAllowed(string $endpoint, string $user_identifier): bool {
  38. if (!isset($this->config[$endpoint])) {
  39. // Endpoint not configured, allow unlimited requests.
  40. return true;
  41. }
  42. $limit = $this->config[$endpoint]['limit'];
  43. $window = $this->config[$endpoint]['window'];
  44. if (empty($this->request_counts[$user_identifier][$endpoint])) {
  45. $this->request_counts[$user_identifier][$endpoint] = [
  46. 'count' => 0,
  47. 'last_reset' => time()
  48. ];
  49. }
  50. $current_count = $this->request_counts[$user_identifier][$endpoint]['count'];
  51. if (time() - $this->request_counts[$user_identifier][$endpoint]['last_reset'] > $window) {
  52. $current_count = 0; // Reset count at the beginning of the window
  53. }
  54. if ($current_count < $limit) {
  55. $this->request_counts[$user_identifier][$endpoint]['count']++;
  56. $this->request_counts[$user_identifier][$endpoint]['last_reset'] = time();
  57. return true;
  58. } else {
  59. return false; // Rate limit exceeded
  60. }
  61. }
  62. /**
  63. * Resets the request count for a user and endpoint.
  64. * @param string $user_identifier The identifier of the user.
  65. * @param string $endpoint The API endpoint.
  66. */
  67. public function resetCount(string $user_identifier, string $endpoint): void {
  68. if (isset($this->request_counts[$user_identifier][$endpoint])) {
  69. $this->request_counts[$user_identifier][$endpoint]['count'] = 0;
  70. $this->request_counts[$user_identifier][$endpoint]['last_reset'] = time();
  71. }
  72. }
  73. }
  74. // Example Usage (within your API endpoint handler)
  75. /*
  76. $rateLimiter = new RateLimiter('config.json');
  77. if ($rateLimiter->isAllowed('scheduled_task', 'user123')) {
  78. // Process the API request
  79. echo "Request allowed!";
  80. } else {
  81. // Rate limit exceeded
  82. header('HTTP/1.1 429 Too Many Requests');
  83. echo 'Rate limit exceeded. Please try again later.';
  84. exit;
  85. }
  86. */
  87. ?>

Add your comment