8000 [RateLimiter] Add support of long intervals (months and years) · symfony/symfony@dd44a83 · GitHub
[go: up one dir, main page]

Skip to content

Commit dd44a83

Browse files
[RateLimiter] Add support of long intervals (months and years)
1 parent 93d07e9 commit dd44a83

File tree

5 files changed

+71
-13
lines changed

5 files changed

+71
-13
lines changed

src/Symfony/Component/RateLimiter/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* The component is not experimental anymore
8+
* Add support of long intervals (months and years)
89

910
5.2.0
1011
-----

src/Symfony/Component/RateLimiter/Policy/Rate.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ public static function perDay(int $rate = 1): self
4949
return new static(new \DateInterval('P1D'), $rate);
5050
}
5151

52+
public static function perMonth(int $rate = 1): self
53+
{
54+
return new static(new \DateInterval('P1M'), $rate);
55+
}
56+
57+
public static function perYear(int $rate = 1): self
58+
{
59+
return new static(new \DateInterval('P1Y'), $rate);
60+
}
61+
5262
/**
5363
* @param string $string using the format: "%interval_spec%-%rate%", {@see DateInterval}
5464
*/
@@ -91,6 +101,6 @@ public function calculateNewTokensDuringInterval(float $duration): int
91101

92102
public function __toString(): string
93103
{
94-
return $this->refillTime->format('P%dDT%HH%iM%sS').'-'.$this->refillAmount;
104+
return $this->refillTime->format('P%y%m%dDT%HH%iM%sS').'-'.$this->refillAmount;
95105
}
96106
}

src/Symfony/Component/RateLimiter/Tests/Policy/FixedWindowLimiterTest.php

+24-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\RateLimiter\Policy\FixedWindowLimiter;
1717
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
1818
use Symfony\Component\RateLimiter\Tests\Resources\DummyWindow;
19+
use Symfony\Component\RateLimiter\Util\TimeUtil;
1920

2021
/**
2122
* @group time-sensitive
@@ -49,16 +50,19 @@ public function testConsume()
4950
$this->assertSame(10, $rateLimit->getLimit());
5051
}
5152

52-
public function testConsumeOutsideInterval()
53+
/**
54+
* @dataProvider provideConsumeOutsideInterval
55+
*/
56+
public function testConsumeOutsideInterval(string $dateIntervalString)
5357
{
54-
$limiter = $this->createLimiter();
58+
$limiter = $this->createLimiter($dateIntervalString);
5559

5660
// start window...
5761
$limiter->consume();
58-
// ...add a max burst at the end of the window...
59-
sleep(55);
62+
// ...add a max burst, 5 seconds before the end of the window...
63+
sleep(TimeUtil::dateIntervalToSeconds(new \DateInterval($dateIntervalString)) - 5);
6064
$limiter->consume(9);
61-
// ...try bursting again at the start of the next window
65+
// ...try bursting again at the start of the next window, 10 seconds later
6266
sleep(10);
6367
$rateLimit = $limiter->consume(10);
6468
$this->assertEquals(0, $rateLimit->getRemainingTokens());
@@ -74,8 +78,21 @@ public function testWrongWindowFromCache()
7478
$this->assertEquals(9, $rateLimit->getRemainingTokens());
7579
}
7680

77-
private function createLimiter(): FixedWindowLimiter
81+
private function createLimiter(string $dateIntervalString = 'PT1M'): FixedWindowLimiter
82+
{
83+
return new FixedWindowLimiter('test', 10, new \DateInterval($dateIntervalString), $this->storage);
84+
}
85+
86+
public function provideConsumeOutsideInterval(): \Generator
7887
{
79-
return new FixedWindowLimiter('test', 10, new \DateInterval('PT1M'), $this->storage);
88+
yield ['PT15S'];
89+
90+
yield ['PT1M'];
91+
92+
yield ['PT1H'];
93+
94+
yield ['P1M'];
95+
96+
yield ['P1Y'];
8097
}
8198
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Symfony\Component\RateLimiter\Tests\Util;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Symfony\Component\RateLimiter\Util\TimeUtil;
7+
8+
class TimeUtilTest extends TestCase
9+
{
10+
public function provideTimeUtilData(): \Generator
11+
{
12+
yield ['PT5S', 5];
13+
14+
yield ['PT1M5S', 65];
15+
16+
yield ['PT2H4M', 2 * 3600 + 4 * 60];
17+
18+
// Use of 't' so the test will work independently of current month.
19+
yield ['P1M', 24 * 3600 * date('t')];
20+
21+
// Use of 'L' to determine if it's a leap year. Same goal as above set.
22+
yield ['P1Y', 24 * 3600 * (date('L') ? 366 : 365)];
23+
}
24+
25+
/**
26+
* @dataProvider provideTimeUtilData
27+
*/
28+
public function testDateIntervalToSeconds(string $dateIntervalString, int $expectedSeconds)
29+
{
30+
$this->assertSame($expectedSeconds, TimeUtil::dateIntervalToSeconds(new \DateInterval($dateIntervalString)));
31+
}
32+
}

src/Symfony/Component/RateLimiter/Util/TimeUtil.php

+3-5
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ final class TimeUtil
2020
{
2121
public static function dateIntervalToSeconds(\DateInterval $interval): int
2222
{
23-
return (float) $interval->format('%s') // seconds
24-
+ $interval->format('%i') * 60 // minutes
25-
+ $interval->format('%H') * 3600 // hours
26-
+ $interval->format('%d') * 3600 * 24 // days
27-
;
23+
$now = new \DateTimeImmutable();
24+
25+
return ($now->add($interval))->getTimestamp() - $now->getTimestamp();
2826
}
2927
}

0 commit comments

Comments
 (0)
0