Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
308 changes: 223 additions & 85 deletions src/Phaseolies/Cache/CacheStore.php

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions src/Phaseolies/Cache/IncrementableCacheInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Phaseolies\Cache;

use Psr\SimpleCache\CacheInterface;

interface IncrementableCacheInterface extends CacheInterface
{
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param int $value
* @return int|bool
*/
public function increment($key, $value = 1): int|bool;

/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
* @return bool
*/
public function add($key, $value, $ttl = null): bool;
}
42 changes: 27 additions & 15 deletions src/Phaseolies/Cache/RateLimiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Phaseolies\Cache;

use Psr\SimpleCache\CacheInterface;
use Phaseolies\Cache\RateLimit;
use Psr\SimpleCache\InvalidArgumentException;

Expand All @@ -11,17 +10,17 @@ class RateLimiter
/**
* The cache store implementation.
*
* @var CacheInterface
* @var IncrementableCacheInterface
*/
protected $cache;

/**
* Create a new rate limiter instance.
*
* @param CacheInterface $cache
* @param IncrementableCacheInterface $cache
* @return void
*/
public function __construct(CacheInterface $cache)
public function __construct(IncrementableCacheInterface $cache)
{
$this->cache = $cache;
}
Expand All @@ -39,16 +38,21 @@ public function attempt(string $key, int $maxAttempts, int $decaySeconds): RateL
{
$timerKey = $key . '_timer';
$now = time();
$resetAt = $now + $decaySeconds;

try {
if (!$this->cache->has($key)) {
$this->cache->set($key, 1, $decaySeconds);
$this->cache->set($timerKey, $now + $decaySeconds, $decaySeconds);
if ($this->cache->add($key, 1, $decaySeconds)) {
$this->cache->add($timerKey, $resetAt, $decaySeconds);
$hits = 1;
} else {
$hits = $this->cache->increment($key);
if ($hits <= $maxAttempts) {
$this->cache->set($timerKey, $now + $decaySeconds, $decaySeconds);

if ($hits === false) {
$this->cache->set($key, 1, $decaySeconds);
$this->cache->set($timerKey, $resetAt, $decaySeconds);
$hits = 1;
} elseif ($hits <= $maxAttempts) {
$this->cache->set($timerKey, $resetAt, $decaySeconds);
}
}

Expand Down Expand Up @@ -127,17 +131,25 @@ public function hit(string $key, int $decaySeconds): int
{
$timerKey = $key . '_timer';
$now = time();
$resetAt = $now + $decaySeconds;

try {
if (!$this->cache->has($key)) {
if ($this->cache->add($key, 1, $decaySeconds)) {
$this->cache->add($timerKey, $resetAt, $decaySeconds);
return 1;
}

$hits = $this->cache->increment($key);

if ($hits === false) {
$this->cache->set($key, 1, $decaySeconds);
$this->cache->set($timerKey, $now + $decaySeconds, $decaySeconds);
$this->cache->set($timerKey, $resetAt, $decaySeconds);
return 1;
} else {
$hits = $this->cache->increment($key);
$this->cache->set($timerKey, $now + $decaySeconds, $decaySeconds);
return $hits;
}

$this->cache->set($timerKey, $resetAt, $decaySeconds);

return $hits;
} catch (InvalidArgumentException $e) {
throw $e;
}
Expand Down
24 changes: 24 additions & 0 deletions src/Phaseolies/Helpers/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,30 @@ function config(string|array $key, ?string $default = null): mixed
}
}

if (!function_exists('cache')) {
/**
* Get the cache store instance, retrieve an item, or store multiple items
*
* @param string|array|null $key
* @param mixed $default
* @return mixed
*/
function cache(string|array|null $key = null, mixed $default = null): mixed
{
$store = app('cache');

if (is_null($key)) {
return $store;
}

if (is_array($key)) {
return $store->setMultiple($key, $default);
}

return $store->get($key, $default);
}
}

if (!function_exists('is_auth')) {
/**
* Check if the user is authenticated.
Expand Down
3 changes: 3 additions & 0 deletions src/Phaseolies/Providers/CacheServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Psr\SimpleCache\CacheInterface;
use Phaseolies\Providers\ServiceProvider;
use Phaseolies\Cache\CacheStore;
use Phaseolies\Cache\IncrementableCacheInterface;

class CacheServiceProvider extends ServiceProvider
{
Expand All @@ -26,6 +27,8 @@ public function register(): void
{
$adapter = $this->createAdapter(config('caching.default', 'file'));
$cacheStore = new CacheStore($adapter, config('caching.prefix'));
$this->app->singleton(CacheStore::class, fn() => $cacheStore);
$this->app->singleton(IncrementableCacheInterface::class, fn() => $cacheStore);
$this->app->singleton(CacheInterface::class, fn() => $cacheStore);
$this->app->singleton('cache', fn() => $cacheStore);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Phaseolies/Providers/RateLimiterServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Phaseolies\Providers;

use Psr\SimpleCache\CacheInterface;
use Phaseolies\Providers\ServiceProvider;
use Phaseolies\Cache\IncrementableCacheInterface;
use Phaseolies\Cache\RateLimiter;

class RateLimiterServiceProvider extends ServiceProvider
Expand All @@ -16,7 +16,7 @@ class RateLimiterServiceProvider extends ServiceProvider
public function register(): void
{
$this->app->singleton(RateLimiter::class, function ($app) {
return new RateLimiter($app->make(CacheInterface::class));
return new RateLimiter($app->make(IncrementableCacheInterface::class));
});
}

Expand Down
Loading
Loading