Skip to content

Commit 6cc8aaf

Browse files
[8.x] Adds attempt method to Rate Limiter (#38313)
* Adds an attempt method w/tests. * Modified to return callback result. * Style fixes. * formatting Co-authored-by: Taylor Otwell <[email protected]>
1 parent 9b7491e commit 6cc8aaf

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

src/Illuminate/Cache/RateLimiter.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@ public function limiter(string $name)
6060
return $this->limiters[$name] ?? null;
6161
}
6262

63+
/**
64+
* Attempts to execute a callback if it's not limited.
65+
*
66+
* @param string $key
67+
* @param int $maxAttempts
68+
* @param \Closure $callback
69+
* @param int $decaySeconds
70+
* @return mixed
71+
*/
72+
public function attempt($key, $maxAttempts, Closure $callback, $decaySeconds = 60)
73+
{
74+
if ($this->tooManyAttempts($key, $maxAttempts)) {
75+
return false;
76+
}
77+
78+
return tap($callback() ?: true, function () use ($key, $decaySeconds) {
79+
$this->hit($key, $decaySeconds);
80+
});
81+
}
82+
6383
/**
6484
* Determine if the given key has been "accessed" too many times.
6585
*

src/Illuminate/Support/Facades/RateLimiter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* @method static int retriesLeft($key, $maxAttempts)
1313
* @method static void clear($key)
1414
* @method static int availableIn($key)
15+
* @method static bool attempt($key, $maxAttempts, \Closure $callback, $decaySeconds = 60)
1516
*
1617
* @see \Illuminate\Cache\RateLimiter
1718
*/

tests/Cache/CacheRateLimiterTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,53 @@ public function testAvailableInReturnsPositiveValues()
7676
$this->assertTrue($rateLimiter->availableIn('key:timer') >= 0);
7777
$this->assertTrue($rateLimiter->availableIn('key:timer') >= 0);
7878
}
79+
80+
public function testAttemptsCallbackReturnsTrue()
81+
{
82+
$cache = m::mock(Cache::class);
83+
$cache->shouldReceive('get')->once()->with('key', 0)->andReturn(0);
84+
$cache->shouldReceive('add')->once()->with('key:timer', m::type('int'), 1);
85+
$cache->shouldReceive('add')->once()->with('key', 0, 1)->andReturns(1);
86+
$cache->shouldReceive('increment')->once()->with('key')->andReturn(1);
87+
88+
$executed = false;
89+
90+
$rateLimiter = new RateLimiter($cache);
91+
92+
$this->assertTrue($rateLimiter->attempt('key', 1, function() use (&$executed) {
93+
$executed = true;
94+
}, 1));
95+
$this->assertTrue($executed);
96+
}
97+
98+
public function testAttemptsCallbackReturnsCallbackReturn()
99+
{
100+
$cache = m::mock(Cache::class);
101+
$cache->shouldReceive('get')->once()->with('key', 0)->andReturn(0);
102+
$cache->shouldReceive('add')->once()->with('key:timer', m::type('int'), 1);
103+
$cache->shouldReceive('add')->once()->with('key', 0, 1)->andReturns(1);
104+
$cache->shouldReceive('increment')->once()->with('key')->andReturn(1);
105+
106+
$rateLimiter = new RateLimiter($cache);
107+
108+
$this->assertEquals('foo', $rateLimiter->attempt('key', 1, function() {
109+
return 'foo';
110+
}, 1));
111+
}
112+
113+
public function testAttemptsCallbackReturnsFalse()
114+
{
115+
$cache = m::mock(Cache::class);
116+
$cache->shouldReceive('get')->once()->with('key', 0)->andReturn(2);
117+
$cache->shouldReceive('has')->once()->with('key:timer')->andReturn(true);
118+
119+
$executed = false;
120+
121+
$rateLimiter = new RateLimiter($cache);
122+
123+
$this->assertFalse($rateLimiter->attempt('key', 1, function() use (&$executed) {
124+
$executed = true;
125+
}, 1));
126+
$this->assertFalse($executed);
127+
}
79128
}

0 commit comments

Comments
 (0)