update
This commit is contained in:
105
projects/priceservice/vendor/symfony/rate-limiter/Policy/FixedWindowLimiter.php
vendored
Normal file
105
projects/priceservice/vendor/symfony/rate-limiter/Policy/FixedWindowLimiter.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\RateLimiter\Exception\MaxWaitDurationExceededException;
|
||||
use Symfony\Component\RateLimiter\LimiterInterface;
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
use Symfony\Component\RateLimiter\Reservation;
|
||||
use Symfony\Component\RateLimiter\Storage\StorageInterface;
|
||||
use Symfony\Component\RateLimiter\Util\TimeUtil;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class FixedWindowLimiter implements LimiterInterface
|
||||
{
|
||||
use ResetLimiterTrait;
|
||||
|
||||
private int $limit;
|
||||
private int $interval;
|
||||
|
||||
public function __construct(string $id, int $limit, \DateInterval $interval, StorageInterface $storage, ?LockInterface $lock = null)
|
||||
{
|
||||
if ($limit < 1) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot set the limit of "%s" to 0, as that would never accept any hit.', __CLASS__));
|
||||
}
|
||||
|
||||
$this->storage = $storage;
|
||||
$this->lock = $lock;
|
||||
$this->id = $id;
|
||||
$this->limit = $limit;
|
||||
$this->interval = TimeUtil::dateIntervalToSeconds($interval);
|
||||
}
|
||||
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
|
||||
{
|
||||
if ($tokens > $this->limit) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot reserve more tokens (%d) than the size of the rate limiter (%d).', $tokens, $this->limit));
|
||||
}
|
||||
|
||||
$this->lock?->acquire(true);
|
||||
|
||||
try {
|
||||
$window = $this->storage->fetch($this->id);
|
||||
if (!$window instanceof Window) {
|
||||
$window = new Window($this->id, $this->interval, $this->limit);
|
||||
}
|
||||
|
||||
$now = microtime(true);
|
||||
$availableTokens = $window->getAvailableTokens($now);
|
||||
|
||||
if (0 === $tokens) {
|
||||
$waitDuration = $window->calculateTimeForTokens(1, $now);
|
||||
$reservation = new Reservation($now + $waitDuration, new RateLimit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), true, $this->limit));
|
||||
} elseif ($availableTokens >= $tokens) {
|
||||
$window->add($tokens, $now);
|
||||
|
||||
$reservation = new Reservation($now, new RateLimit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit));
|
||||
} else {
|
||||
$waitDuration = $window->calculateTimeForTokens($tokens, $now);
|
||||
|
||||
if (null !== $maxTime && $waitDuration > $maxTime) {
|
||||
// process needs to wait longer than set interval
|
||||
throw new MaxWaitDurationExceededException(\sprintf('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds).', $waitDuration, $maxTime), new RateLimit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
|
||||
}
|
||||
|
||||
$window->add($tokens, $now);
|
||||
|
||||
$reservation = new Reservation($now + $waitDuration, new RateLimit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
|
||||
}
|
||||
|
||||
if (0 < $tokens) {
|
||||
$this->storage->save($window);
|
||||
}
|
||||
} finally {
|
||||
$this->lock?->release();
|
||||
}
|
||||
|
||||
return $reservation;
|
||||
}
|
||||
|
||||
public function consume(int $tokens = 1): RateLimit
|
||||
{
|
||||
try {
|
||||
return $this->reserve($tokens, 0)->getRateLimit();
|
||||
} catch (MaxWaitDurationExceededException $e) {
|
||||
return $e->getRateLimit();
|
||||
}
|
||||
}
|
||||
|
||||
public function getAvailableTokens(int $hitCount): int
|
||||
{
|
||||
return $this->limit - $hitCount;
|
||||
}
|
||||
}
|
||||
41
projects/priceservice/vendor/symfony/rate-limiter/Policy/NoLimiter.php
vendored
Normal file
41
projects/priceservice/vendor/symfony/rate-limiter/Policy/NoLimiter.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\RateLimiter\LimiterInterface;
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
use Symfony\Component\RateLimiter\Reservation;
|
||||
|
||||
/**
|
||||
* Implements a non limiting limiter.
|
||||
*
|
||||
* This can be used in cases where an implementation requires a
|
||||
* limiter, but no rate limit should be enforced.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class NoLimiter implements LimiterInterface
|
||||
{
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
|
||||
{
|
||||
return new Reservation(microtime(true), new RateLimit(\PHP_INT_MAX, new \DateTimeImmutable(), true, \PHP_INT_MAX));
|
||||
}
|
||||
|
||||
public function consume(int $tokens = 1): RateLimit
|
||||
{
|
||||
return new RateLimit(\PHP_INT_MAX, new \DateTimeImmutable(), true, \PHP_INT_MAX);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
118
projects/priceservice/vendor/symfony/rate-limiter/Policy/Rate.php
vendored
Normal file
118
projects/priceservice/vendor/symfony/rate-limiter/Policy/Rate.php
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\RateLimiter\Util\TimeUtil;
|
||||
|
||||
/**
|
||||
* Data object representing the fill rate of a token bucket.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class Rate
|
||||
{
|
||||
private \DateInterval $refillTime;
|
||||
private int $refillAmount;
|
||||
|
||||
public function __construct(\DateInterval $refillTime, int $refillAmount = 1)
|
||||
{
|
||||
$this->refillTime = $refillTime;
|
||||
$this->refillAmount = $refillAmount;
|
||||
}
|
||||
|
||||
public static function perSecond(int $rate = 1): self
|
||||
{
|
||||
return new static(new \DateInterval('PT1S'), $rate);
|
||||
}
|
||||
|
||||
public static function perMinute(int $rate = 1): self
|
||||
{
|
||||
return new static(new \DateInterval('PT1M'), $rate);
|
||||
}
|
||||
|
||||
public static function perHour(int $rate = 1): self
|
||||
{
|
||||
return new static(new \DateInterval('PT1H'), $rate);
|
||||
}
|
||||
|
||||
public static function perDay(int $rate = 1): self
|
||||
{
|
||||
return new static(new \DateInterval('P1D'), $rate);
|
||||
}
|
||||
|
||||
public static function perMonth(int $rate = 1): self
|
||||
{
|
||||
return new static(new \DateInterval('P1M'), $rate);
|
||||
}
|
||||
|
||||
public static function perYear(int $rate = 1): self
|
||||
{
|
||||
return new static(new \DateInterval('P1Y'), $rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string using the format: "%interval_spec%-%rate%", {@see DateInterval}
|
||||
*/
|
||||
public static function fromString(string $string): self
|
||||
{
|
||||
[$interval, $rate] = explode('-', $string, 2);
|
||||
|
||||
return new static(new \DateInterval($interval), $rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the time needed to free up the provided number of tokens in seconds.
|
||||
*/
|
||||
public function calculateTimeForTokens(int $tokens): int
|
||||
{
|
||||
$cyclesRequired = ceil($tokens / $this->refillAmount);
|
||||
|
||||
return TimeUtil::dateIntervalToSeconds($this->refillTime) * $cyclesRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next moment of token availability.
|
||||
*/
|
||||
public function calculateNextTokenAvailability(): \DateTimeImmutable
|
||||
{
|
||||
return (new \DateTimeImmutable())->add($this->refillTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of new free tokens during $duration.
|
||||
*
|
||||
* @param float $duration interval in seconds
|
||||
*/
|
||||
public function calculateNewTokensDuringInterval(float $duration): int
|
||||
{
|
||||
$cycles = floor($duration / TimeUtil::dateIntervalToSeconds($this->refillTime));
|
||||
|
||||
return $cycles * $this->refillAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates total amount in seconds of refill intervals during $duration (for maintain strict refill frequency).
|
||||
*
|
||||
* @param float $duration interval in seconds
|
||||
*/
|
||||
public function calculateRefillInterval(float $duration): int
|
||||
{
|
||||
$cycleTime = TimeUtil::dateIntervalToSeconds($this->refillTime);
|
||||
|
||||
return floor($duration / $cycleTime) * $cycleTime;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->refillTime->format('P%yY%mM%dDT%HH%iM%sS').'-'.$this->refillAmount;
|
||||
}
|
||||
}
|
||||
33
projects/priceservice/vendor/symfony/rate-limiter/Policy/ResetLimiterTrait.php
vendored
Normal file
33
projects/priceservice/vendor/symfony/rate-limiter/Policy/ResetLimiterTrait.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\RateLimiter\Storage\StorageInterface;
|
||||
|
||||
trait ResetLimiterTrait
|
||||
{
|
||||
private ?LockInterface $lock;
|
||||
private StorageInterface $storage;
|
||||
private string $id;
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
try {
|
||||
$this->lock?->acquire(true);
|
||||
|
||||
$this->storage->delete($this->id);
|
||||
} finally {
|
||||
$this->lock?->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
145
projects/priceservice/vendor/symfony/rate-limiter/Policy/SlidingWindow.php
vendored
Normal file
145
projects/priceservice/vendor/symfony/rate-limiter/Policy/SlidingWindow.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\RateLimiter\Exception\InvalidIntervalException;
|
||||
use Symfony\Component\RateLimiter\LimiterStateInterface;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class SlidingWindow implements LimiterStateInterface
|
||||
{
|
||||
private string $id;
|
||||
private int $hitCount = 0;
|
||||
private int $hitCountForLastWindow = 0;
|
||||
private int $intervalInSeconds;
|
||||
private float $windowEndAt;
|
||||
|
||||
public function __construct(string $id, int $intervalInSeconds)
|
||||
{
|
||||
if ($intervalInSeconds < 1) {
|
||||
throw new InvalidIntervalException(\sprintf('The interval must be positive integer, "%d" given.', $intervalInSeconds));
|
||||
}
|
||||
$this->id = $id;
|
||||
$this->intervalInSeconds = $intervalInSeconds;
|
||||
$this->windowEndAt = microtime(true) + $intervalInSeconds;
|
||||
}
|
||||
|
||||
public static function createFromPreviousWindow(self $window, int $intervalInSeconds): self
|
||||
{
|
||||
$new = new self($window->id, $intervalInSeconds);
|
||||
$windowEndAt = $window->windowEndAt + $intervalInSeconds;
|
||||
|
||||
if (microtime(true) < $windowEndAt) {
|
||||
$new->hitCountForLastWindow = $window->hitCount;
|
||||
$new->windowEndAt = $windowEndAt;
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remaining of this timeframe and the next one.
|
||||
*/
|
||||
public function getExpirationTime(): int
|
||||
{
|
||||
return (int) ($this->windowEndAt + $this->intervalInSeconds - microtime(true));
|
||||
}
|
||||
|
||||
public function isExpired(): bool
|
||||
{
|
||||
return microtime(true) > $this->windowEndAt;
|
||||
}
|
||||
|
||||
public function add(int $hits = 1): void
|
||||
{
|
||||
$this->hitCount += $hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the sliding window number of request.
|
||||
*/
|
||||
public function getHitCount(): int
|
||||
{
|
||||
$startOfWindow = $this->windowEndAt - $this->intervalInSeconds;
|
||||
$percentOfCurrentTimeFrame = min((microtime(true) - $startOfWindow) / $this->intervalInSeconds, 1);
|
||||
|
||||
return (int) floor($this->hitCountForLastWindow * (1 - $percentOfCurrentTimeFrame) + $this->hitCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 6.4, use {@see self::calculateTimeForTokens} instead
|
||||
*/
|
||||
public function getRetryAfter(): \DateTimeImmutable
|
||||
{
|
||||
trigger_deprecation('symfony/ratelimiter', '6.4', 'The "%s()" method is deprecated, use "%s::calculateTimeForTokens" instead.', __METHOD__, self::class);
|
||||
|
||||
return \DateTimeImmutable::createFromFormat('U.u', \sprintf('%.6F', microtime(true) + $this->calculateTimeForTokens(max(1, $this->getHitCount()), 1)));
|
||||
}
|
||||
|
||||
public function calculateTimeForTokens(int $maxSize, int $tokens): float
|
||||
{
|
||||
$remaining = $maxSize - $this->getHitCount();
|
||||
if ($remaining >= $tokens) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$time = microtime(true);
|
||||
$startOfWindow = $this->windowEndAt - $this->intervalInSeconds;
|
||||
$timePassed = $time - $startOfWindow;
|
||||
$windowPassed = min($timePassed / $this->intervalInSeconds, 1);
|
||||
$releasable = max(1, $maxSize - floor($this->hitCountForLastWindow * (1 - $windowPassed)));
|
||||
$remainingWindow = $this->intervalInSeconds - $timePassed;
|
||||
$needed = $tokens - $remaining;
|
||||
|
||||
if ($releasable >= $needed) {
|
||||
return $needed * ($remainingWindow / max(1, $releasable));
|
||||
}
|
||||
|
||||
return ($this->windowEndAt - $time) + ($needed - $releasable) * ($this->intervalInSeconds / $maxSize);
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return [
|
||||
pack('NNN', $this->hitCount, $this->hitCountForLastWindow, $this->intervalInSeconds).$this->id => $this->windowEndAt,
|
||||
];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
// BC layer for old objects serialized via __sleep
|
||||
if (5 === \count($data)) {
|
||||
$data = array_values($data);
|
||||
$this->id = $data[0];
|
||||
$this->hitCount = $data[1];
|
||||
$this->intervalInSeconds = $data[2];
|
||||
$this->hitCountForLastWindow = $data[3];
|
||||
$this->windowEndAt = $data[4];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pack = key($data);
|
||||
$this->windowEndAt = $data[$pack];
|
||||
['a' => $this->hitCount, 'b' => $this->hitCountForLastWindow, 'c' => $this->intervalInSeconds] = unpack('Na/Nb/Nc', $pack);
|
||||
$this->id = substr($pack, 12);
|
||||
}
|
||||
}
|
||||
114
projects/priceservice/vendor/symfony/rate-limiter/Policy/SlidingWindowLimiter.php
vendored
Normal file
114
projects/priceservice/vendor/symfony/rate-limiter/Policy/SlidingWindowLimiter.php
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\RateLimiter\Exception\MaxWaitDurationExceededException;
|
||||
use Symfony\Component\RateLimiter\LimiterInterface;
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
use Symfony\Component\RateLimiter\Reservation;
|
||||
use Symfony\Component\RateLimiter\Storage\StorageInterface;
|
||||
use Symfony\Component\RateLimiter\Util\TimeUtil;
|
||||
|
||||
/**
|
||||
* The sliding window algorithm will look at your last window and the current one.
|
||||
* It is good algorithm to reduce bursts.
|
||||
*
|
||||
* Example:
|
||||
* Last time window we did 8 hits. We are currently 25% into
|
||||
* the current window. We have made 3 hits in the current window so far.
|
||||
* That means our sliding window hit count is (75% * 8) + 3 = 9.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class SlidingWindowLimiter implements LimiterInterface
|
||||
{
|
||||
use ResetLimiterTrait;
|
||||
|
||||
private int $limit;
|
||||
private int $interval;
|
||||
|
||||
public function __construct(string $id, int $limit, \DateInterval $interval, StorageInterface $storage, ?LockInterface $lock = null)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
$this->lock = $lock;
|
||||
$this->id = $id;
|
||||
$this->limit = $limit;
|
||||
$this->interval = TimeUtil::dateIntervalToSeconds($interval);
|
||||
}
|
||||
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
|
||||
{
|
||||
if ($tokens > $this->limit) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot reserve more tokens (%d) than the size of the rate limiter (%d).', $tokens, $this->limit));
|
||||
}
|
||||
|
||||
$this->lock?->acquire(true);
|
||||
|
||||
try {
|
||||
$window = $this->storage->fetch($this->id);
|
||||
if (!$window instanceof SlidingWindow) {
|
||||
$window = new SlidingWindow($this->id, $this->interval);
|
||||
} elseif ($window->isExpired()) {
|
||||
$window = SlidingWindow::createFromPreviousWindow($window, $this->interval);
|
||||
}
|
||||
|
||||
$now = microtime(true);
|
||||
$hitCount = $window->getHitCount();
|
||||
$availableTokens = $this->getAvailableTokens($hitCount);
|
||||
if (0 === $tokens) {
|
||||
$resetDuration = $window->calculateTimeForTokens($this->limit, $window->getHitCount());
|
||||
$resetTime = \DateTimeImmutable::createFromFormat('U', $availableTokens ? floor($now) : floor($now + $resetDuration));
|
||||
|
||||
return new Reservation($now, new RateLimit($availableTokens, $resetTime, true, $this->limit));
|
||||
}
|
||||
if ($availableTokens >= $tokens) {
|
||||
$window->add($tokens);
|
||||
|
||||
$reservation = new Reservation($now, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit));
|
||||
} else {
|
||||
$waitDuration = $window->calculateTimeForTokens($this->limit, $tokens);
|
||||
|
||||
if (null !== $maxTime && $waitDuration > $maxTime) {
|
||||
// process needs to wait longer than set interval
|
||||
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));
|
||||
}
|
||||
|
||||
$window->add($tokens);
|
||||
|
||||
$reservation = new Reservation($now + $waitDuration, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
|
||||
}
|
||||
|
||||
if (0 < $tokens) {
|
||||
$this->storage->save($window);
|
||||
}
|
||||
} finally {
|
||||
$this->lock?->release();
|
||||
}
|
||||
|
||||
return $reservation;
|
||||
}
|
||||
|
||||
public function consume(int $tokens = 1): RateLimit
|
||||
{
|
||||
try {
|
||||
return $this->reserve($tokens, 0)->getRateLimit();
|
||||
} catch (MaxWaitDurationExceededException $e) {
|
||||
return $e->getRateLimit();
|
||||
}
|
||||
}
|
||||
|
||||
private function getAvailableTokens(int $hitCount): int
|
||||
{
|
||||
return $this->limit - $hitCount;
|
||||
}
|
||||
}
|
||||
112
projects/priceservice/vendor/symfony/rate-limiter/Policy/TokenBucket.php
vendored
Normal file
112
projects/priceservice/vendor/symfony/rate-limiter/Policy/TokenBucket.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\RateLimiter\LimiterStateInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class TokenBucket implements LimiterStateInterface
|
||||
{
|
||||
private string $id;
|
||||
private Rate $rate;
|
||||
private int $tokens;
|
||||
private int $burstSize;
|
||||
private float $timer;
|
||||
|
||||
/**
|
||||
* @param string $id unique identifier for this bucket
|
||||
* @param int $initialTokens the initial number of tokens in the bucket (i.e. the max burst size)
|
||||
* @param Rate $rate the fill rate and time of this bucket
|
||||
* @param float|null $timer the current timer of the bucket, defaulting to microtime(true)
|
||||
*/
|
||||
public function __construct(string $id, int $initialTokens, Rate $rate, ?float $timer = null)
|
||||
{
|
||||
if ($initialTokens < 1) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot set the limit of "%s" to 0, as that would never accept any hit.', TokenBucketLimiter::class));
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
$this->tokens = $this->burstSize = $initialTokens;
|
||||
$this->rate = $rate;
|
||||
$this->timer = $timer ?? microtime(true);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTimer(float $microtime): void
|
||||
{
|
||||
$this->timer = $microtime;
|
||||
}
|
||||
|
||||
public function getTimer(): float
|
||||
{
|
||||
return $this->timer;
|
||||
}
|
||||
|
||||
public function setTokens(int $tokens): void
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
}
|
||||
|
||||
public function getAvailableTokens(float $now): int
|
||||
{
|
||||
$elapsed = max(0, $now - $this->timer);
|
||||
$newTokens = $this->rate->calculateNewTokensDuringInterval($elapsed);
|
||||
|
||||
if ($newTokens > 0) {
|
||||
$this->timer += $this->rate->calculateRefillInterval($elapsed);
|
||||
}
|
||||
|
||||
return min($this->burstSize, $this->tokens + $newTokens);
|
||||
}
|
||||
|
||||
public function getExpirationTime(): int
|
||||
{
|
||||
return $this->rate->calculateTimeForTokens($this->burstSize);
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return [
|
||||
pack('N', $this->burstSize).$this->id => $this->tokens,
|
||||
(string) $this->rate => $this->timer,
|
||||
];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
// BC layer for old objects serialized via __sleep
|
||||
if (5 === \count($data)) {
|
||||
$data = array_values($data);
|
||||
$this->id = $data[0];
|
||||
$this->tokens = $data[1];
|
||||
$this->timer = $data[2];
|
||||
$this->burstSize = $data[3];
|
||||
$this->rate = Rate::fromString($data[4]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$this->tokens, $this->timer] = array_values($data);
|
||||
[$pack, $rate] = array_keys($data);
|
||||
$this->rate = Rate::fromString($rate);
|
||||
$this->burstSize = unpack('Na', $pack)['a'];
|
||||
$this->id = substr($pack, 4);
|
||||
}
|
||||
}
|
||||
124
projects/priceservice/vendor/symfony/rate-limiter/Policy/TokenBucketLimiter.php
vendored
Normal file
124
projects/priceservice/vendor/symfony/rate-limiter/Policy/TokenBucketLimiter.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\RateLimiter\Exception\MaxWaitDurationExceededException;
|
||||
use Symfony\Component\RateLimiter\LimiterInterface;
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
use Symfony\Component\RateLimiter\Reservation;
|
||||
use Symfony\Component\RateLimiter\Storage\StorageInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class TokenBucketLimiter implements LimiterInterface
|
||||
{
|
||||
use ResetLimiterTrait;
|
||||
|
||||
private int $maxBurst;
|
||||
private Rate $rate;
|
||||
|
||||
public function __construct(string $id, int $maxBurst, Rate $rate, StorageInterface $storage, ?LockInterface $lock = null)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->maxBurst = $maxBurst;
|
||||
$this->rate = $rate;
|
||||
$this->storage = $storage;
|
||||
$this->lock = $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the required number of tokens is available.
|
||||
*
|
||||
* The reserved tokens will be taken into account when calculating
|
||||
* future token consumptions. Do not use this method if you intend
|
||||
* to skip this process.
|
||||
*
|
||||
* @param int $tokens the number of tokens required
|
||||
* @param float|null $maxTime maximum accepted waiting time in seconds
|
||||
*
|
||||
* @throws MaxWaitDurationExceededException if $maxTime is set and the process needs to wait longer than its value (in seconds)
|
||||
* @throws \InvalidArgumentException if $tokens is larger than the maximum burst size
|
||||
*/
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
|
||||
{
|
||||
if ($tokens > $this->maxBurst) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot reserve more tokens (%d) than the burst size of the rate limiter (%d).', $tokens, $this->maxBurst));
|
||||
}
|
||||
|
||||
$this->lock?->acquire(true);
|
||||
|
||||
try {
|
||||
$bucket = $this->storage->fetch($this->id);
|
||||
if (!$bucket instanceof TokenBucket) {
|
||||
$bucket = new TokenBucket($this->id, $this->maxBurst, $this->rate);
|
||||
}
|
||||
|
||||
$now = microtime(true);
|
||||
$availableTokens = $bucket->getAvailableTokens($now);
|
||||
|
||||
if ($availableTokens > $this->maxBurst) {
|
||||
$availableTokens = $this->maxBurst;
|
||||
}
|
||||
|
||||
if ($availableTokens >= $tokens) {
|
||||
// tokens are now available, update bucket
|
||||
$bucket->setTokens($availableTokens - $tokens);
|
||||
|
||||
if (0 === $availableTokens) {
|
||||
// This means 0 tokens where consumed (discouraged in most cases).
|
||||
// Return the first time a new token is available
|
||||
$waitDuration = $this->rate->calculateTimeForTokens(1);
|
||||
$waitTime = \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration));
|
||||
} else {
|
||||
$waitTime = \DateTimeImmutable::createFromFormat('U', floor($now));
|
||||
}
|
||||
|
||||
$reservation = new Reservation($now, new RateLimit($bucket->getAvailableTokens($now), $waitTime, true, $this->maxBurst));
|
||||
} else {
|
||||
$remainingTokens = $tokens - $availableTokens;
|
||||
$waitDuration = $this->rate->calculateTimeForTokens($remainingTokens);
|
||||
|
||||
if (null !== $maxTime && $waitDuration > $maxTime) {
|
||||
// process needs to wait longer than set interval
|
||||
$rateLimit = new RateLimit($availableTokens, \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->maxBurst);
|
||||
|
||||
throw new MaxWaitDurationExceededException(\sprintf('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds).', $waitDuration, $maxTime), $rateLimit);
|
||||
}
|
||||
|
||||
// at $now + $waitDuration all tokens will be reserved for this process,
|
||||
// so no tokens are left for other processes.
|
||||
$bucket->setTokens($availableTokens - $tokens);
|
||||
|
||||
$reservation = new Reservation($now + $waitDuration, new RateLimit(0, \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->maxBurst));
|
||||
}
|
||||
|
||||
if (0 < $tokens) {
|
||||
$this->storage->save($bucket);
|
||||
}
|
||||
} finally {
|
||||
$this->lock?->release();
|
||||
}
|
||||
|
||||
return $reservation;
|
||||
}
|
||||
|
||||
public function consume(int $tokens = 1): RateLimit
|
||||
{
|
||||
try {
|
||||
return $this->reserve($tokens, 0)->getRateLimit();
|
||||
} catch (MaxWaitDurationExceededException $e) {
|
||||
return $e->getRateLimit();
|
||||
}
|
||||
}
|
||||
}
|
||||
109
projects/priceservice/vendor/symfony/rate-limiter/Policy/Window.php
vendored
Normal file
109
projects/priceservice/vendor/symfony/rate-limiter/Policy/Window.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\RateLimiter\Policy;
|
||||
|
||||
use Symfony\Component\RateLimiter\LimiterStateInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Window implements LimiterStateInterface
|
||||
{
|
||||
private string $id;
|
||||
private int $hitCount = 0;
|
||||
private int $intervalInSeconds;
|
||||
private int $maxSize;
|
||||
private float $timer;
|
||||
|
||||
public function __construct(string $id, int $intervalInSeconds, int $windowSize, ?float $timer = null)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->intervalInSeconds = $intervalInSeconds;
|
||||
$this->maxSize = $windowSize;
|
||||
$this->timer = $timer ?? microtime(true);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getExpirationTime(): ?int
|
||||
{
|
||||
return $this->intervalInSeconds;
|
||||
}
|
||||
|
||||
public function add(int $hits = 1, ?float $now = null): void
|
||||
{
|
||||
$now ??= microtime(true);
|
||||
if (($now - $this->timer) > $this->intervalInSeconds) {
|
||||
// reset window
|
||||
$this->timer = $now;
|
||||
$this->hitCount = 0;
|
||||
}
|
||||
|
||||
$this->hitCount += $hits;
|
||||
}
|
||||
|
||||
public function getHitCount(): int
|
||||
{
|
||||
return $this->hitCount;
|
||||
}
|
||||
|
||||
public function getAvailableTokens(float $now): int
|
||||
{
|
||||
// if now is more than the window interval in the past, all tokens are available
|
||||
if (($now - $this->timer) > $this->intervalInSeconds) {
|
||||
return $this->maxSize;
|
||||
}
|
||||
|
||||
return $this->maxSize - $this->hitCount;
|
||||
}
|
||||
|
||||
public function calculateTimeForTokens(int $tokens, float $now): int
|
||||
{
|
||||
if (($this->maxSize - $this->hitCount) >= $tokens) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) ceil($this->timer + $this->intervalInSeconds - $now);
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return [
|
||||
$this->id => $this->timer,
|
||||
pack('NN', $this->hitCount, $this->intervalInSeconds) => $this->maxSize,
|
||||
];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
// BC layer for old objects serialized via __sleep
|
||||
if (5 === \count($data)) {
|
||||
$data = array_values($data);
|
||||
$this->id = $data[0];
|
||||
$this->hitCount = $data[1];
|
||||
$this->intervalInSeconds = $data[2];
|
||||
$this->maxSize = $data[3];
|
||||
$this->timer = $data[4];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$this->timer, $this->maxSize] = array_values($data);
|
||||
[$this->id, $pack] = array_keys($data);
|
||||
['a' => $this->hitCount, 'b' => $this->intervalInSeconds] = unpack('Na/Nb', $pack);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user