update
This commit is contained in:
23
projects/priceservice/vendor/symfony/rate-limiter/CHANGELOG.md
vendored
Normal file
23
projects/priceservice/vendor/symfony/rate-limiter/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Add `SlidingWindowLimiter::reserve()`
|
||||
|
||||
6.2
|
||||
---
|
||||
|
||||
* Move `symfony/lock` to dev dependency in `composer.json`
|
||||
|
||||
5.4
|
||||
---
|
||||
|
||||
* The component is not experimental anymore
|
||||
* Add support for long intervals (months and years)
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
* added the component
|
||||
63
projects/priceservice/vendor/symfony/rate-limiter/CompoundLimiter.php
vendored
Normal file
63
projects/priceservice/vendor/symfony/rate-limiter/CompoundLimiter.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\RateLimiter\Exception\ReserveNotSupportedException;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class CompoundLimiter implements LimiterInterface
|
||||
{
|
||||
private array $limiters;
|
||||
|
||||
/**
|
||||
* @param LimiterInterface[] $limiters
|
||||
*/
|
||||
public function __construct(array $limiters)
|
||||
{
|
||||
if (!$limiters) {
|
||||
throw new \LogicException(\sprintf('"%s::%s()" require at least one limiter.', self::class, __METHOD__));
|
||||
}
|
||||
$this->limiters = $limiters;
|
||||
}
|
||||
|
||||
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->isAccepted() && !$rateLimit->isAccepted())
|
||||
) {
|
||||
$minimalRateLimit = $rateLimit;
|
||||
}
|
||||
}
|
||||
|
||||
return $minimalRateLimit;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
foreach ($this->limiters as $limiter) {
|
||||
$limiter->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
projects/priceservice/vendor/symfony/rate-limiter/Exception/InvalidIntervalException.php
vendored
Normal file
19
projects/priceservice/vendor/symfony/rate-limiter/Exception/InvalidIntervalException.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
class InvalidIntervalException extends \LogicException
|
||||
{
|
||||
}
|
||||
34
projects/priceservice/vendor/symfony/rate-limiter/Exception/MaxWaitDurationExceededException.php
vendored
Normal file
34
projects/priceservice/vendor/symfony/rate-limiter/Exception/MaxWaitDurationExceededException.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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\Exception;
|
||||
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class MaxWaitDurationExceededException extends \RuntimeException
|
||||
{
|
||||
private RateLimit $rateLimit;
|
||||
|
||||
public function __construct(string $message, RateLimit $rateLimit, int $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->rateLimit = $rateLimit;
|
||||
}
|
||||
|
||||
public function getRateLimit(): RateLimit
|
||||
{
|
||||
return $this->rateLimit;
|
||||
}
|
||||
}
|
||||
49
projects/priceservice/vendor/symfony/rate-limiter/Exception/RateLimitExceededException.php
vendored
Normal file
49
projects/priceservice/vendor/symfony/rate-limiter/Exception/RateLimitExceededException.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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\Exception;
|
||||
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
class RateLimitExceededException extends \RuntimeException
|
||||
{
|
||||
private RateLimit $rateLimit;
|
||||
|
||||
public function __construct(RateLimit $rateLimit, int $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct('Rate Limit Exceeded', $code, $previous);
|
||||
|
||||
$this->rateLimit = $rateLimit;
|
||||
}
|
||||
|
||||
public function getRateLimit(): RateLimit
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
23
projects/priceservice/vendor/symfony/rate-limiter/Exception/ReserveNotSupportedException.php
vendored
Normal file
23
projects/priceservice/vendor/symfony/rate-limiter/Exception/ReserveNotSupportedException.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class ReserveNotSupportedException extends \BadMethodCallException
|
||||
{
|
||||
public function __construct(string $limiterClass, int $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct(\sprintf('Reserving tokens is not supported by "%s".', $limiterClass), $code, $previous);
|
||||
}
|
||||
}
|
||||
19
projects/priceservice/vendor/symfony/rate-limiter/LICENSE
vendored
Normal file
19
projects/priceservice/vendor/symfony/rate-limiter/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
50
projects/priceservice/vendor/symfony/rate-limiter/LimiterInterface.php
vendored
Normal file
50
projects/priceservice/vendor/symfony/rate-limiter/LimiterInterface.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\RateLimiter\Exception\MaxWaitDurationExceededException;
|
||||
use Symfony\Component\RateLimiter\Exception\ReserveNotSupportedException;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface LimiterInterface
|
||||
{
|
||||
/**
|
||||
* 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 ReserveNotSupportedException if this limiter implementation doesn't support reserving tokens
|
||||
* @throws \InvalidArgumentException if $tokens is larger than the maximum burst size
|
||||
*/
|
||||
public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation;
|
||||
|
||||
/**
|
||||
* Use this method if you intend to drop if the required number
|
||||
* of tokens is unavailable.
|
||||
*
|
||||
* @param int $tokens the number of tokens required
|
||||
*/
|
||||
public function consume(int $tokens = 1): RateLimit;
|
||||
|
||||
/**
|
||||
* Resets the limit.
|
||||
*/
|
||||
public function reset(): void;
|
||||
}
|
||||
28
projects/priceservice/vendor/symfony/rate-limiter/LimiterStateInterface.php
vendored
Normal file
28
projects/priceservice/vendor/symfony/rate-limiter/LimiterStateInterface.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Representing the stored state of the limiter.
|
||||
*
|
||||
* Classes implementing this interface must be serializable,
|
||||
* which is used by the storage implementations to store the
|
||||
* object.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface LimiterStateInterface
|
||||
{
|
||||
public function getId(): string;
|
||||
|
||||
public function getExpirationTime(): ?int;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
43
projects/priceservice/vendor/symfony/rate-limiter/README.md
vendored
Normal file
43
projects/priceservice/vendor/symfony/rate-limiter/README.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
Rate Limiter Component
|
||||
======================
|
||||
|
||||
The Rate Limiter component provides a Token Bucket implementation to
|
||||
rate limit input and output in your application.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
```
|
||||
$ composer require symfony/rate-limiter
|
||||
```
|
||||
|
||||
```php
|
||||
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
|
||||
use Symfony\Component\RateLimiter\RateLimiterFactory;
|
||||
|
||||
$factory = new RateLimiterFactory([
|
||||
'id' => 'login',
|
||||
'policy' => 'token_bucket',
|
||||
'limit' => 10,
|
||||
'rate' => ['interval' => '15 minutes'],
|
||||
], new InMemoryStorage());
|
||||
|
||||
$limiter = $factory->create();
|
||||
|
||||
// blocks until 1 token is free to use for this process
|
||||
$limiter->reserve(1)->wait();
|
||||
// ... execute the code
|
||||
|
||||
// only claims 1 token if it's free at this moment (useful if you plan to skip this process)
|
||||
if ($limiter->consume(1)->isAccepted()) {
|
||||
// ... execute the code
|
||||
}
|
||||
```
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
77
projects/priceservice/vendor/symfony/rate-limiter/RateLimit.php
vendored
Normal file
77
projects/priceservice/vendor/symfony/rate-limiter/RateLimit.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\RateLimiter\Exception\RateLimitExceededException;
|
||||
|
||||
/**
|
||||
* @author Valentin Silvestre <vsilvestre.pro@gmail.com>
|
||||
*/
|
||||
class RateLimit
|
||||
{
|
||||
private int $availableTokens;
|
||||
private \DateTimeImmutable $retryAfter;
|
||||
private bool $accepted;
|
||||
private int $limit;
|
||||
|
||||
public function __construct(int $availableTokens, \DateTimeImmutable $retryAfter, bool $accepted, int $limit)
|
||||
{
|
||||
$this->availableTokens = $availableTokens;
|
||||
$this->retryAfter = $retryAfter;
|
||||
$this->accepted = $accepted;
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
public function isAccepted(): bool
|
||||
{
|
||||
return $this->accepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*
|
||||
* @throws RateLimitExceededException if not accepted
|
||||
*/
|
||||
public function ensureAccepted(): static
|
||||
{
|
||||
if (!$this->accepted) {
|
||||
throw new RateLimitExceededException($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRetryAfter(): \DateTimeImmutable
|
||||
{
|
||||
return $this->retryAfter;
|
||||
}
|
||||
|
||||
public function getRemainingTokens(): int
|
||||
{
|
||||
return $this->availableTokens;
|
||||
}
|
||||
|
||||
public function getLimit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
public function wait(): void
|
||||
{
|
||||
$delta = $this->retryAfter->format('U.u') - microtime(true);
|
||||
if ($delta <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
usleep((int) ($delta * 1e6));
|
||||
}
|
||||
}
|
||||
102
projects/priceservice/vendor/symfony/rate-limiter/RateLimiterFactory.php
vendored
Normal file
102
projects/priceservice/vendor/symfony/rate-limiter/RateLimiterFactory.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Lock\LockFactory;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\RateLimiter\Policy\FixedWindowLimiter;
|
||||
use Symfony\Component\RateLimiter\Policy\NoLimiter;
|
||||
use Symfony\Component\RateLimiter\Policy\Rate;
|
||||
use Symfony\Component\RateLimiter\Policy\SlidingWindowLimiter;
|
||||
use Symfony\Component\RateLimiter\Policy\TokenBucketLimiter;
|
||||
use Symfony\Component\RateLimiter\Storage\StorageInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class RateLimiterFactory
|
||||
{
|
||||
private array $config;
|
||||
private StorageInterface $storage;
|
||||
private ?LockFactory $lockFactory;
|
||||
|
||||
public function __construct(array $config, StorageInterface $storage, ?LockFactory $lockFactory = null)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
$this->lockFactory = $lockFactory;
|
||||
|
||||
$options = new OptionsResolver();
|
||||
self::configureOptions($options);
|
||||
|
||||
$this->config = $options->resolve($config);
|
||||
}
|
||||
|
||||
public function create(?string $key = null): LimiterInterface
|
||||
{
|
||||
$id = $this->config['id'].'-'.$key;
|
||||
$lock = $this->lockFactory?->createLock($id);
|
||||
|
||||
return match ($this->config['policy']) {
|
||||
'token_bucket' => new TokenBucketLimiter($id, $this->config['limit'], $this->config['rate'], $this->storage, $lock),
|
||||
'fixed_window' => new FixedWindowLimiter($id, $this->config['limit'], $this->config['interval'], $this->storage, $lock),
|
||||
'sliding_window' => new SlidingWindowLimiter($id, $this->config['limit'], $this->config['interval'], $this->storage, $lock),
|
||||
'no_limit' => new NoLimiter(),
|
||||
default => throw new \LogicException(\sprintf('Limiter policy "%s" does not exists, it must be either "token_bucket", "sliding_window", "fixed_window" or "no_limit".', $this->config['policy'])),
|
||||
};
|
||||
}
|
||||
|
||||
protected static function configureOptions(OptionsResolver $options): void
|
||||
{
|
||||
$intervalNormalizer = static function (Options $options, string $interval): \DateInterval {
|
||||
// Create DateTimeImmutable from unix timesatmp, so the default timezone is ignored and we don't need to
|
||||
// deal with quirks happening when modifying dates using a timezone with DST.
|
||||
$now = \DateTimeImmutable::createFromFormat('U', time());
|
||||
|
||||
try {
|
||||
$nowPlusInterval = @$now->modify('+'.$interval);
|
||||
} catch (\DateMalformedStringException $e) {
|
||||
throw new \LogicException(\sprintf('Cannot parse interval "%s", please use a valid unit as described on https://php.net/datetime.formats#datetime.formats.relative', $interval), 0, $e);
|
||||
}
|
||||
|
||||
if (!$nowPlusInterval) {
|
||||
throw new \LogicException(\sprintf('Cannot parse interval "%s", please use a valid unit as described on https://php.net/datetime.formats#datetime.formats.relative', $interval));
|
||||
}
|
||||
|
||||
return $now->diff($nowPlusInterval);
|
||||
};
|
||||
|
||||
$options
|
||||
->define('id')->required()
|
||||
->define('policy')
|
||||
->required()
|
||||
->allowedValues('token_bucket', 'fixed_window', 'sliding_window', 'no_limit')
|
||||
|
||||
->define('limit')->allowedTypes('int')
|
||||
->define('interval')->allowedTypes('string')->normalize($intervalNormalizer)
|
||||
->define('rate')
|
||||
->default(function (OptionsResolver $rate) use ($intervalNormalizer) {
|
||||
$rate
|
||||
->define('amount')->allowedTypes('int')->default(1)
|
||||
->define('interval')->allowedTypes('string')->normalize($intervalNormalizer)
|
||||
;
|
||||
})
|
||||
->normalize(function (Options $options, $value) {
|
||||
if (!isset($value['interval'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Rate($value['interval'], $value['amount']);
|
||||
})
|
||||
;
|
||||
}
|
||||
}
|
||||
50
projects/priceservice/vendor/symfony/rate-limiter/Reservation.php
vendored
Normal file
50
projects/priceservice/vendor/symfony/rate-limiter/Reservation.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
final class Reservation
|
||||
{
|
||||
private float $timeToAct;
|
||||
private RateLimit $rateLimit;
|
||||
|
||||
/**
|
||||
* @param float $timeToAct Unix timestamp in seconds when this reservation should act
|
||||
*/
|
||||
public function __construct(float $timeToAct, RateLimit $rateLimit)
|
||||
{
|
||||
$this->timeToAct = $timeToAct;
|
||||
$this->rateLimit = $rateLimit;
|
||||
}
|
||||
|
||||
public function getTimeToAct(): float
|
||||
{
|
||||
return $this->timeToAct;
|
||||
}
|
||||
|
||||
public function getWaitDuration(): float
|
||||
{
|
||||
return max(0, (-microtime(true)) + $this->timeToAct);
|
||||
}
|
||||
|
||||
public function getRateLimit(): RateLimit
|
||||
{
|
||||
return $this->rateLimit;
|
||||
}
|
||||
|
||||
public function wait(): void
|
||||
{
|
||||
usleep((int) ($this->getWaitDuration() * 1e6));
|
||||
}
|
||||
}
|
||||
55
projects/priceservice/vendor/symfony/rate-limiter/Storage/CacheStorage.php
vendored
Normal file
55
projects/priceservice/vendor/symfony/rate-limiter/Storage/CacheStorage.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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\Storage;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\RateLimiter\LimiterStateInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class CacheStorage implements StorageInterface
|
||||
{
|
||||
private CacheItemPoolInterface $pool;
|
||||
|
||||
public function __construct(CacheItemPoolInterface $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
}
|
||||
|
||||
public function save(LimiterStateInterface $limiterState): void
|
||||
{
|
||||
$cacheItem = $this->pool->getItem(sha1($limiterState->getId()));
|
||||
$cacheItem->set($limiterState);
|
||||
if (null !== ($expireAfter = $limiterState->getExpirationTime())) {
|
||||
$cacheItem->expiresAfter($expireAfter);
|
||||
}
|
||||
|
||||
$this->pool->save($cacheItem);
|
||||
}
|
||||
|
||||
public function fetch(string $limiterStateId): ?LimiterStateInterface
|
||||
{
|
||||
$cacheItem = $this->pool->getItem(sha1($limiterStateId));
|
||||
$value = $cacheItem->get();
|
||||
if ($value instanceof LimiterStateInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function delete(string $limiterStateId): void
|
||||
{
|
||||
$this->pool->deleteItem(sha1($limiterStateId));
|
||||
}
|
||||
}
|
||||
61
projects/priceservice/vendor/symfony/rate-limiter/Storage/InMemoryStorage.php
vendored
Normal file
61
projects/priceservice/vendor/symfony/rate-limiter/Storage/InMemoryStorage.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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\Storage;
|
||||
|
||||
use Symfony\Component\RateLimiter\LimiterStateInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class InMemoryStorage implements StorageInterface
|
||||
{
|
||||
private array $buckets = [];
|
||||
|
||||
public function save(LimiterStateInterface $limiterState): void
|
||||
{
|
||||
$this->buckets[$limiterState->getId()] = [$this->getExpireAt($limiterState), serialize($limiterState)];
|
||||
}
|
||||
|
||||
public function fetch(string $limiterStateId): ?LimiterStateInterface
|
||||
{
|
||||
if (!isset($this->buckets[$limiterStateId])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$expireAt, $limiterState] = $this->buckets[$limiterStateId];
|
||||
if (null !== $expireAt && $expireAt <= microtime(true)) {
|
||||
unset($this->buckets[$limiterStateId]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return unserialize($limiterState);
|
||||
}
|
||||
|
||||
public function delete(string $limiterStateId): void
|
||||
{
|
||||
if (!isset($this->buckets[$limiterStateId])) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->buckets[$limiterStateId]);
|
||||
}
|
||||
|
||||
private function getExpireAt(LimiterStateInterface $limiterState): ?float
|
||||
{
|
||||
if (null !== $expireSeconds = $limiterState->getExpirationTime()) {
|
||||
return microtime(true) + $expireSeconds;
|
||||
}
|
||||
|
||||
return $this->buckets[$limiterState->getId()][0] ?? null;
|
||||
}
|
||||
}
|
||||
26
projects/priceservice/vendor/symfony/rate-limiter/Storage/StorageInterface.php
vendored
Normal file
26
projects/priceservice/vendor/symfony/rate-limiter/Storage/StorageInterface.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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\Storage;
|
||||
|
||||
use Symfony\Component\RateLimiter\LimiterStateInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface StorageInterface
|
||||
{
|
||||
public function save(LimiterStateInterface $limiterState): void;
|
||||
|
||||
public function fetch(string $limiterStateId): ?LimiterStateInterface;
|
||||
|
||||
public function delete(string $limiterStateId): void;
|
||||
}
|
||||
27
projects/priceservice/vendor/symfony/rate-limiter/Util/TimeUtil.php
vendored
Normal file
27
projects/priceservice/vendor/symfony/rate-limiter/Util/TimeUtil.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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\Util;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class TimeUtil
|
||||
{
|
||||
public static function dateIntervalToSeconds(\DateInterval $interval): int
|
||||
{
|
||||
$now = \DateTimeImmutable::createFromFormat('U', time());
|
||||
|
||||
return $now->add($interval)->getTimestamp() - $now->getTimestamp();
|
||||
}
|
||||
}
|
||||
34
projects/priceservice/vendor/symfony/rate-limiter/composer.json
vendored
Normal file
34
projects/priceservice/vendor/symfony/rate-limiter/composer.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "symfony/rate-limiter",
|
||||
"type": "library",
|
||||
"description": "Provides a Token Bucket implementation to rate limit input and output in your application",
|
||||
"keywords": ["limiter", "rate-limiter"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Wouter de Jong",
|
||||
"email": "wouter@wouterj.nl"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/options-resolver": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/cache": "^1.0|^2.0|^3.0",
|
||||
"symfony/lock": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\RateLimiter\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
Reference in New Issue
Block a user