Files
core/src/sammo/KVStorage.php
T

365 lines
9.3 KiB
PHP

<?php
namespace sammo;
use MeekroDBException;
use Ds\Map;
class KVStorage
{
private $db;
private $tableName;
private $storNamespace;
private ?Map $cacheData = null;
/** @var null|Map<string, self> */
static private ?Map $storageList = null;
static public function getStorage(\MeekroDB $db, $storNamespace, string $tableName = 'storage'): self
{
if (self::$storageList === null) {
self::$storageList = new Map();
}
$obj_id = spl_object_hash($db);
$fullKey = $obj_id . ',' . $storNamespace . ',' . $tableName;
if (self::$storageList->hasKey($fullKey)) {
return self::$storageList[$fullKey];
}
$obj = new static($db, $storNamespace, $tableName);
self::$storageList[$fullKey] = $obj;
return $obj;
}
protected function __construct(\MeekroDB $db, $storNamespace, string $tableName = 'storage')
{
$this->db = $db;
$this->storNamespace = $storNamespace;
$this->tableName = $tableName;
$this->turnOnCache();
}
public static function getValuesFromInterNamespace(\MeekroDB $db, string $tableName, string|int|\BackedEnum $key): array
{
$result = [];
$key = Util::valueFromEnum($key);
foreach ($db->queryAllLists(
'SELECT `namespace`, `value` FROM %b WHERE `key`=%s',
$tableName,
$key
) as [$namespaceName, $value]) {
$result[$namespaceName] = Json::decode($value);
}
return $result;
}
public function __get(string|int|\BackedEnum $key)
{
return $this->getValue($key);
}
public function __set(string|int|\BackedEnum $key, $value)
{
$this->setValue($key, $value);
}
public function __unset(string|int|\BackedEnum $key)
{
$this->deleteValue($key);
}
public function turnOnCache(): self
{
if ($this->cacheData === null) {
$this->cacheData = new Map();
}
return $this;
}
public function turnOffCache(): self
{
if ($this->cacheData !== null) {
$this->cacheData = null;
}
return $this;
}
public function resetCache(bool $disableCache = true): self
{
if ($disableCache) {
$this->cacheData = null;
} else {
$this->cacheData = [];
}
return $this;
}
public function resetValues(): self
{
if ($this->cacheData !== null) {
$this->cacheData = new Map();
}
return $this->resetDBNamespace();
}
public function invalidateCacheValue(string|int|\BackedEnum $key): self
{
if ($this->cacheData === null) {
return $this;
}
$key = Util::valueFromEnum($key);
$this->cacheData->remove($key, null);
return $this;
}
public function invalidateCacheValues(array $keys): self
{
if ($this->cacheData === null) {
return $this;
}
$keys = Util::valuesFromEnumArray($keys);
foreach ($keys as $key) {
$this->cacheData->remove($key, null);
}
return $this;
}
public function cacheAll(bool $invalidateAll = true): self
{
if (!$invalidateAll && $this->cacheData !== null && count($this->cacheData) > 0) {
return $this;
}
$this->cacheData = $this->getDBAll();
return $this;
}
public function cacheValues(array $keys, bool $invalidateAll = false): self
{
if ($this->cacheData === null) {
$this->cacheData = new Map();
}
$keys = Util::valuesFromEnumArray($keys);
if ($invalidateAll) {
$notExists = $keys;
} else {
$notExists = [];
foreach ($keys as $key) {
if (!$this->cacheData->hasKey($key)) {
$notExists[] = $key;
}
}
if (!$notExists) {
return $this;
}
}
$values = $this->getDBValues($notExists);
foreach ($notExists as $key) {
$this->cacheData[$key] = $values->get($key, null);
}
return $this;
}
public function getAll(bool $onlyCache = false): array
{
if ($onlyCache && $this->cacheData !== null && count($this->cacheData) > 0) {
return $this->cacheData->toArray();
}
$result = $this->getDBAll();
if ($this->cacheData !== null) {
$this->cacheData = $result;
}
return $result->toArray();
}
public function getValuesAsArray(array $keys, bool $onlyCache = false): array
{
if (!$keys) {
return [];
}
$keys = Util::valuesFromEnumArray($keys);
$dictResult = $this->getValues($keys, $onlyCache);
$result = [];
foreach ($keys as $key) {
$result[] = $dictResult[$key] ?? null;
}
return $result;
}
public function getValues(array $keys, bool $onlyCache = false): array
{
if (!$keys) {
return [];
}
$keys = Util::valuesFromEnumArray($keys);
if ($this->cacheData === null) {
return $this->getDBValues($keys)->toArray();
}
$result = [];
$notExists = [];
//TODO: DB Select에서 as를 쓸 수 있으면 좋을 듯.
foreach ($keys as $key) {
if (!$this->cacheData->hasKey($key)) {
$notExists[] = $key;
continue;
}
$result[$key] = $this->cacheData[$key];
}
if ($onlyCache) {
foreach ($notExists as $emptyKey) {
$result[$emptyKey] = null;
}
return $result;
}
$dbResult = $this->getDBValues($notExists);
foreach ($dbResult as $key => $value) {
$result[$key] = $value;
$this->cacheData[$key] = $value;
}
return $result;
}
public function getValue(string|int|\BackedEnum $key, bool $onlyCache = false)
{
$key = Util::valueFromEnum($key);
if ($this->cacheData !== null && ($onlyCache || $this->cacheData->hasKey($key))) {
return $this->cacheData->get($key, null);
}
$value = $this->getDBValue($key);
if ($this->cacheData !== null) {
$this->cacheData[$key] = $value;
}
return $value;
}
/**
* @param mixed $value
* @return KVStorage
* @throws MeekroDBException
*/
public function setValue(string|int|\BackedEnum $key, $value): self
{
$key = Util::valueFromEnum($key);
if ($value === null) {
return $this->deleteValue($key);
}
if ($this->cacheData) {
$this->cacheData[$key] = $value;
}
return $this->setDBValue($key, $value);
}
/**
*
* @param string|int $key
* @param mixed $value
* @return KVStorage
* @throws MeekroDBException
*/
public function deleteValue(string|int|\BackedEnum $key): self
{
$key = Util::valueFromEnum($key);
if($this->cacheData !== null){
$this->cacheData->remove($key, null);
}
return $this->deleteDBValue($key);
}
private function getDBAll(): Map
{
$result = new Map();
foreach ($this->db->queryAllLists(
'SELECT `key`, `value` FROM %b WHERE `namespace`=%s',
$this->tableName,
$this->storNamespace
) as list($key, $value)) {
$result[$key] = Json::decode($value);
}
return $result;
}
private function getDBValues(array $keys): Map
{
if (!$keys) {
return new Map();
}
$result = new Map();
foreach ($this->db->queryAllLists(
'SELECT `key`, `value` FROM %b WHERE `namespace`=%s AND `key` IN %ls',
$this->tableName,
$this->storNamespace,
$keys
) as list($key, $value)) {
$result[$key] = Json::decode($value);
}
foreach ($keys as $key) {
if (!$result->hasKey($key)) {
$result[$key] = null;
}
}
return $result;
}
private function getDBValue(string $key)
{
$value = $this->db->queryFirstField(
'SELECT `value` FROM %b WHERE `namespace`=%s AND `key`=%s',
$this->tableName,
$this->storNamespace,
$key
);
if ($value === null) {
return null;
}
return Json::decode($value);
}
private function setDBValue(string $key, $value): self
{
if ($value === null) {
return $this->deleteDBValue($key);
}
$this->db->insertUpdate($this->tableName, [
'namespace' => $this->storNamespace,
'key' => $key,
'value' => Json::encode($value)
]);
return $this;
}
private function deleteDBValue(string $key): self
{
$this->db->delete(
$this->tableName,
'`namespace`=%s AND `key`=%s',
$this->storNamespace,
$key
);
return $this;
}
private function resetDBNamespace(): self
{
$this->db->delete($this->tableName, 'namespace=%s', $this->storNamespace);
return $this;
}
}