107 lines
2.1 KiB
PHP
107 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Exception;
|
|
|
|
class FileStorage
|
|
{
|
|
private string $basePath;
|
|
|
|
public function __construct(?string $basePath = null)
|
|
{
|
|
// Default base path is the 'data' directory in the project root
|
|
if ($basePath === null) {
|
|
$this->basePath = realpath(__DIR__ . '/../../data');
|
|
} else {
|
|
$this->basePath = realpath($basePath);
|
|
}
|
|
|
|
if (!$this->basePath || !is_dir($this->basePath)) {
|
|
throw new Exception("Invalid base path for FileStorage.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Safely resolves and validates a path within the base directory.
|
|
* Prevents path traversal attacks.
|
|
*/
|
|
private function resolvePath(string $path): string
|
|
{
|
|
$fullPath = $this->basePath . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
|
|
$realPath = realpath(dirname($fullPath));
|
|
|
|
if ($realPath === false || strpos($realPath, $this->basePath) !== 0) {
|
|
throw new Exception("Security Alert: Path traversal attempt detected or invalid path: " . $path);
|
|
}
|
|
|
|
return $fullPath;
|
|
}
|
|
|
|
/**
|
|
* Saves an array as a JSON file.
|
|
*/
|
|
public function put(string $path, array $data): bool
|
|
{
|
|
$fullPath = $this->resolvePath($path);
|
|
|
|
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
|
|
|
|
if (file_put_contents($fullPath, $json) === false) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reads a JSON file and returns it as an associative array.
|
|
*/
|
|
public function get(string $path): ?array
|
|
{
|
|
$fullPath = $this->resolvePath($path);
|
|
|
|
if (!file_exists($fullPath)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$content = file_get_contents($fullPath);
|
|
if ($content === false) {
|
|
return null;
|
|
}
|
|
|
|
return json_decode($content, true, 512, JSON_THROW_ON_ERROR);
|
|
} catch (Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if a file exists.
|
|
*/
|
|
public function exists(string $path): bool
|
|
{
|
|
try {
|
|
$fullPath = $this->resolvePath($path);
|
|
return file_exists($fullPath);
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a file.
|
|
*/
|
|
public function delete(string $path): bool
|
|
{
|
|
$fullPath = $this->resolvePath($path);
|
|
|
|
if (file_exists($fullPath)) {
|
|
return unlink($fullPath);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|