Last active
August 3, 2021 23:46
-
-
Save DarkGhostHunter/0a6874d128323deae2d0c864852d7317 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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