0 follower

Final Class Yiisoft\Yii\RateLimiter\Counter

InheritanceYiisoft\Yii\RateLimiter\Counter
ImplementsYiisoft\Yii\RateLimiter\CounterInterface

Counter implements generic cell rate limit algorithm (GCRA) that ensures that after reaching the limit further increments are distributed equally.

Public Methods

Hide inherited methods

Method Description Defined By
__construct() Yiisoft\Yii\RateLimiter\Counter
hit() Determines when the next hit won't be limited. Should be called on each request. Yiisoft\Yii\RateLimiter\Counter

Constants

Hide inherited constants

Constant Value Description Defined By
DEFAULT_MAX_CAS_ATTEMPTS 10 Yiisoft\Yii\RateLimiter\Counter
DEFAULT_TTL 86400 Yiisoft\Yii\RateLimiter\Counter
ID_PREFIX 'rate-limiter-' Yiisoft\Yii\RateLimiter\Counter
MILLISECONDS_PER_SECOND 1000 Yiisoft\Yii\RateLimiter\Counter

Method Details

Hide inherited methods

__construct() public method

public mixed __construct ( Yiisoft\Yii\RateLimiter\Storage\StorageInterface $storage, integer $limit, integer $periodInSeconds, integer $storageTtlInSeconds self::DEFAULT_TTL, string $storagePrefix self::ID_PREFIX, Yiisoft\Yii\RateLimiter\Time\TimerInterface|null $timer null, integer $maxCasAttempts self::DEFAULT_MAX_CAS_ATTEMPTS )
$storage Yiisoft\Yii\RateLimiter\Storage\StorageInterface

Storage to use for counter values.

$limit integer

Maximum number of increments that could be performed before increments are limited.

$periodInSeconds integer

Period to apply limit to.

$storageTtlInSeconds integer

Storage TTL. Should be higher than $periodInSeconds.

$storagePrefix string

Storage prefix.

$timer Yiisoft\Yii\RateLimiter\Time\TimerInterface|null

Timer instance to get current time from.

$maxCasAttempts integer

Maximum number of times to retry saveIfNotExists/saveCompareAndSwap operations before returning an error.

                public function __construct(
    private StorageInterface $storage,
    private int $limit,
    int $periodInSeconds,
    private int $storageTtlInSeconds = self::DEFAULT_TTL,
    private string $storagePrefix = self::ID_PREFIX,
    TimerInterface|null $timer = null,
    private int $maxCasAttempts = self::DEFAULT_MAX_CAS_ATTEMPTS,
) {
    if ($limit < 1) {
        throw new InvalidArgumentException('The limit must be a positive value.');
    }
    if ($periodInSeconds < 1) {
        throw new InvalidArgumentException('The period must be a positive value.');
    }
    $this->periodInMilliseconds = $periodInSeconds * self::MILLISECONDS_PER_SECOND;
    $this->timer = $timer ?: new MicrotimeTimer();
    $this->incrementIntervalInMilliseconds = $this->periodInMilliseconds / $this->limit;
}

            
hit() public method

Determines when the next hit won't be limited. Should be called on each request.

public Yiisoft\Yii\RateLimiter\CounterState hit ( string $id )
$id string

Counter ID. Counters with distinct IDs do not affect each other. For example, using a current user ID will limit for the current user and using IP will limit by IP.

return Yiisoft\Yii\RateLimiter\CounterState

Information about when the next hit won't be limited.

                public function hit(string $id): CounterState
{
    $attempts = 0;
    $isFailStoreUpdatedData = false;
    do {
        // Last increment time.
        // In GCRA it's known as arrival time.
        $lastIncrementTimeInMilliseconds = $this->timer->nowInMilliseconds();
        $lastStoredTheoreticalNextIncrementTime = $this->getLastStoredTheoreticalNextIncrementTime($id);
        $theoreticalNextIncrementTime = $this->calculateTheoreticalNextIncrementTime(
            $lastIncrementTimeInMilliseconds,
            $lastStoredTheoreticalNextIncrementTime
        );
        $remaining = $this->calculateRemaining($lastIncrementTimeInMilliseconds, $theoreticalNextIncrementTime);
        $resetAfter = $this->calculateResetAfter($theoreticalNextIncrementTime);
        if ($remaining === 0) {
            break;
        }
        $isStored = $this->storeTheoreticalNextIncrementTime(
            $id,
            $theoreticalNextIncrementTime,
            $lastStoredTheoreticalNextIncrementTime
        );
        if ($isStored) {
            break;
        }
        $attempts++;
        if ($attempts >= $this->maxCasAttempts) {
            $isFailStoreUpdatedData = true;
            break;
        }
    } while (true);
    return new CounterState($this->limit, $remaining, $resetAfter, $isFailStoreUpdatedData);
}