2022-11-23 18:18:40 +00:00
|
|
|
<?php
|
|
|
|
class Cache_Local implements Cache_Adapter {
|
|
|
|
private string $dir;
|
|
|
|
|
2022-11-23 19:09:04 +00:00
|
|
|
public function get_mtime(string $filename) {
|
|
|
|
return filemtime($this->get_full_path($filename));
|
|
|
|
}
|
|
|
|
|
2022-11-23 18:18:40 +00:00
|
|
|
public function set_dir(string $dir) : void {
|
|
|
|
$this->dir = Config::get(Config::CACHE_DIR) . "/" . basename(clean($dir));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get_dir(): string {
|
|
|
|
return $this->dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function make_dir(): bool {
|
|
|
|
if (!is_dir($this->dir)) {
|
|
|
|
return mkdir($this->dir);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function is_writable(?string $filename = null): bool {
|
|
|
|
if ($filename) {
|
|
|
|
if (file_exists($this->get_full_path($filename)))
|
|
|
|
return is_writable($this->get_full_path($filename));
|
|
|
|
else
|
|
|
|
return is_writable($this->dir);
|
|
|
|
} else {
|
|
|
|
return is_writable($this->dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function exists(string $filename): bool {
|
|
|
|
return file_exists($this->get_full_path($filename));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return int|false -1 if the file doesn't exist, false if an error occurred, size in bytes otherwise
|
|
|
|
*/
|
|
|
|
public function get_size(string $filename) {
|
|
|
|
if ($this->exists($filename))
|
|
|
|
return filesize($this->get_full_path($filename));
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get_full_path(string $filename): string {
|
|
|
|
return $this->dir . "/" . basename(clean($filename));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get(string $filename): ?string {
|
|
|
|
if ($this->exists($filename))
|
|
|
|
return file_get_contents($this->get_full_path($filename));
|
|
|
|
else
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param mixed $data
|
|
|
|
*
|
|
|
|
* @return int|false Bytes written or false if an error occurred.
|
|
|
|
*/
|
|
|
|
public function put(string $filename, $data) {
|
|
|
|
return file_put_contents($this->get_full_path($filename), $data);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function touch(string $filename): bool {
|
|
|
|
return touch($this->get_full_path($filename));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return false|null|string false if detection failed, null if the file doesn't exist, string mime content type otherwise
|
|
|
|
*/
|
|
|
|
public function get_mime_type(string $filename) {
|
|
|
|
if ($this->exists($filename))
|
|
|
|
return mime_content_type($this->get_full_path($filename));
|
|
|
|
else
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return bool|int false if the file doesn't exist (or unreadable) or isn't audio/video, true if a plugin handled, otherwise int of bytes sent
|
|
|
|
*/
|
|
|
|
public function send(string $filename) {
|
|
|
|
return $this->send_local_file($this->get_full_path($filename));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function expire_all(): void {
|
|
|
|
$dirs = array_filter(glob(Config::get(Config::CACHE_DIR) . "/*"), "is_dir");
|
|
|
|
|
|
|
|
foreach ($dirs as $cache_dir) {
|
|
|
|
$num_deleted = 0;
|
|
|
|
|
|
|
|
if (is_writable($cache_dir) && !file_exists("$cache_dir/.no-auto-expiry")) {
|
|
|
|
$files = glob("$cache_dir/*");
|
|
|
|
|
|
|
|
if ($files) {
|
|
|
|
foreach ($files as $file) {
|
|
|
|
if (time() - filemtime($file) > 86400*Config::get(Config::CACHE_MAX_DAYS)) {
|
|
|
|
unlink($file);
|
|
|
|
|
|
|
|
++$num_deleted;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Debug::log("Expired $cache_dir: removed $num_deleted files.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* this is essentially a wrapper for readfile() which allows plugins to hook
|
|
|
|
* output with httpd-specific "fast" implementation i.e. X-Sendfile or whatever else
|
|
|
|
*
|
|
|
|
* hook function should return true if request was handled (or at least attempted to)
|
|
|
|
*
|
|
|
|
* note that this can be called without user context so the plugin to handle this
|
|
|
|
* should be loaded systemwide in config.php
|
|
|
|
*
|
|
|
|
* @return bool|int false if the file doesn't exist (or unreadable) or isn't audio/video, true if a plugin handled, otherwise int of bytes sent
|
|
|
|
*/
|
|
|
|
function send_local_file(string $filename) {
|
|
|
|
if (file_exists($filename)) {
|
|
|
|
|
|
|
|
if (is_writable($filename)) touch($filename);
|
|
|
|
|
|
|
|
$tmppluginhost = new PluginHost();
|
|
|
|
|
|
|
|
$tmppluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_SYSTEM);
|
|
|
|
//$tmppluginhost->load_data();
|
|
|
|
|
|
|
|
if ($tmppluginhost->run_hooks_until(PluginHost::HOOK_SEND_LOCAL_FILE, true, $filename))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
$stamp = gmdate("D, d M Y H:i:s", (int)filemtime($filename)) . " GMT";
|
|
|
|
header("Last-Modified: $stamp", true);
|
|
|
|
|
|
|
|
return readfile($filename);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|