Skip to content

Instantly share code, notes, and snippets.

@DarkGhostHunter
Last active August 3, 2021 23:46
Show Gist options
  • Save DarkGhostHunter/0a6874d128323deae2d0c864852d7317 to your computer and use it in GitHub Desktop.
Save DarkGhostHunter/0a6874d128323deae2d0c864852d7317 to your computer and use it in GitHub Desktop.
<?php
namespace App\Helpers;
use DateInterval;
use DateTimeInterface;
use Illuminate\Contracts\Cache\Factory;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\Repository;
use RuntimeException;
class RememberUnique
{
/**
* Create a new instance.
*
* @param \Illuminate\Contracts\Cache\Factory $cache
* @param string|null $store
* @param int $timeout
* @param string|null $owner
*/
public function __construct(
protected Factory $cache,
protected ?string $store = null,
protected int $timeout = 10,
protected ?string $owner = null)
{
//
}
/**
* Sets the store to use for getting the results. Accepts "null" to default.
*
* @param string|null $store
*
* @return $this
*/
public function store(string $store = null): static
{
$this->store = $store;
return $this;
}
/**
* Sets the timeout for waiting the lock release.
*
* @param int $timeout
*
* @return $this
*/
public function waitFor(int $timeout): static
{
$this->timeout = $timeout;
return $this;
}
/**
* Sets the owner key shared across multiple processes.
*
* @param string $key
*
* @return $this
*/
public function withOwnerKey(string $key): static
{
$this->owner = $key;
return $this;
}
/**
* Remembers the callback results for a given amount of time.
*
* @param string $key
* @param int|\DateTimeInterface|\DateInterval $ttl
* @param callable $callback
*
* @return mixed
* @throws \Illuminate\Contracts\Cache\LockTimeoutException
*/
public function remember(string $key, int|DateTimeInterface|DateInterval $ttl, callable $callback): mixed
{
$cache = $this->getCache();
// If the cache already has the result stored, return it.
if ($cache->has($key)) {
return $cache->get($key);
}
/** @var \Illuminate\Contracts\Cache\Lock $lock */
$lock = $cache->getStore()->lock($key, $this->timeout, $this->owner);
// If the lock is instantly acquired, execute the callback, save it in
// the cache store, release the lock, and return it to the developer.
if ($lock->get()) {
try {
$result = value($callback);
} finally {
$lock->release();
}
$cache->put($key, $result, $ttl);
return $result;
}
// Wait until the lock is released to retrieve and return the results.
return $lock->block($this->timeout, static function () use ($cache, $key): mixed {
return $cache->get($key);
});
}
/**
* Returns the Cache Repository to retrieve and store the data.
*
* @return \Illuminate\Contracts\Cache\Repository
*/
protected function getCache(): Repository
{
$cache = $this->cache->store($this->store);
if (! $cache->getStore() instanceof LockProvider) {
throw new RuntimeException("The cache [$this->store] doesn't supports atomic locks.");
}
return $cache;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment