DatabaseStore.php
TLDR
The file DatabaseStore.php
is a PHP file that defines the DatabaseStore
class in the Illuminate\Cache
namespace. This class implements the LockProvider
and Store
interfaces. It is responsible for storing and retrieving cache items in a database table.
Methods
__construct
This method is the constructor of the DatabaseStore
class. It initializes the necessary properties of the class, such as the database connection, cache table name, cache key prefix, lock table name, lock lottery configuration, and default lock timeout.
get
This method retrieves a cache item from the database by its key. It checks if the cache item exists and is not expired. If the item is expired, it removes it from the database. If the item is valid, it unserializes the value and returns it.
put
This method stores an item in the cache for a given number of seconds. It serializes the value and inserts or updates the cache item in the database table.
add
This method stores an item in the cache only if it does not already exist. It checks if the item exists in the cache database table. If it does not exist, it serializes the value and inserts it into the table. If the database connection supports the insertOrIgnore
method, it uses it to avoid duplicate entries.
increment
and decrement
These methods increment or decrement the value of a cache item by a given amount. They retrieve the cache item from the database, update the value, and store it back in the database.
forever
This method stores an item in the cache indefinitely. It calls the put
method with a very large expiration time (10 years).
lock
This method returns a DatabaseLock
instance, which is used to manage locks for cache items in the database.
restoreLock
This method restores a lock instance using the owner identifier. It calls the lock
method with a 0 seconds expiration time.
forget
This method removes an item from the cache by its key. It deletes the cache item from the database table.
forgetIfExpired
This method removes an item from the cache if it is expired. It deletes the cache item from the database table if its expiration time is less than or equal to the current time.
flush
This method removes all items from the cache. It deletes all cache items from the database table.
table
This method returns a query builder instance for the cache table. It is used to perform database operations on the cache table.
getConnection
This method returns the underlying database connection instance.
setLockConnection
This method sets the database connection that should be used to manage locks.
getPrefix
This method returns the cache key prefix.
serialize
and unserialize
These methods are used to serialize and unserialize cache values. They handle special cases for Postgres connections, where serialization may include binary data that needs to be encoded/decoded with base64.
Classes
There are no additional classes in this file.
<?php
namespace Illuminate\Cache;
use Closure;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\QueryException;
use Illuminate\Database\SqlServerConnection;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;
class DatabaseStore implements LockProvider, Store
{
use InteractsWithTime, RetrievesMultipleKeys;
/**
* The database connection instance.
*
* @var \Illuminate\Database\ConnectionInterface
*/
protected $connection;
/**
* The database connection instance that should be used to manage locks.
*
* @var \Illuminate\Database\ConnectionInterface
*/
protected $lockConnection;
/**
* The name of the cache table.
*
* @var string
*/
protected $table;
/**
* A string that should be prepended to keys.
*
* @var string
*/
protected $prefix;
/**
* The name of the cache locks table.
*
* @var string
*/
protected $lockTable;
/**
* An array representation of the lock lottery odds.
*
* @var array
*/
protected $lockLottery;
/**
* The default number of seconds that a lock should be held.
*
* @var int
*/
protected $defaultLockTimeoutInSeconds;
/**
* Create a new database store.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param string $table
* @param string $prefix
* @param string $lockTable
* @param array $lockLottery
* @return void
*/
public function __construct(ConnectionInterface $connection,
$table,
$prefix = '',
$lockTable = 'cache_locks',
$lockLottery = [2, 100],
$defaultLockTimeoutInSeconds = 86400)
{
$this->table = $table;
$this->prefix = $prefix;
$this->connection = $connection;
$this->lockTable = $lockTable;
$this->lockLottery = $lockLottery;
$this->defaultLockTimeoutInSeconds = $defaultLockTimeoutInSeconds;
}
/**
* Retrieve an item from the cache by key.
*
* @param string|array $key
* @return mixed
*/
public function get($key)
{
$prefixed = $this->prefix.$key;
$cache = $this->table()->where('key', '=', $prefixed)->first();
// If we have a cache record we will check the expiration time against current
// time on the system and see if the record has expired. If it has, we will
// remove the records from the database table so it isn't returned again.
if (is_null($cache)) {
return;
}
$cache = is_array($cache) ? (object) $cache : $cache;
// If this cache expiration date is past the current time, we will remove this
// item from the cache. Then we will return a null value since the cache is
// expired. We will use "Carbon" to make this comparison with the column.
if ($this->currentTime() >= $cache->expiration) {
$this->forgetIfExpired($key);
return;
}
return $this->unserialize($cache->value);
}
/**
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function put($key, $value, $seconds)
{
$key = $this->prefix.$key;
$value = $this->serialize($value);
$expiration = $this->getTime() + $seconds;
return $this->table()->upsert(compact('key', 'value', 'expiration'), 'key') > 0;
}
/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function add($key, $value, $seconds)
{
if (! is_null($this->get($key))) {
return false;
}
$key = $this->prefix.$key;
$value = $this->serialize($value);
$expiration = $this->getTime() + $seconds;
$doesntSupportInsertOrIgnore = [SqlServerConnection::class];
if (! in_array(get_class($this->getConnection()), $doesntSupportInsertOrIgnore)) {
return $this->table()->insertOrIgnore(compact('key', 'value', 'expiration')) > 0;
}
try {
return $this->table()->insert(compact('key', 'value', 'expiration'));
} catch (QueryException) {
// ...
}
return false;
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|bool
*/
public function increment($key, $value = 1)
{
return $this->incrementOrDecrement($key, $value, function ($current, $value) {
return $current + $value;
});
}
/**
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|bool
*/
public function decrement($key, $value = 1)
{
return $this->incrementOrDecrement($key, $value, function ($current, $value) {
return $current - $value;
});
}
/**
* Increment or decrement an item in the cache.
*
* @param string $key
* @param mixed $value
* @param \Closure $callback
* @return int|bool
*/
protected function incrementOrDecrement($key, $value, Closure $callback)
{
return $this->connection->transaction(function () use ($key, $value, $callback) {
$prefixed = $this->prefix.$key;
$cache = $this->table()->where('key', $prefixed)
->lockForUpdate()->first();
// If there is no value in the cache, we will return false here. Otherwise the
// value will be decrypted and we will proceed with this function to either
// increment or decrement this value based on the given action callbacks.
if (is_null($cache)) {
return false;
}
$cache = is_array($cache) ? (object) $cache : $cache;
$current = $this->unserialize($cache->value);
// Here we'll call this callback function that was given to the function which
// is used to either increment or decrement the function. We use a callback
// so we do not have to recreate all this logic in each of the functions.
$new = $callback((int) $current, $value);
if (! is_numeric($current)) {
return false;
}
// Here we will update the values in the table. We will also encrypt the value
// since database cache values are encrypted by default with secure storage
// that can't be easily read. We will return the new value after storing.
$this->table()->where('key', $prefixed)->update([
'value' => $this->serialize($new),
]);
return $new;
});
}
/**
* Get the current system time.
*
* @return int
*/
protected function getTime()
{
return $this->currentTime();
}
/**
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
{
return $this->put($key, $value, 315360000);
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new DatabaseLock(
$this->lockConnection ?? $this->connection,
$this->lockTable,
$this->prefix.$name,
$seconds,
$owner,
$this->lockLottery,
$this->defaultLockTimeoutInSeconds
);
}
/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
/**
* Remove an item from the cache.
*
* @param string $key
* @return bool
*/
public function forget($key)
{
$this->table()->where('key', '=', $this->prefix.$key)->delete();
return true;
}
/**
* Remove an item from the cache if it is expired.
*
* @param string $key
* @return bool
*/
public function forgetIfExpired($key)
{
$this->table()
->where('key', '=', $this->prefix.$key)
->where('expiration', '<=', $this->getTime())
->delete();
return true;
}
/**
* Remove all items from the cache.
*
* @return bool
*/
public function flush()
{
$this->table()->delete();
return true;
}
/**
* Get a query builder for the cache table.
*
* @return \Illuminate\Database\Query\Builder
*/
protected function table()
{
return $this->connection->table($this->table);
}
/**
* Get the underlying database connection.
*
* @return \Illuminate\Database\ConnectionInterface
*/
public function getConnection()
{
return $this->connection;
}
/**
* Specify the name of the connection that should be used to manage locks.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @return $this
*/
public function setLockConnection($connection)
{
$this->lockConnection = $connection;
return $this;
}
/**
* Get the cache key prefix.
*
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Serialize the given value.
*
* @param mixed $value
* @return string
*/
protected function serialize($value)
{
$result = serialize($value);
if ($this->connection instanceof PostgresConnection && str_contains($result, "\0")) {
$result = base64_encode($result);
}
return $result;
}
/**
* Unserialize the given value.
*
* @param string $value
* @return mixed
*/
protected function unserialize($value)
{
if ($this->connection instanceof PostgresConnection && ! Str::contains($value, [':', ';'])) {
$value = base64_decode($value);
}
return unserialize($value);
}
}