Initial commit
This commit is contained in:
commit
6ffc729cc7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/composer.lock
|
||||
/vendor
|
25
composer.json
Normal file
25
composer.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "noccylabs/local-cache",
|
||||
"description": "A local system-wide always available cache",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christopher Vagnetoft",
|
||||
"email": "cvagnetoft@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"psr/log": "^1.1",
|
||||
"psr/cache": "^1.0",
|
||||
"psr/simple-cache": "^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"NoccyLabs\\LocalCache\\": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.4"
|
||||
}
|
||||
}
|
176
src/LocalCache.php
Normal file
176
src/LocalCache.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\LocalCache;
|
||||
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\CacheException;
|
||||
|
||||
class LocalCache implements CacheInterface
|
||||
{
|
||||
|
||||
private $cacheDir;
|
||||
|
||||
private $poolName;
|
||||
|
||||
private $defaultTtl = 3600;
|
||||
|
||||
public function __construct(string $poolName)
|
||||
{
|
||||
$this->poolName = preg_replace("/[^a-zA-Z0-9_\\-]/", "_", $poolName);
|
||||
$this->cacheDir = $this->findCachePath();
|
||||
}
|
||||
|
||||
protected function findCachePath(): string
|
||||
{
|
||||
$candidates = [
|
||||
'/var/cache',
|
||||
getenv("HOME").'/.cache',
|
||||
];
|
||||
while ($candidate = array_shift($candidates)) {
|
||||
if (!is_writable($candidate)) {
|
||||
continue;
|
||||
}
|
||||
$path = $candidate . DIRECTORY_SEPARATOR . 'php-localcache';
|
||||
/*
|
||||
if (!is_dir($path)) {
|
||||
@mkdir($path);
|
||||
if (!is_dir($path)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*/
|
||||
return $path;
|
||||
}
|
||||
throw new CacheException("Couldn't find a usable cache directory");
|
||||
}
|
||||
|
||||
protected function hashKey(string $key)
|
||||
{
|
||||
$hash = hash("sha256", $key);
|
||||
return $hash;
|
||||
}
|
||||
|
||||
protected function getKeyPath(string $key)
|
||||
{
|
||||
$hash = $this->hashKey($key);
|
||||
// $path = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2, 2) . DIRECTORY_SEPARATOR . $hash;
|
||||
$full = $this->cacheDir . DIRECTORY_SEPARATOR . $this->poolName . DIRECTORY_SEPARATOR . $hash; // $path;
|
||||
return $full;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (!$this->has($key)) {
|
||||
return $default;
|
||||
}
|
||||
$path = $this->getKeyPath($key);
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
$info = [
|
||||
'key' => $key,
|
||||
'ttl' => $ttl ?? $this->defaultTtl
|
||||
];
|
||||
$info['expires'] = time() + $info['ttl'];
|
||||
$path = $this->getKeyPath($key);
|
||||
if (!is_dir(dirname($path))) {
|
||||
mkdir(dirname($path), 0777, true);
|
||||
}
|
||||
file_put_contents($path, $value);
|
||||
file_put_contents($path.".info", serialize($info));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
if (!$this->has($key)) {
|
||||
return;
|
||||
}
|
||||
$path = $this->getKeyPath($key);
|
||||
@unlink($path);
|
||||
@unlink($path.".info");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
$path = $this->getKeyPath($key);
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$info = unserialize(file_get_contents($path.".info"));
|
||||
if ($info['expires'] < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$items = glob($this->cacheDir.DIRECTORY_SEPARATOR.$this->poolName.DIRECTORY_SEPARATOR."*.info");
|
||||
foreach ($items as $item) {
|
||||
unlink(substr($item, 0, -5));
|
||||
unlink($item);
|
||||
}
|
||||
@rmdir($this->cacheDir . DIRECTORY_SEPARATOR . $this->poolName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
if (!is_iterable($keys)) {
|
||||
throw new InvalidArgumentException("Argument not iterable");
|
||||
}
|
||||
$ret = [];
|
||||
foreach ($keys as $key) {
|
||||
$ret[$key] = $this->get($key, $default);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if (!is_iterable($values)) {
|
||||
throw new InvalidArgumentException("Argument not iterable");
|
||||
}
|
||||
foreach ($values as $key=>$value) {
|
||||
$this->set($key, $value, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
if (!is_iterable($keys)) {
|
||||
throw new InvalidArgumentException("Argument not iterable");
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
$this->delete($key);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user