Initial commit

This commit is contained in:
Chris 2020-10-11 03:09:54 +02:00
commit 6ffc729cc7
3 changed files with 203 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/composer.lock
/vendor

25
composer.json Normal file
View 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
View 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);
}
}
}