287 lines
8.9 KiB
PHP
287 lines
8.9 KiB
PHP
<?php
|
|
|
|
class Filebase
|
|
{
|
|
|
|
static function hash($s = false)
|
|
{
|
|
if($s === false) $s = time().random_int(0, 2147483647);
|
|
return hash('sha256', strtolower(trim((string)$s)));
|
|
}
|
|
|
|
static function validate_path_part($value, $label, $allow_slash = false)
|
|
{
|
|
$value = (string)$value;
|
|
if($value === '')
|
|
throw new InvalidArgumentException($label.' must not be empty');
|
|
if(strpos($value, "\0") !== false || strpos($value, "\\") !== false)
|
|
throw new InvalidArgumentException($label.' contains an unsafe path character');
|
|
|
|
$segments = $allow_slash ? explode('/', $value) : array($value);
|
|
foreach($segments as $segment)
|
|
{
|
|
if($segment === '' || $segment === '.' || $segment === '..')
|
|
throw new InvalidArgumentException($label.' contains an unsafe path segment');
|
|
if(!$allow_slash && strpos($segment, '/') !== false)
|
|
throw new InvalidArgumentException($label.' must be a single path segment');
|
|
}
|
|
return($value);
|
|
}
|
|
|
|
static function make_bucket_path($p)
|
|
{
|
|
$p = self::validate_path_part($p, 'bucket', true);
|
|
if(stristr($p, '/') !== false)
|
|
{
|
|
$seg = explode('/', $p);
|
|
$p = array_shift($seg);
|
|
return(substr($p, -2).'/'.$p.'/'.implode('/', $seg));
|
|
}
|
|
return(substr($p, -2).'/'.$p);
|
|
}
|
|
|
|
static function storage_path($class, $bucket)
|
|
{
|
|
$class = self::validate_path_part($class, 'class');
|
|
return(first($GLOBALS['config']['filebase']['path'], 'data/').$class.'/'.self::make_bucket_path($bucket));
|
|
}
|
|
|
|
static function data_filename($class, $bucket, $type)
|
|
{
|
|
$type = self::validate_path_part($type, 'type');
|
|
return(self::storage_path($class, $bucket).'/'.$type.'.json');
|
|
}
|
|
|
|
static function write_file($filename, $data)
|
|
{
|
|
$directory = dirname($filename);
|
|
if (!is_dir($directory) && !@mkdir($directory, 0700, true)) return false;
|
|
$fp = @fopen($filename, "c+");
|
|
|
|
if(!$fp)
|
|
{
|
|
Log::text('<ERR>', 'file_put_contents_ex('.$filename.') could not write file');
|
|
return;
|
|
}
|
|
|
|
if (flock($fp, LOCK_EX))
|
|
{
|
|
ftruncate($fp, 0);
|
|
rewind($fp);
|
|
$written = fwrite($fp, $data);
|
|
fflush($fp);
|
|
@chmod($filename, 0600);
|
|
flock($fp, LOCK_UN);
|
|
fclose($fp);
|
|
return $written === strlen($data);
|
|
}
|
|
else
|
|
{
|
|
Log::text('<ERR>', 'file_put_contents_ex('.$filename.') could not acquire lock');
|
|
}
|
|
|
|
fclose($fp);
|
|
return false;
|
|
}
|
|
|
|
static function delete_file($file_name)
|
|
{
|
|
unlink($file_name);
|
|
}
|
|
|
|
static function read_file($file_name)
|
|
{
|
|
if(!file_exists($file_name))
|
|
return('');
|
|
$fsz = filesize($file_name);
|
|
if($fsz == 0)
|
|
return('');
|
|
|
|
$fp = fopen($file_name, "rb+");
|
|
|
|
if(!$fp)
|
|
return('');
|
|
|
|
if (flock($fp, LOCK_SH)) {
|
|
$content = fread($fp, $fsz);
|
|
flock($fp, LOCK_UN);
|
|
} else {
|
|
Log::text('<ERR>', 'read_file('.$file_name.') could not acquire lock');
|
|
}
|
|
|
|
fclose($fp);
|
|
|
|
return($content);
|
|
}
|
|
|
|
static function write_data($class, $bucket, $type, $data)
|
|
{
|
|
$storage_path = self::storage_path($class, $bucket);
|
|
if(!file_exists($storage_path)) @mkdir($storage_path, 0774, true);
|
|
if(!file_exists($storage_path)) @mkdir($storage_path, 0700, true);
|
|
$json = json_encode($data);
|
|
if ($json === false) return false;
|
|
return self::write_file(self::data_filename($class, $bucket, $type), $json);
|
|
}
|
|
|
|
static function read_data($class, $bucket, $type)
|
|
{
|
|
return(json_decode(self::read_file(self::data_filename($class, $bucket, $type)), true));
|
|
}
|
|
|
|
static function delete_data($class, $bucket, $type)
|
|
{
|
|
return(self::delete_file(self::data_filename($class, $bucket, $type)));
|
|
}
|
|
|
|
static function get_data_filename($class, $bucket, $type)
|
|
{
|
|
return(self::data_filename($class, $bucket, $type));
|
|
}
|
|
|
|
static function list_bucket($class, $bucket)
|
|
{
|
|
$items = array();
|
|
$storage_path = self::storage_path($class, $bucket);
|
|
if(!is_dir($storage_path))
|
|
return($items);
|
|
foreach(scandir($storage_path) as $name)
|
|
{
|
|
if(substr($name, 0, 1) != '_' && trim($name) != '' && $name !== '.' && $name !== '..')
|
|
$items[] = $name;
|
|
}
|
|
return($items);
|
|
}
|
|
|
|
static function search_bucket($class, $bucket, $q)
|
|
{
|
|
$items = array();
|
|
$storage_path = self::storage_path($class, $bucket);
|
|
if(!is_dir($storage_path))
|
|
return($items);
|
|
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($storage_path, FilesystemIterator::SKIP_DOTS)) as $file)
|
|
{
|
|
if(!$file->isFile())
|
|
continue;
|
|
if(substr($file->getFilename(), -5) !== '.json')
|
|
continue;
|
|
$content = @file_get_contents($file->getPathname());
|
|
if($content === false || strpos($content, $q) === false)
|
|
continue;
|
|
$name = substr($file->getPathname(), strlen($storage_path)+1, -5);
|
|
if(substr($name, 0, 1) != '_' && trim($name) != '')
|
|
$items[] = $name;
|
|
}
|
|
return($items);
|
|
}
|
|
|
|
static function delete_directory($dir)
|
|
{
|
|
if(!is_dir($dir))
|
|
return('');
|
|
foreach(scandir($dir) as $entry)
|
|
{
|
|
if($entry === '.' || $entry === '..')
|
|
continue;
|
|
$path = $dir.'/'.$entry;
|
|
if(is_dir($path) && !is_link($path))
|
|
{
|
|
$error = self::delete_directory($path);
|
|
if($error !== '') return($error);
|
|
}
|
|
else if(!@unlink($path))
|
|
{
|
|
return('could not delete '.$path);
|
|
}
|
|
}
|
|
if(!@rmdir($dir))
|
|
return('could not delete '.$dir);
|
|
return('');
|
|
}
|
|
|
|
static function delete_bucket($class, $bucket)
|
|
{
|
|
return(self::delete_directory(self::storage_path($class, $bucket)));
|
|
}
|
|
|
|
static function write_log($class, $bucket, $type, $data)
|
|
{
|
|
$storage_path = self::storage_path($class, $bucket);
|
|
if(!file_exists($storage_path)) @mkdir($storage_path, 0774, true);
|
|
WriteToFile($storage_path.'/'.self::validate_path_part($type, 'type').'.log', json_encode($data).chr(10));
|
|
}
|
|
|
|
static function read_log($class, $bucket, $type, $line_count = 8, $offset = false)
|
|
{
|
|
$storage_path = self::storage_path($class, $bucket);
|
|
return(get_json_tail($storage_path.'/'.self::validate_path_part($type, 'type').'.log', $line_count, $offset));
|
|
}
|
|
|
|
static function line_count($class, $bucket, $type)
|
|
{
|
|
$storage_path = self::storage_path($class, $bucket);
|
|
return(intval(trim(shell_exec('wc -l '.escapeshellarg($storage_path.'/'.self::validate_path_part($type, 'type').'.log')))));
|
|
}
|
|
|
|
static function get_json_tail($from_file, $line_count = 8, $offset = false)
|
|
{
|
|
if($offset > 0)
|
|
{
|
|
$lines = trim(shell_exec(
|
|
'tail -n '.escapeshellarg($offset+$line_count).' '.escapeshellarg($from_file).' | head -n '.escapeshellarg($line_count)));
|
|
}
|
|
else
|
|
{
|
|
$lines = trim(shell_exec(
|
|
'tail -n '.escapeshellarg($line_count).' '.escapeshellarg($from_file)));
|
|
}
|
|
return(json_lines($lines));
|
|
}
|
|
|
|
static function get_tail($from_file, $line_count = 8, $offset = false)
|
|
{
|
|
$line_count = intval($line_count);
|
|
if($offset > 0)
|
|
{
|
|
$lines = trim(shell_exec(
|
|
'tail -n '.escapeshellarg($offset+$line_count).' '.escapeshellarg($from_file).' | head -n '.escapeshellarg($line_count)));
|
|
}
|
|
else
|
|
{
|
|
$lines = trim(shell_exec(
|
|
'tail -n '.escapeshellarg($line_count).' '.escapeshellarg($from_file)));
|
|
}
|
|
return(explode(chr(10), $lines));
|
|
}
|
|
|
|
static function get_all_lines($from_file)
|
|
{
|
|
return(explode(chr(10), trim(file_get_contents($from_file))));
|
|
}
|
|
|
|
static function truncate_log($from_file, $lines = 128)
|
|
{
|
|
$lines = intval($lines);
|
|
$tmp = tempnam(sys_get_temp_dir(), 'trunc_');
|
|
shell_exec('tail -n '.$lines.' '.escapeshellarg($from_file).' > '.escapeshellarg($tmp).' && cp '.escapeshellarg($tmp).' '.escapeshellarg($from_file));
|
|
@unlink($tmp);
|
|
return true;
|
|
}
|
|
|
|
static function json_lines($lines)
|
|
{
|
|
if($lines == '')
|
|
{
|
|
return(array());
|
|
}
|
|
else
|
|
{
|
|
$result = array();
|
|
foreach(explode(chr(10), $lines) as $line)
|
|
$result[] = json_decode($line, true);
|
|
return($result);
|
|
}
|
|
}
|
|
|
|
}
|