1212namespace Symfony \Component \RateLimiter \Policy ;
1313
1414use Symfony \Component \Lock \LockInterface ;
15- use Symfony \Component \RateLimiter \Exception \ReserveNotSupportedException ;
15+ use Symfony \Component \RateLimiter \Exception \MaxWaitDurationExceededException ;
1616use Symfony \Component \RateLimiter \LimiterInterface ;
1717use Symfony \Component \RateLimiter \RateLimit ;
1818use Symfony \Component \RateLimiter \Reservation ;
@@ -48,11 +48,10 @@ public function __construct(string $id, int $limit, \DateInterval $interval, Sto
4848
4949public function reserve (int $ tokens =1 ,float $ maxTime =null ):Reservation
5050 {
51- throw new ReserveNotSupportedException (__CLASS__ );
52- }
51+ if ($ tokens >$ this ->limit ) {
52+ throw new \InvalidArgumentException (sprintf ('Cannot reserve more tokens (%d) than the size of the rate limiter (%d). ' ,$ tokens ,$ this ->limit ));
53+ }
5354
54- public function consume (int $ tokens =1 ):RateLimit
55- {
5655$ this ->lock ?->acquire(true );
5756
5857try {
@@ -63,22 +62,43 @@ public function consume(int $tokens = 1): RateLimit
6362$ window = SlidingWindow::createFromPreviousWindow ($ window ,$ this ->interval );
6463 }
6564
65+ $ now =microtime (true );
6666$ hitCount =$ window ->getHitCount ();
6767$ availableTokens =$ this ->getAvailableTokens ($ hitCount );
68- if ($ availableTokens <$ tokens ) {
69- return new RateLimit ($ availableTokens ,$ window ->getRetryAfter (),false ,$ this ->limit );
70- }
68+ if ($ availableTokens >=$ tokens ) {
69+ $ window ->add ($ tokens );
7170
72- $ window ->add ($ tokens );
71+ $ reservation =new Reservation ($ now ,new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()), \DateTimeImmutable::createFromFormat ('U ' ,floor ($ now )),true ,$ this ->limit ));
72+ }else {
73+ $ waitDuration =$ window ->calculateTimeForTokens ($ this ->limit ,max (1 ,$ tokens ));
74+
75+ if (null !==$ maxTime &&$ waitDuration >$ maxTime ) {
76+ // process needs to wait longer than set interval
77+ throw new MaxWaitDurationExceededException (sprintf ('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds). ' ,$ waitDuration ,$ maxTime ),new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()), \DateTimeImmutable::createFromFormat ('U ' ,floor ($ now +$ waitDuration )),false ,$ this ->limit ));
78+ }
79+
80+ $ window ->add ($ tokens );
81+
82+ $ reservation =new Reservation ($ now +$ waitDuration ,new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()), \DateTimeImmutable::createFromFormat ('U ' ,floor ($ now +$ waitDuration )),false ,$ this ->limit ));
83+ }
7384
7485if (0 <$ tokens ) {
7586$ this ->storage ->save ($ window );
7687 }
77-
78- return new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()),$ window ->getRetryAfter (),true ,$ this ->limit );
7988 }finally {
8089$ this ->lock ?->release();
8190 }
91+
92+ return $ reservation ;
93+ }
94+
95+ public function consume (int $ tokens =1 ):RateLimit
96+ {
97+ try {
98+ return $ this ->reserve ($ tokens ,0 )->getRateLimit ();
99+ }catch (MaxWaitDurationExceededException $ e ) {
100+ return $ e ->getRateLimit ();
101+ }
82102 }
83103
84104private function getAvailableTokens (int $ hitCount ):int