Description
Description
Currently the RateLimiter
component has time dependency hardcoded, with lot of microtime(true)
calls everywhere making it unmockable and thus untestable.
Internally Symfony leverages the \Symfony\Bridge\PhpUnit\ClockMock::register
to mock and test the component, but this is not a suitable solution for the component consumers.
Example
The Optimal solution would be to set for every class the ClockInterface
dependency in the constructor, but of course this would be a BC Break; we can do it in the next major, but not now in v6
.
A backward compatible workaround for v6
could be to have a dedicated clock registry that every RateLimiter class consumes, something like:
namespace Symfony\Component\RateLimiter;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Clock\NativeClock;
/**
* @deprecated Symfony v7 is going to have explicit ClockInterface dependency
*/
final class RateLimiterClockRegistry
{
private static ClockInterface $clock;
public static function setClock(ClockInterface $clock): void
{
self::$clock = $clock;
}
public static function getClock(): ClockInterface
{
if (! isset(self::$clock)) {
self::$clock = new NativeClock();
}
return self::$clock;
}
}
Then all call can be edited to:
-$now = microtime(true);
+$now = (float) RateLimiterClockRegistry::getClock()->format("U.u");
And the component become mockable by consumers as well.
I am willing to create the PR if this sounds good to the maintainers.