php-local-cache/src/LocalCache.php

182 lines
4.4 KiB
PHP

<?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 . $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);
*/
$path = $this->cacheDir . DIRECTORY_SEPARATOR . $this->poolName;
exec("rm -rf ".escapeshellarg($path));
}
/**
* {@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);
}
}
}