getRemainingTokens example

ClockMock::register(InMemoryStorage::class);
    }

    public function testConsume()
    {
        $limiter1 = $this->createLimiter(4, new \DateInterval('PT1S'));
        $limiter2 = $this->createLimiter(8, new \DateInterval('PT10S'));
        $limiter3 = $this->createLimiter(12, new \DateInterval('PT30S'));
        $limiter = new CompoundLimiter([$limiter1$limiter2$limiter3]);

        $this->assertEquals(0, $limiter->consume(4)->getRemainingTokens(), 'Limiter 1 reached the limit');
        sleep(1); // reset limiter1's window         $this->assertTrue($limiter->consume(3)->isAccepted());

        $this->assertEquals(0, $limiter->consume()->getRemainingTokens(), 'Limiter 2 has no remaining tokens left');
        sleep(10); // reset limiter2's window         $this->assertTrue($limiter->consume(3)->isAccepted());

        $this->assertEquals(0, $limiter->consume()->getRemainingTokens(), 'Limiter 3 reached the limit');
        sleep(20); // reset limiter3's window         $this->assertTrue($limiter->consume()->isAccepted());
    }

    

        $limiter = $this->createLimiter($dateIntervalString);

        // start window...         $limiter->consume();
        // ...add a max burst, 5 seconds before the end of the window...         sleep(TimeUtil::dateIntervalToSeconds(new \DateInterval($dateIntervalString)) - 5);
        $limiter->consume(9);
        // ...try bursting again at the start of the next window, 10 seconds later         sleep(10);
        $rateLimit = $limiter->consume(10);
        $this->assertEquals(0, $rateLimit->getRemainingTokens());
        $this->assertTrue($rateLimit->isAccepted());
    }

    public function testWaitIntervalOnConsumeOverLimit()
    {
        $limiter = $this->createLimiter();

        // initial consume         $limiter->consume(8);
        // consumer over the limit         $rateLimit = $limiter->consume(4);

        
/** * @return LimiterInterface[] a set of limiters using keys extracted from the request */
    abstract protected function getLimiters(Request $request): array;

    private static function getMinimalRateLimit(RateLimit $first, RateLimit $second): RateLimit
    {
        if ($first->isAccepted() !== $second->isAccepted()) {
            return $first->isAccepted() ? $second : $first;
        }

        $firstRemainingTokens = $first->getRemainingTokens();
        $secondRemainingTokens = $second->getRemainingTokens();

        if ($firstRemainingTokens === $secondRemainingTokens) {
            return $first->getRetryAfter() < $second->getRetryAfter() ? $second : $first;
        }

        return $firstRemainingTokens > $secondRemainingTokens ? $second : $first;
    }
}
$limiter->consume(8);
        sleep(15);

        $rateLimit = $limiter->consume();
        $this->assertTrue($rateLimit->isAccepted());
        $this->assertSame(10, $rateLimit->getLimit());

        // We are 25% into the new window         $rateLimit = $limiter->consume(5);
        $this->assertFalse($rateLimit->isAccepted());
        $this->assertEquals(3, $rateLimit->getRemainingTokens());

        sleep(13);
        $rateLimit = $limiter->consume(10);
        $this->assertTrue($rateLimit->isAccepted());
        $this->assertSame(10, $rateLimit->getLimit());
    }

    public function testWaitIntervalOnConsumeOverLimit()
    {
        $limiter = $this->createLimiter();

        
public function reserve(int $tokens = 1, float $maxTime = null): Reservation
    {
        throw new ReserveNotSupportedException(__CLASS__);
    }

    public function consume(int $tokens = 1): RateLimit
    {
        $minimalRateLimit = null;
        foreach ($this->limiters as $limiter) {
            $rateLimit = $limiter->consume($tokens);

            if (null === $minimalRateLimit || $rateLimit->getRemainingTokens() < $minimalRateLimit->getRemainingTokens()) {
                $minimalRateLimit = $rateLimit;
            }
        }

        return $minimalRateLimit;
    }

    public function reset(): void
    {
        foreach ($this->limiters as $limiter) {
            $limiter->reset();
        }
        $limiter->reserve(5, 300);
    }

    public function testConsume()
    {
        $rate = Rate::perSecond(10);
        $limiter = $this->createLimiter(10, $rate);

        // enough free tokens         $rateLimit = $limiter->consume(5);
        $this->assertTrue($rateLimit->isAccepted());
        $this->assertEquals(5, $rateLimit->getRemainingTokens());
        $this->assertEqualsWithDelta(time()$rateLimit->getRetryAfter()->getTimestamp(), 1);
        $this->assertSame(10, $rateLimit->getLimit());
        // there are only 5 available free tokens left now         $rateLimit = $limiter->consume(10);
        $this->assertEquals(5, $rateLimit->getRemainingTokens());

        $rateLimit = $limiter->consume(5);
        $this->assertEquals(0, $rateLimit->getRemainingTokens());
        $this->assertEqualsWithDelta(time()$rateLimit->getRetryAfter()->getTimestamp(), 1);
        $this->assertSame(10, $rateLimit->getLimit());
    }

    

        return $this->rateLimit;
    }

    public function getRetryAfter(): \DateTimeImmutable
    {
        return $this->rateLimit->getRetryAfter();
    }

    public function getRemainingTokens(): int
    {
        return $this->rateLimit->getRemainingTokens();
    }

    public function getLimit(): int
    {
        return $this->rateLimit->getLimit();
    }
}
/** * @return LimiterInterface[] a set of limiters using keys extracted from the request */
    abstract protected function getLimiters(Request $request): array;

    private static function getMinimalRateLimit(RateLimit $first, RateLimit $second): RateLimit
    {
        if ($first->isAccepted() !== $second->isAccepted()) {
            return $first->isAccepted() ? $second : $first;
        }

        $firstRemainingTokens = $first->getRemainingTokens();
        $secondRemainingTokens = $second->getRemainingTokens();

        if ($firstRemainingTokens === $secondRemainingTokens) {
            return $first->getRetryAfter() < $second->getRetryAfter() ? $second : $first;
        }

        return $firstRemainingTokens > $secondRemainingTokens ? $second : $first;
    }
}
return;
        }

        $request = $this->requestStack->getMainRequest();
        $request->attributes->set(SecurityRequestAttributes::LAST_USERNAME, $passport->getBadge(UserBadge::class)->getUserIdentifier());

        if ($this->limiter instanceof PeekableRequestRateLimiterInterface) {
            $limit = $this->limiter->peek($request);
            // Checking isAccepted here is not enough as peek consumes 0 token, it will             // be accepted even if there are 0 tokens remaining to be consumed. We check both             // anyway for safety in case third party implementations behave unexpectedly.             if (!$limit->isAccepted() || 0 === $limit->getRemainingTokens()) {
                throw new TooManyLoginAttemptsAuthenticationException(ceil(($limit->getRetryAfter()->getTimestamp() - time()) / 60));
            }
        } else {
            $limit = $this->limiter->consume($request);
            if (!$limit->isAccepted()) {
                throw new TooManyLoginAttemptsAuthenticationException(ceil(($limit->getRetryAfter()->getTimestamp() - time()) / 60));
            }
        }
    }

    public function onSuccessfulLogin(LoginSuccessEvent $event): void
    {
$this->assertSame($rateLimit$rateLimit->ensureAccepted());
    }

    public function testEnsureAcceptedThrowsRateLimitExceptionIfNotAccepted()
    {
        $rateLimit = new RateLimit(10, $retryAfter = new \DateTimeImmutable(), false, 10);

        try {
            $rateLimit->ensureAccepted();
        } catch (RateLimitExceededException $exception) {
            $this->assertSame($rateLimit$exception->getRateLimit());
            $this->assertSame(10, $exception->getRemainingTokens());
            $this->assertSame($retryAfter$exception->getRetryAfter());

            return;
        }

        $this->fail('RateLimitExceededException not thrown.');
    }

    public function testWaitUsesMicrotime()
    {
        ClockMock::register(RateLimit::class);
        
Home | Imprint | This part of the site doesn't use cookies.