maatify/rate-limiter

PSR-compliant rate limiter with Redis, MongoDB, and MySQL support.

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/maatify/rate-limiter

dev-main 2025-11-07 17:47 UTC

This package is auto-updated.

Last update: 2025-11-07 17:47:57 UTC


README

Maatify.dev

Build Status Current version Packagist PHP Version Support Monthly Downloads Total Downloads License

🧩 Maatify Rate Limiter

A PSR-compliant Rate Limiter library supporting Redis, MongoDB, and MySQL β€” with dynamic driver resolver, middleware integration, and reusable enum contracts.

πŸ”— Ψ¨Ψ§Ω„ΨΉΨ±Ψ¨ΩŠ

βœ… Completed Phases

  • Phase 1 – Environment Setup (Local)
  • Phase 2 – Core Architecture
  • Phase 3 – Storage Drivers
  • Phase 3.1 – Enum Contracts Refactor
  • Phase 4 – Resolver & Middleware
  • Phase 4.1 – Continuous Integration (Docker + GitHub Actions)
  • Phase 5 – Exponential Backoff & Global Limit

βš™οΈ Local Setup

composer install
cp .env.example .env

Then edit .env to match your local database and driver configuration.

🧠 Description

Maatify Rate Limiter provides a unified abstraction for distributed rate limiting with multiple backends (Redis, MongoDB, MySQL) and dynamic resolver support.

It follows PSR-12, PSR-15, and PSR-7 standards, and can be integrated directly with frameworks like Slim or Laravel.

πŸ“‚ Project Structure

maatify-rate-limiter/
β”‚
β”œβ”€β”€ .env.example
β”œβ”€β”€ composer.json
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       └── ci.yml
β”œβ”€β”€ docker-compose.ci.yml
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ Config/
β”‚   β”‚   └── RateLimitConfig.php
β”‚   β”œβ”€β”€ Contracts/
β”‚   β”‚   β”œβ”€β”€ RateLimiterInterface.php
β”‚   β”‚   β”œβ”€β”€ RateLimitActionInterface.php
β”‚   β”‚   └── PlatformInterface.php
β”‚   β”œβ”€β”€ DTO/
β”‚   β”‚   └── RateLimitStatusDTO.php
β”‚   β”œβ”€β”€ Drivers/
β”‚   β”‚   β”œβ”€β”€ RedisRateLimiter.php
β”‚   β”‚   β”œβ”€β”€ MongoRateLimiter.php
β”‚   β”‚   └── MySQLRateLimiter.php
β”‚   β”œβ”€β”€ Enums/
β”‚   β”‚   β”œβ”€β”€ RateLimitActionEnum.php
β”‚   β”‚   └── PlatformEnum.php
β”‚   β”œβ”€β”€ Exceptions/
β”‚   β”‚   └── TooManyRequestsException.php
β”‚   β”œβ”€β”€ Middleware/
β”‚   β”‚   └── RateLimitHeadersMiddleware.php
β”‚   └── Resolver/
β”‚       └── RateLimiterResolver.php
β”‚
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ bootstrap.php
β”‚   β”œβ”€β”€ CoreStructureTest.php
β”‚   β”œβ”€β”€ DriversTest.php
β”‚   └── MiddlewareTest.php
β”‚
β”œβ”€β”€ docs/
β”‚   └── phases/
β”‚       β”œβ”€β”€ README.phase1.md
β”‚       β”œβ”€β”€ README.phase2.md
β”‚       β”œβ”€β”€ README.phase3.md
β”‚       β”œβ”€β”€ README.phase3.1.md
β”‚       β”œβ”€β”€ README.phase4.md
β”‚       └── README.phase4.1.md
β”‚
β”œβ”€β”€ CHANGELOG.md
β”œβ”€β”€ VERSION
└── README.md

🧩 CI/CD Integration (Phase 4.1)

πŸš€ Phase 4.1 introduced full Continuous Integration support via Docker Compose + GitHub Actions.

  • CI runs Redis, MySQL, and MongoDB containers in isolation.
  • PHPUnit runs inside Docker (docker compose run --rm php) with live console output.
  • Auto .env generation during pipeline.
  • Composer caching for faster re-runs.
  • Optional upload of test results (tests/_output).

πŸ’‘ CI Workflow File: .github/workflows/ci.yml πŸ’‘ Docker Stack File: docker-compose.ci.yml

🧩 Current Version

1.0.0-alpha-phase5

🧾 CHANGELOG SUMMARY

Phase 4.1 – Continuous Integration (CI)

  • Added Docker-based CI with docker-compose.ci.yml.
  • Added GitHub Actions workflow .github/workflows/ci.yml.
  • Integrated Redis 7, MySQL 8, and MongoDB 7 containers.
  • Enabled live PHPUnit output inside CI logs.
  • Automated .env creation and Composer caching.
  • Added artifact upload for test results.
  • Completed full integration test environment.

Phase 5 – Exponential Backoff & Global Limit

  • Added adaptive rate-limiting using exponential backoff (2ⁿ logic).
  • Added global per-IP rate limit (across all actions).
  • Extended RateLimitStatusDTO to include backoffSeconds and nextAllowedAt.
  • Added RateLimitStatusDTO::fromArray() for DTO reconstruction.
  • Enhanced TooManyRequestsException to include retry metadata.
  • Updated .env.example with:
    • GLOBAL_RATE_LIMIT
    • GLOBAL_RATE_WINDOW
    • BACKOFF_BASE
    • BACKOFF_MAX
  • Added new unit tests tests/BackoffTest.php.
  • Implemented global per-IP rate tracking for Redis.

βœ… Summary Table

Environment Supported Notes
PHP (raw) βœ… Works out of the box
Slim βœ… Fully PSR-15 compatible
Laravel βœ… Custom middleware ready
Custom Enums βœ… Through interface contracts
Redis / Mongo / MySQL βœ… Switch easily via resolver
PSR Standards βœ… PSR-7 / PSR-15 / PSR-12

πŸ“˜ USAGE EXAMPLES

🧱 1️⃣ Basic Example (Native PHP)

<?php

require 'vendor/autoload.php';

use Maatify\RateLimiter\Resolver\RateLimiterResolver;
use Maatify\RateLimiter\Enums\RateLimitActionEnum;
use Maatify\RateLimiter\Enums\PlatformEnum;
use Maatify\RateLimiter\Exceptions\TooManyRequestsException;

$config = [
    'driver' => 'redis',
    'redis_host' => '127.0.0.1',
    'redis_port' => 6379,
];

$resolver = new RateLimiterResolver($config);
$limiter = $resolver->resolve();

$key = $_SERVER['REMOTE_ADDR'] ?? 'unknown';

try {
    $status = $limiter->attempt($key, RateLimitActionEnum::LOGIN, PlatformEnum::WEB);
    echo "βœ… Allowed. Remaining: {$status->remaining}\n";
} catch (TooManyRequestsException $e) {
    echo "β›” {$e->getMessage()}. Try again later.\n";
}

βš™οΈ 2️⃣ Slim Framework Example (Full Middleware Integration)

use Slim\Factory\AppFactory;
use Maatify\RateLimiter\Resolver\RateLimiterResolver;
use Maatify\RateLimiter\Middleware\RateLimitHeadersMiddleware;
use Maatify\RateLimiter\Enums\RateLimitActionEnum;
use Maatify\RateLimiter\Enums\PlatformEnum;

require __DIR__ . '/vendor/autoload.php';

$app = AppFactory::create();

$config = [
    'driver' => 'redis',
    'redis_host' => '127.0.0.1',
];

$resolver = new RateLimiterResolver($config);
$limiter = $resolver->resolve();

$app->add(new RateLimitHeadersMiddleware(
    $limiter,
    RateLimitActionEnum::LOGIN,
    PlatformEnum::WEB
));

$app->get('/login', function ($request, $response) {
    $response->getBody()->write('Welcome to login endpoint!');
    return $response;
});

$app->run();

πŸ“˜ Output Headers:

X-RateLimit-Limit: 5
X-RateLimit-Remaining: 4
X-RateLimit-Reset: 60
Retry-After: 60

🧩 3️⃣ Laravel Example (Custom Middleware)

πŸ“„ app/Http/Middleware/RateLimitHeaders.php

<?php

namespace App\Http\Middleware;

use Closure;
use Maatify\RateLimiter\Resolver\RateLimiterResolver;
use Maatify\RateLimiter\Enums\RateLimitActionEnum;
use Maatify\RateLimiter\Enums\PlatformEnum;
use Maatify\RateLimiter\Exceptions\TooManyRequestsException;

class RateLimitHeaders
{
    public function handle($request, Closure $next)
    {
        $config = ['driver' => 'redis', 'redis_host' => '127.0.0.1'];
        $resolver = new RateLimiterResolver($config);
        $limiter = $resolver->resolve();

        $key = $request->ip();

        try {
            $status = $limiter->attempt($key, RateLimitActionEnum::API_CALL, PlatformEnum::API);
        } catch (TooManyRequestsException $e) {
            return response()->json([
                'error' => 'Too many requests',
                'retry_after' => $status->retryAfter ?? 60,
            ], 429);
        }

        $response = $next($request);

        return $response
            ->header('X-RateLimit-Limit', $status->limit)
            ->header('X-RateLimit-Remaining', $status->remaining)
            ->header('X-RateLimit-Reset', $status->resetAfter);
    }
}

πŸ“˜ Register in Kernel.php:

'ratelimit' => \App\Http\Middleware\RateLimitHeaders::class,

Usage:

Route::get('/api/orders', [OrderController::class, 'index'])->middleware('ratelimit');

🌍 4️⃣ API JSON Example (Custom Controller)

<?php

use Maatify\RateLimiter\Resolver\RateLimiterResolver;
use Maatify\RateLimiter\Enums\RateLimitActionEnum;
use Maatify\RateLimiter\Enums\PlatformEnum;
use Maatify\RateLimiter\Exceptions\TooManyRequestsException;

$config = ['driver' => 'mysql', 'mysql_dsn' => 'mysql:host=127.0.0.1;dbname=ratelimiter', 'mysql_user' => 'root'];

$resolver = new RateLimiterResolver($config);
$limiter = $resolver->resolve();

header('Content-Type: application/json');
$key = $_SERVER['REMOTE_ADDR'];

try {
    $status = $limiter->attempt($key, RateLimitActionEnum::API_CALL, PlatformEnum::API);

    echo json_encode([
        'success' => true,
        'remaining' => $status->remaining,
        'reset_after' => $status->resetAfter,
    ]);
} catch (TooManyRequestsException $e) {
    http_response_code(429);
    echo json_encode([
        'success' => false,
        'error' => $e->getMessage(),
        'retry_after' => $status->retryAfter ?? 60,
    ]);
}

🧠 5️⃣ Custom Enum Contracts Example (From Phase 3.1)

use Maatify\RateLimiter\Contracts\RateLimitActionInterface;
use Maatify\RateLimiter\Contracts\PlatformInterface;
use Maatify\RateLimiter\Resolver\RateLimiterResolver;

enum MyActionEnum: string implements RateLimitActionInterface
{
    case ORDER_SUBMIT = 'order_submit';
    public function value(): string { return $this->value; }
}

enum MyPlatformEnum: string implements PlatformInterface
{
    case CUSTOMER_APP = 'customer_app';
    public function value(): string { return $this->value; }
}

$config = ['driver' => 'redis'];
$resolver = new RateLimiterResolver($config);
$limiter = $resolver->resolve();

$status = $limiter->attempt('user-501', MyActionEnum::ORDER_SUBMIT, MyPlatformEnum::CUSTOMER_APP);

echo json_encode($status->toArray(), JSON_PRETTY_PRINT);

🧩 6️⃣ Custom Header Key Example (X-API-KEY Mode)

$app->add(new RateLimitHeadersMiddleware(
    $limiter,
    RateLimitActionEnum::API_CALL,
    PlatformEnum::API,
    keyHeader: 'X-API-KEY'
));

🧠 7️⃣ Exponential Backoff Example (Redis Adaptive Mode)

use Maatify\RateLimiter\Resolver\RateLimiterResolver;
use Maatify\RateLimiter\Enums\RateLimitActionEnum;
use Maatify\RateLimiter\Enums\PlatformEnum;
use Maatify\RateLimiter\Exceptions\TooManyRequestsException;

$config = [
    'driver' => 'redis',
    'redis_host' => '127.0.0.1',
    'redis_port' => 6379,
    'backoff_base' => 2,
    'backoff_max' => 3600,
];

$resolver = new RateLimiterResolver($config);
$limiter  = $resolver->resolve();

$key = 'ip:' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');

try {
    $status = $limiter->attempt($key, RateLimitActionEnum::LOGIN, PlatformEnum::WEB);
    echo "βœ… Allowed – Remaining: {$status->remaining}";
} catch (TooManyRequestsException $e) {
    echo "β›” Retry after {$status->backoffSeconds}s (next allowed: {$status->nextAllowedAt})";
}

πŸ“˜ Environment variables:

GLOBAL_RATE_LIMIT=1000
GLOBAL_RATE_WINDOW=3600
BACKOFF_BASE=2
BACKOFF_MAX=3600

βš™οΈ Environment Variables (Rate Limiter Configuration)

These variables control the global rate limit and exponential backoff behavior
used across all actions and IP addresses.

You can define them in your .env, .env.local, or CI environment.

Variable Description Example Type
GLOBAL_RATE_LIMIT Maximum number of allowed actions per IP (or user) within the window duration. 5 integer
GLOBAL_RATE_WINDOW Duration of the rate-limit window in seconds. After this period, counters reset. 60 (1 minute) integer
BACKOFF_BASE Exponential backoff multiplier. Each violation increases delay exponentially by this base value. 2 β†’ 2, 4, 8, 16... integer / float
BACKOFF_MAX Maximum delay (in seconds) allowed by exponential backoff. Prevents unreasonably long waits. 3600 (1 hour) integer

πŸ“˜ Formula:


backoff_seconds = min( BACKOFF_BASE ** violation_count , BACKOFF_MAX )

πŸ” Example .env file

# Basic local testing
GLOBAL_RATE_LIMIT=5
GLOBAL_RATE_WINDOW=60
BACKOFF_BASE=2
BACKOFF_MAX=3600

πŸ’‘ Tips

  • Lower values (e.g., 5 req/min) are recommended for login or OTP endpoints.
  • Higher values are suitable for public APIs.
  • BACKOFF_BASE=2 gives a balanced exponential delay pattern.
  • Always include a Retry-After header in responses to inform the client when to retry.

🧱 Example exponential pattern:

Violation Count Delay (seconds)
1 2
2 4
3 8
4 16
5 32
6 64
... ... up to BACKOFF_MAX

πŸ“¦ Composer Dependencies

To use this library fully:

composer require psr/http-message psr/http-server-middleware psr/http-server-handler

For Slim Framework support:

composer require slim/slim

πŸͺͺ License

MIT license Β© Maatify.dev

You’re free to use, modify, and distribute this library with attribution.

🧱 Authors & Credits

Developed by: Maatify.dev https://www.Maatify.dev

Maintainer: Mohamed Abdulalim

Project: maatify:rate-limiter