Initial commit cache

This commit is contained in:
2018-02-28 15:01:45 +01:00
commit 01a13b7f2b
20 changed files with 1117 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace IQParts\Cache\Adapter;
interface CacheAdapterInterface
{
public const NO_TTL = -1;
/**
* @param string $key
* @return mixed
*/
public function get(string $key);
/**
* @param string $key
* @param $value
* @param int|null $ttl
* @return void
*/
public function set(string $key, $value, int $ttl = null);
/**
* @param string $key
* @return mixed
*/
public function delete(string $key);
/**
* @param string $key
* @return mixed
*/
public function keys($key = '*');
/**
* @param $key
* @return int
*/
public function ttl($key): int;
}

View File

@@ -0,0 +1,242 @@
<?php
namespace IQParts\Cache\Adapter;
use IQParts\Cache\Serializer\SerializerInterface;
class FilesystemAdapter implements CacheAdapterInterface
{
/**
* @var string
*/
private $directory;
/**
* @var null|int
*/
private $chmod;
/**
* @var string
*/
private $directorySeparator;
/**
* @var int|null
*/
private $ttl;
/**
* @var array
*/
private $timeToLive;
/**
* @var SerializerInterface
*/
private $serializer;
/**
* @var array
*/
private $index;
/**
* @var string
*/
private $indexLocation;
/**
* @var string
*/
private $ttlLocation;
/**
* FilesystemAdapter constructor.
* @param string $directory
* @param SerializerInterface $serializer
* @param int|null $chmod
* @param int|null $ttl
* @param string|null $directorySeperator
*/
public function __construct(
string $directory,
SerializerInterface $serializer,
int $chmod = null,
int $ttl = null,
string $directorySeperator = null
)
{
$this->indexLocation = $directory . '/' . md5($directory) . 'index';
$this->ttlLocation = $directory . '/' . md5($directory) . 'ttl';
$this->directory = $directory;
$this->serializer = $serializer;
$this->directorySeparator = $directorySeperator;
$this->chmod = $chmod;
$this->ttl = $ttl;
if (file_exists($this->indexLocation)) {
$this->index = json_decode(file_get_contents($this->indexLocation), true);
} else {
$this->index = [];
}
if (file_exists($this->ttlLocation)) {
$this->timeToLive = json_decode(file_get_contents($this->ttlLocation), true);
} else {
$this->timeToLive = [];
}
}
/**
* @param string $key
* @return mixed
*/
public function get(string $key)
{
$file = $this->getFilename($key);
if ($this->exists($file)) {
if (isset($this->timeToLive[$key])) {
if ($this->ttl($key) > 0) {
return $this->serializer->deserialize(file_get_contents($file));
}
} else {
return $this->serializer->deserialize(file_get_contents($file));
}
}
return null;
}
/**
* @param string $key
* @param $value
* @param int|null $ttl
* @return void
*/
public function set(string $key, $value, int $ttl = null)
{
$file = $this->getFilename($key);
file_put_contents($file, $this->serializer->serialize($value));
if ($this->chmod !== null) {
chmod($file, $this->chmod);
}
if ($ttl !== null) {
$this->timeToLive[$key] = $ttl;
} else if ($this->ttl !== null) {
$this->timeToLive[$key] = $this->ttl;
}
$this->index[$key] = $file;
$this->saveIndex();
}
/**
* @param string $key
*/
public function delete(string $key)
{
if (strpos($key, '*') !== false) {
$this->deleteGlob($key);
$this->saveIndex();
} else {
$file = $this->getFilename($key);
if ($this->exists($file)) {
unlink($file);
}
unset($this->index[$key]);
unset($this->timeToLive[$key]);
$this->saveIndex();
}
}
/**
* @param string $key
* @return mixed
*/
public function keys($key = '*')
{
$matches = [];
foreach ($this->index as $name => $index) {
if (fnmatch($key, $name)) {
$matches[] = $name;
}
}
return $matches;
}
/**
* @param $key
* @return int
*/
public function ttl($key): int
{
$file = $this->getFilename($key);
if (isset($this->timeToLive[$key])) {
return max(filemtime($file) + $this->timeToLive[$key] - time(), 0);
}
return self::NO_TTL;
}
/**
* @param $pattern
*/
private function deleteGlob($pattern)
{
foreach ($this->index as $key => $value) {
if (fnmatch($pattern, $key)) {
$this->delete($key);
}
}
}
/**
* @param $file
* @return bool
*/
private function exists(string $file)
{
return file_exists($file);
}
/**
* @param $key
* @return string
*/
private function getFilename(string $key)
{
list($directory, $file) = $this->getDirectoryAndFile($key);
return $directory . '/' . md5($file);
}
/**
* @param string $key
* @return array
*/
private function getDirectoryAndFile(string $key)
{
$directory = $this->directory;
if ($this->directorySeparator !== null) {
while (($position = strpos($key, $this->directorySeparator)) !== false) {
$dirName = md5(substr($key, 0, $position));
$directory = $directory . '/' . $dirName;
$this->createSubDirectoryIfNotExists($directory);
$key = substr($key, $position + 1);
}
}
return [$directory, $key];
}
/**
* @param $directory
*/
private function createSubDirectoryIfNotExists($directory)
{
if (file_exists($directory) === false) {
mkdir($directory);
if ($this->chmod !== null) {
chmod($directory, $this->chmod);
}
}
}
private function saveIndex()
{
file_put_contents($this->indexLocation, json_encode($this->index));
file_put_contents($this->ttlLocation, json_encode($this->timeToLive));
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace IQParts\Cache\Adapter;
final class MemoryAdapter implements CacheAdapterInterface
{
/**
* @var array
*/
private $data = [];
/**
* @var array
*/
private $timeToLive = [];
/**
* @param string $key
* @return mixed
*/
public function get(string $key)
{
if (!isset($this->data[$key])) {
return null;
}
if (!isset($this->timeToLive[$key])) {
return $this->data[$key];
}
if ($this->ttl($key) > 0) {
return $this->data[$key];
}
unset($this->timeToLive[$key]);
unset($this->data[$key]);
return null;
}
/**
* @param string $key
* @param $value
* @param int|null $ttl
* @return void
*/
public function set(string $key, $value, int $ttl = null)
{
$this->data[$key] = $value;
if ($ttl !== null) {
$this->timeToLive[$key] = time() + $ttl;
}
}
/**
* @param string $key
* @return mixed
*/
public function delete(string $key)
{
unset($this->data[$key]);
unset($this->timeToLive[$key]);
return 1;
}
/**
* @param string $key
* @return mixed
*/
public function keys($key = '*')
{
$matches = [];
foreach ($this->data as $name => $value) {
if (fnmatch($key, $name)) {
$matches[] = $name;
}
}
return $matches;
}
/**
* @param $key
* @return int
*/
public function ttl($key): int
{
if (isset($this->timeToLive[$key])) {
return max($this->timeToLive[$key] - time(), 0);
}
return self::NO_TTL;
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace IQParts\Cache\Adapter;
final class NamespaceAdapter implements CacheAdapterInterface
{
/**
* @var string
*/
private $namespace;
/**
* @var CacheAdapterInterface
*/
private $cacheAdapter;
/**
* NamespaceAdapter constructor.
* @param string $namespace
* @param CacheAdapterInterface $cacheAdapter
*/
public function __construct(string $namespace, CacheAdapterInterface $cacheAdapter)
{
$this->namespace = $namespace . ':';
$this->cacheAdapter = $cacheAdapter;
}
/**
* @param string $key
* @return mixed
*/
public function get(string $key)
{
return $this->cacheAdapter->get($this->namespace . $key);
}
/**
* @param string $key
* @param $value
* @param int|null $ttl
* @return void
*/
public function set(string $key, $value, int $ttl = null): void
{
$this->cacheAdapter->set($this->namespace . $key, $value, $ttl);
}
/**
* @param string $key
* @return void
*/
public function delete(string $key)
{
$this->cacheAdapter->delete($this->namespace . $key);
}
/**
* @param string $key
* @return array
*/
public function keys($key = '*')
{
if (substr($key, -1) !== '*') {
$key = $key . '*';
}
$keys = $this->cacheAdapter->keys($this->namespace . $key);
$allowed = [];
$len = strlen($this->namespace);
foreach ($keys as $key) {
if (substr($key, 0, $len) === $this->namespace) {
$allowed[] = substr($key, $len);
}
}
return $allowed;
}
/**
* @param $key
* @return int
*/
public function ttl($key): int
{
return $this->cacheAdapter->ttl($this->namespace . $key);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace IQParts\Cache\Adapter;
final class NullAdapter implements CacheAdapterInterface
{
/**
* @param string $key
* @return mixed
*/
public function get(string $key)
{
return null;
}
/**
* @param string $key
* @param $value
* @param int|null $ttl
* @return void
*/
public function set(string $key, $value, int $ttl = null)
{
}
/**
* @param string $key
* @return void
*/
public function delete(string $key)
{
}
/**
* @param string $key
* @return mixed
*/
public function keys($key = '')
{
return [];
}
/**
* @param $key
* @return int
*/
public function ttl($key): int
{
return 0;
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace IQParts\Cache\Adapter;
use IQParts\Cache\Serializer\SerializerInterface;
use Predis\Client;
use Predis\ClientInterface;
final class PredisAdapter implements CacheAdapterInterface
{
/**
* @var Client
*/
private $client;
/**
* @var SerializerInterface
*/
private $serializer;
/**
* @var null
*/
private $defaultTtl;
/**
* PredisAdapter constructor.
* @param ClientInterface $client
* @param SerializerInterface $serializer
* @param int $defaultTtl
*/
public function __construct(ClientInterface $client, SerializerInterface $serializer, $defaultTtl = null)
{
$this->client = $client;
$this->serializer = $serializer;
$this->defaultTtl = $defaultTtl;
}
/**
* @param string $key
* @return mixed
*/
public function get(string $key)
{
return $this->serializer->deserialize($this->client->get($key));
}
/**
* @param string $key
* @param $value
* @param int|null $ttl TTL in seconds
*/
public function set(string $key, $value, int $ttl = null)
{
if ($ttl !== null && $ttl !== 0) {
$this->client->setex($key, $ttl ?? $this->defaultTtl, $this->serializer->serialize($value));
} else {
$this->client->set($key, $this->serializer->serialize($value));
}
}
/**
* @param string $key
* @return integer
*/
public function delete(string $key): int
{
return $this->client->del([$key]);
}
/**
* @param string $key
* @return mixed
*/
public function keys($key = '*')
{
return $this->client->keys($key);
}
/**
* @param $key
* @return int
*/
public function ttl($key): int
{
return $this->client->ttl($key);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace IQParts\Cache\Serializer;
final class JsonSerializer implements SerializerInterface
{
public function serialize($input)
{
if (is_array($input)) {
return json_encode($input);
}
return $input;
}
public function deserialize($string)
{
return json_decode($string, true) ?? $string;
}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace IQParts\Cache\Serializer;
final class NoSerializer implements SerializerInterface
{
public function serialize($input)
{
return $input;
}
public function deserialize($string)
{
return $string;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace IQParts\Cache\Serializer;
interface SerializerInterface
{
public function serialize($input);
public function deserialize($string);
}