phil / rate-limit
The production guard of rate limits over symfony/rate-limiter: a compiler pass that fails the container build on a limiter counting in a cache pool or a storage each server keeps for itself, and on a rate limit enabled without the component
Requires
- php: >=8.5
- symfony/config: ^8.2
- symfony/dependency-injection: ^8.2
Requires (Dev)
- phpunit/phpunit: ^13.4
- roave/security-advisories: dev-latest
- symfony/cache: ^8.2
- symfony/rate-limiter: ^8.2
Suggests
- symfony/rate-limiter: The rate limiter whose stores the guard checks, and that requireComponent() asks for
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-12 23:51:59 UTC
README
The production guard of rate limits over symfony/rate-limiter: a container that would build a limit that does not
limit fails to build.
A rate limit counts in a store. When that store is a cache pool or a storage each server keeps for itself — the
filesystem pool cache.app is one by default —, an application behind a load balancer counts on each server apart, and
a client can spend the limit once per server. A limit enabled without symfony/rate-limiter installed fails only on
the first request that needs it.
phil/rate-limit turns both into errors of the container build:
RateLimitGuard::requireComponent()refuses a rate limit enabled without the component, and names the option that turns it off;RateLimitGuard::watch()notes each store a bundle configures, and the compiler passRateLimitGuardrefuses, in theprodenvironment, one thatStoreLocalityfinds local to the server: a pool on the filesystem, APCu, array or system adapter ofsymfony/cache, theInMemoryStorageofsymfony/rate-limiter, or aCacheStoragegiven such a pool. An application on one server says so, and its stores are not noted.
It reads the service definitions only: limiting stays the work of symfony/rate-limiter, which the library neither
wraps nor replaces. Several bundles share one hidden parameter and one check, however many of them add the pass.
It is a plain PHP library whose requirements are symfony/config and symfony/dependency-injection; the cache and the
rate limiter components are named by their classes and need not be installed.
Status
| Part | State |
|---|---|
A rate limit refused without symfony/rate-limiter |
Implemented and tested |
Stores noted by configuration path, single_server, the option that turns a limit off |
Implemented and tested |
Local pools by adapter or class, InMemoryStorage, CacheStorage followed to its pool, aliases and parents followed, bounded |
Implemented and tested |
The check in prod only, once for every bundle |
Implemented and tested |
Documentation
| Document | What it covers |
|---|---|
| Installation | Requirements, installing, adding the guard to a bundle. |
| The guard | What is refused, when, the messages, what is local and what is shared. |
| Extension points | Using the guard from a bundle, deciding locality yourself. |
| Testing | The package's tests. |
| Reference | Every public type, with its role. |
Quick start
composer require phil/rate-limit
use Phil\RateLimit\RateLimitGuard; use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; final class AcmeBundle extends AbstractBundle { public function build(ContainerBuilder $container): void { $container->addCompilerPass(new RateLimitGuard()); } public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void { RateLimitGuard::requireComponent(interface_exists(RateLimiterFactoryInterface::class), 'acme.rate_limit'); RateLimitGuard::watch($builder, $config['rate_limit']['cache_pool'], 'acme.rate_limit.cache_pool', $config['rate_limit']['single_server'], 'enabled'); // ... the CacheStorage and the RateLimiterFactory of symfony/rate-limiter } }
Requirements
PHP 8.5 or later, symfony/config and symfony/dependency-injection 8.2 or later.
Contributing
Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.
License
MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.