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
321 changes: 283 additions & 38 deletions src/Phaseolies/Cache/CacheStore.php

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions src/Phaseolies/Cache/RateLimiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ public function attempt(string $key, int $maxAttempts, int $decaySeconds): RateL
$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 @@ -147,8 +145,6 @@ public function hit(string $key, int $decaySeconds): int
return 1;
}

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

return $hits;
} catch (InvalidArgumentException $e) {
throw $e;
Expand Down
193 changes: 193 additions & 0 deletions src/Phaseolies/Cache/TaggedCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php

namespace Phaseolies\Cache;

use Symfony\Component\Cache\Adapter\TagAwareAdapter;
use Symfony\Contracts\Cache\ItemInterface;

class TaggedCache
{
/**
* @param CacheStore $store
* @param TagAwareAdapter $pool
* @param array<int, string> $tags
*/
public function __construct(
protected CacheStore $store,
protected TagAwareAdapter $pool,
protected array $tags
) {
if ($this->tags === []) {
throw new \InvalidArgumentException('At least one cache tag is required.');
}
}

/**
* Get a tagged item.
*
* @param mixed $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null): mixed
{
$item = $this->pool->getItem($this->store->validatedKey($key));

return $item->isHit() ? $item->get() : $default;
}

/**
* Store an item with this cache's tags.
*
* @param mixed $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
* @return bool
*/
public function set($key, $value, $ttl = null): bool
{
$item = $this->pool->getItem($this->store->validatedKey($key));
$item->set($value)->tag($this->tags);

$seconds = $this->store->ttlSeconds($ttl);

if ($seconds !== null) {
$item->expiresAfter($seconds);
}

return $this->pool->save($item);
}

/**
* Store an item with this cache's tags until it is flushed.
*
* @param mixed $key
* @param mixed $value
* @return bool
*/
public function forever($key, $value): bool
{
$item = $this->pool->getItem($this->store->validatedKey($key));
$item->set($value)->tag($this->tags)->expiresAfter(null);

return $this->pool->save($item);
}

/**
* Determine whether a tagged item exists.
*
* @param mixed $key
* @return bool
*/
public function has($key): bool
{
return $this->pool->hasItem($this->store->validatedKey($key));
}

/**
* Remove one tagged item.
*
* @param mixed $key
* @return bool
*/
public function delete($key): bool
{
return $this->pool->deleteItem($this->store->validatedKey($key));
}

/**
* Remove one tagged item.
*
* @param mixed $key
* @return bool
*/
public function forget($key): bool
{
return $this->delete($key);
}

/**
* Get a tagged item and remove it in one step.
*
* @param mixed $key
* @param mixed $default
* @return mixed
*/
public function pull($key, $default = null): mixed
{
$key = $this->store->validatedKey($key);
$item = $this->pool->getItem($key);

if (!$item->isHit()) {
return $default;
}

$value = $item->get();
$this->pool->deleteItem($key);

return $value;
}

/**
* Get a tagged item, or run the callback and store its result with these tags.
*
* @param string $key
* @param int|\DateInterval|null $ttl
* @param \Closure $callback
* @return mixed
*/
public function stash(string $key, $ttl, \Closure $callback): mixed
{
return $this->remember($key, $this->store->ttlSeconds($ttl), $callback);
}

/**
* Get a tagged item, or run the callback and store its result until it is flushed.
*
* @param string $key
* @param \Closure $callback
* @return mixed
*/
public function stashForever(string $key, \Closure $callback): mixed
{
return $this->remember($key, null, $callback);
}

/**
* Remove every item that has any of this cache's tags.
*
* @return bool
*/
public function flush(): bool
{
return $this->pool->invalidateTags($this->tags);
}

/**
* The tags this cache writes with.
*
* @return array<int, string>
*/
public function getTags(): array
{
return $this->tags;
}

/**
* @param string $key
* @param int|null $seconds
* @param \Closure $callback
* @return mixed
*/
protected function remember(string $key, ?int $seconds, \Closure $callback): mixed
{
return $this->pool->get(
$this->store->validatedKey($key),
function (ItemInterface $item) use ($seconds, $callback) {
$item->tag($this->tags)->expiresAfter($seconds);

return $callback();
}
);
}
}
12 changes: 11 additions & 1 deletion src/Phaseolies/Console/Commands/ClearCacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ClearCacheCommand extends Command
*
* @var string
*/
protected $name = 'cache:clear';
protected $name = 'cache:clear {--store= : Clear only this store from the caching config, leaving the framework cache files alone}';

/**
* The command description shown in the Pool command list.
Expand All @@ -28,6 +28,16 @@ class ClearCacheCommand extends Command
*/
public function handle(): int
{
$store = $this->option('store');

if ($store) {
return $this->withTiming(function () use ($store) {
Cache::store($store)->clear();

return Command::SUCCESS;
}, "Cache store [{$store}] has been cleared.");
}

return $this->withTiming(function() {
$cacheDir = base_path('storage/framework/cache');
$this->deleteDirectoryContents($cacheDir);
Expand Down
Loading
Loading