trying to implement acceptable type system

This commit is contained in:
Dennis Eichhorn 2019-12-08 00:57:54 +01:00
parent c952489736
commit be1283d6dc
7 changed files with 150 additions and 79 deletions

View File

@ -16,6 +16,7 @@ namespace phpOMS\DataStorage\Cache\Connection;
use phpOMS\DataStorage\Cache\CacheStatus; use phpOMS\DataStorage\Cache\CacheStatus;
use phpOMS\DataStorage\Cache\CacheType; use phpOMS\DataStorage\Cache\CacheType;
use phpOMS\DataStorage\Cache\Connection\CacheValueType;
/** /**
* Cache handler. * Cache handler.
@ -163,20 +164,36 @@ abstract class ConnectionAbstract implements ConnectionInterface
} }
/** /**
* Parse values for cache storage * Analyze caching data type.
* *
* @param mixed $value Value to parse * @param mixed $value Data to cache
* *
* @return mixed * @return int Returns the cache type for a value
*
* @throws \InvalidArgumentException This exception is thrown if an unsupported datatype is used
* *
* @since 1.0.0 * @since 1.0.0
*/ */
protected function parseValue($value) protected function dataType($value) : int
{ {
if (\is_array($value)) { if (\is_int($value)) {
return \json_encode($value); return CacheValueType::_INT;
} elseif (\is_float($value)) {
return CacheValueType::_FLOAT;
} elseif (\is_string($value)) {
return CacheValueType::_STRING;
} elseif (\is_bool($value)) {
return CacheValueType::_BOOL;
} elseif (\is_array($value)) {
return CacheValueType::_ARRAY;
} elseif ($value === null) {
return CacheValueType::_NULL;
} elseif ($value instanceof \Serializable) {
return CacheValueType::_SERIALIZABLE;
} elseif ($value instanceof \JsonSerializable) {
return CacheValueType::_JSONSERIALIZABLE;
} }
return $value; throw new \InvalidArgumentException('Invalid value type.');
} }
} }

View File

@ -194,40 +194,6 @@ class FileCache extends ConnectionAbstract
return $type . self::DELIM . $expire . self::DELIM . $raw; return $type . self::DELIM . $expire . self::DELIM . $raw;
} }
/**
* Analyze caching data type.
*
* @param mixed $value Data to cache
*
* @return int Returns the cache type for a value
*
* @throws \InvalidArgumentException This exception is thrown if an unsupported datatype is used
*
* @since 1.0.0
*/
private function dataType($value) : int
{
if (\is_int($value)) {
return CacheValueType::_INT;
} elseif (\is_float($value)) {
return CacheValueType::_FLOAT;
} elseif (\is_string($value)) {
return CacheValueType::_STRING;
} elseif (\is_bool($value)) {
return CacheValueType::_BOOL;
} elseif (\is_array($value)) {
return CacheValueType::_ARRAY;
} elseif ($value === null) {
return CacheValueType::_NULL;
} elseif ($value instanceof \Serializable) {
return CacheValueType::_SERIALIZABLE;
} elseif ($value instanceof \JsonSerializable) {
return CacheValueType::_JSONSERIALIZABLE;
}
throw new \InvalidArgumentException('Invalid value type.');
}
/** /**
* Create string representation of data for storage * Create string representation of data for storage
* *
@ -350,6 +316,15 @@ class FileCache extends ConnectionAbstract
case CacheValueType::_NULL: case CacheValueType::_NULL:
return null; return null;
case CacheValueType::_JSONSERIALIZABLE: case CacheValueType::_JSONSERIALIZABLE:
$namespaceStart = (int) \strpos($raw, self::DELIM, $expireEnd);
$namespaceEnd = (int) \strpos($raw, self::DELIM, $namespaceStart + 1);
$namespace = \substr($raw, $namespaceStart + 1, $namespaceEnd - $namespaceStart - 1);
if ($namespace === false) {
return null;
}
return new $namespace();
case CacheValueType::_SERIALIZABLE: case CacheValueType::_SERIALIZABLE:
$namespaceStart = (int) \strpos($raw, self::DELIM, $expireEnd); $namespaceStart = (int) \strpos($raw, self::DELIM, $expireEnd);
$namespaceEnd = (int) \strpos($raw, self::DELIM, $namespaceStart + 1); $namespaceEnd = (int) \strpos($raw, self::DELIM, $namespaceStart + 1);

View File

@ -170,7 +170,7 @@ class MemCached extends ConnectionAbstract
return false; return false;
} }
$value = $this->parseValue($value); // todo: handle parsing
return $this->con->replace($key, $value, \max($expire, 0)); return $this->con->replace($key, $value, \max($expire, 0));
} }

View File

@ -17,6 +17,8 @@ namespace phpOMS\DataStorage\Cache\Connection;
use phpOMS\DataStorage\Cache\CacheStatus; use phpOMS\DataStorage\Cache\CacheStatus;
use phpOMS\DataStorage\Cache\CacheType; use phpOMS\DataStorage\Cache\CacheType;
use phpOMS\DataStorage\Cache\Exception\InvalidConnectionConfigException; use phpOMS\DataStorage\Cache\Exception\InvalidConnectionConfigException;
use phpOMS\Stdlib\Base\Exception\InvalidEnumValue;
use phpOMS\DataStorage\Cache\Connection\CacheValueType;
/** /**
* RedisCache class. * RedisCache class.
@ -33,6 +35,14 @@ class RedisCache extends ConnectionAbstract
*/ */
protected string $type = CacheType::REDIS; protected string $type = CacheType::REDIS;
/**
* Delimiter for cache meta data
*
* @var string
* @since 1.0.0
*/
private const DELIM = '$';
/** /**
* Constructor * Constructor
* *
@ -95,21 +105,11 @@ class RedisCache extends ConnectionAbstract
return; return;
} }
if (!(\is_scalar($value) || $value === null || \is_array($value) || $value instanceof \JsonSerializable || $value instanceof \Serializable)) {
throw new \InvalidArgumentException();
}
if (\is_bool($value)) {
$value = $value ? 'true' : 'false';
} elseif ($value === null) {
$value = 'null';
}
if ($expire > 0) { if ($expire > 0) {
$this->con->set($key, $value, $expire); $this->con->set($key, $this->build($value), $expire);
} }
$this->con->set($key, $value); $this->con->set($key, $this->build($value));
} }
/** /**
@ -121,23 +121,11 @@ class RedisCache extends ConnectionAbstract
return false; return false;
} }
// todo: pull out
if (!(\is_scalar($value) || $value === null || \is_array($value) || $value instanceof \JsonSerializable || $value instanceof \Serializable)) {
throw new \InvalidArgumentException();
}
// todo: pull out
if (\is_bool($value)) {
$value = $value ? 'true' : 'false';
} elseif ($value === null) {
$value = 'null';
}
if ($expire > 0) { if ($expire > 0) {
return $this->con->setNx($key, $value, $expire); return $this->con->setNx($key, $this->build($value), $expire);
} }
return $this->con->setNx($key, $value); return $this->con->setNx($key, $this->build($value));
} }
/** /**
@ -151,12 +139,10 @@ class RedisCache extends ConnectionAbstract
$result = $this->con->get($key); $result = $this->con->get($key);
if ($result === 'true') { if (\is_string($result)) {
$result = true; $type = (int) $result[0];
} elseif ($result === 'false') { $start = (int) \strpos($result, self::DELIM);
$result = false; $result = $this->reverseValue($type, $result, $start);
} elseif ($result === 'null') {
$result = null;
} }
return $result; return $result;
@ -211,10 +197,10 @@ class RedisCache extends ConnectionAbstract
return false; return false;
} }
$value = $this->parseValue($value); // todo: parse value
if ($this->con->exists($key) > 0) { if ($this->con->exists($key) > 0) {
$this->set($key, $value, $expire); $this->set($key, $this->build($value), $expire);
return true; return true;
} }
@ -258,4 +244,97 @@ class RedisCache extends ConnectionAbstract
{ {
$this->close(); $this->close();
} }
/**
* Removing all cache elements larger or equal to the expiration date. Call flushAll for removing persistent cache elements (expiration is negative) as well.
*
* @param mixed $value Data to cache
*
* @return mixed
*
* @since 1.0.0
*/
private function build($value)
{
$type = $this->dataType($value);
$raw = $this->cachify($value, $type);
return \is_string($raw) ? $type . self::DELIM . $raw : $raw;
}
/**
* Create string representation of data for storage
*
* @param mixed $value Value of the data
* @param int $type Type of the cache data
*
* @return mixed
*
* @throws InvalidEnumValue This exception is thrown if an unsupported cache value type is used
*
* @since 1.0.0
*/
private function cachify($value, int $type)
{
if (\is_float($value) || \is_int($value)) {
return $value;
} elseif ($type === CacheValueType::_BOOL) {
return (string) ((int) $value);
} elseif ($type === CacheValueType::_ARRAY) {
return (string) \json_encode($value);
} elseif ($type === CacheValueType::_SERIALIZABLE) {
return \get_class($value) . self::DELIM . $value->serialize();
} elseif ($type === CacheValueType::_JSONSERIALIZABLE) {
return \get_class($value) . self::DELIM . ((string) \json_encode($value->jsonSerialize()));
} elseif ($type === CacheValueType::_NULL) {
return '';
}
throw new InvalidEnumValue($type);
}
/**
* Parse cached value
*
* @param int $type Cached value type
* @param mixed $raw Cached value
* @param int $start Value start position
*
* @return mixed
*
* @since 1.0.0
*/
private function reverseValue(int $type, $raw, int $start)
{
switch ($type) {
case \is_int($raw):
case \is_float($raw):
return $raw;
case CacheValueType::_BOOL:
return (bool) \substr($raw, $start + 1);
case CacheValueType::_STRING:
return \substr($raw, $start + 1);
case CacheValueType::_ARRAY:
$array = \substr($raw, $start + 1);
return \json_decode($array === false ? '[]' : $array, true);
case CacheValueType::_NULL:
return null;
case CacheValueType::_JSONSERIALIZABLE:
case CacheValueType::_SERIALIZABLE:
$namespaceStart = (int) \strpos($raw, self::DELIM, $start);
$namespaceEnd = (int) \strpos($raw, self::DELIM, $namespaceStart + 1);
$namespace = \substr($raw, $namespaceStart + 1, $namespaceEnd - $namespaceStart - 1);
if ($namespace === false) {
return null;
}
$obj = new $namespace();
$obj->unserialize(\substr($raw, $namespaceEnd + 1));
return $obj;
default:
return null;
}
}
} }

View File

@ -108,7 +108,7 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals('abc', $this->cache->get('key7')->val); self::assertEquals('abc', $this->cache->get('key7')->val);
$this->cache->set('key8', new FileCacheJsonSerializable()); $this->cache->set('key8', new FileCacheJsonSerializable());
self::assertEquals('abc', $this->cache->get('key8')->val); self::assertEquals('asdf', $this->cache->get('key8')->val);
} }
/** /**

View File

@ -109,7 +109,7 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase
self::assertEquals('abc', $this->cache->get('key7')->val); self::assertEquals('abc', $this->cache->get('key7')->val);
$this->cache->set('key8', new FileCacheJsonSerializable()); $this->cache->set('key8', new FileCacheJsonSerializable());
self::assertEquals('asdf', $this->cache->get('key8')->val); // @todo: different from file cache fix! self::assertEquals('asdf', $this->cache->get('key8')->val);
} }
/** /**

View File

@ -105,7 +105,7 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals('abc', $this->cache->get('key7')->val); self::assertEquals('abc', $this->cache->get('key7')->val);
$this->cache->set('key8', new FileCacheJsonSerializable()); $this->cache->set('key8', new FileCacheJsonSerializable());
self::assertEquals('asdf', $this->cache->get('key8')->val); // todo: different from file cache fix! self::assertEquals('asdf', $this->cache->get('key8')->val);
} }
/** /**