3 * @file src/Model/Storage/Filesystem.php
4 * @brief Storage backend system
7 namespace Friendica\Model\Storage;
9 use Friendica\Core\Config;
10 use Friendica\Core\L10n;
11 use Friendica\Core\Logger;
12 use Friendica\Util\Strings;
15 * @brief Filesystem based storage backend
17 * This class manage data on filesystem.
18 * Base folder for storage is set in storage.filesystem_path.
19 * Best would be for storage folder to be outside webserver folder, we are using a
20 * folder relative to code tree root as default to ease things for users in shared hostings.
21 * Each new resource gets a value as reference and is saved in a
22 * folder tree stucture created from that value.
24 class Filesystem implements IStorage
26 // Default base folder
27 const DEFAULT_BASE_FOLDER = 'storage';
29 private static function getBasePath()
31 $path = Config::get('storage', 'filesystem_path', self::DEFAULT_BASE_FOLDER);
32 return rtrim($path, '/');
36 * @brief Split data ref and return file path
37 * @param string $ref Data reference
40 private static function pathForRef($ref)
42 $base = self::getBasePath();
43 $fold1 = substr($ref, 0, 2);
44 $fold2 = substr($ref, 2, 2);
45 $file = substr($ref, 4);
47 return implode('/', [$base, $fold1, $fold2, $file]);
52 * @brief Create dirctory tree to store file, with .htaccess and index.html files
53 * @param string $file Path and filename
54 * @throws StorageException
56 private static function createFoldersForFile($file)
58 $path = dirname($file);
61 if (!mkdir($path, 0770, true)) {
62 Logger::log('Failed to create dirs ' . $path);
63 throw new StorageException(L10n::t('Filesystem storage failed to create "%s". Check you write permissions.', $path));
67 $base = self::getBasePath();
69 while ($path !== $base) {
70 if (!is_file($path . '/index.html')) {
71 file_put_contents($path . '/index.html', '');
73 chmod($path . '/index.html', 0660);
75 $path = dirname($path);
77 if (!is_file($path . '/index.html')) {
78 file_put_contents($path . '/index.html', '');
79 chmod($path . '/index.html', 0660);
83 public static function get($ref)
85 $file = self::pathForRef($ref);
86 if (!is_file($file)) {
90 return file_get_contents($file);
93 public static function put($data, $ref = '')
96 $ref = Strings::getRandomHex();
98 $file = self::pathForRef($ref);
100 self::createFoldersForFile($file);
102 $r = file_put_contents($file, $data);
104 Logger::log('Failed to write data to ' . $file);
105 throw new StorageException(L10n::t('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
111 public static function delete($ref)
113 $file = self::pathForRef($ref);
114 // return true if file doesn't exists. we want to delete it: success with zero work!
115 if (!is_file($file)) {
118 return unlink($file);
121 public static function getOptions()
126 L10n::t('Storage base path'),
128 L10n::t('Folder where uploaded files are saved. For maximum security, This should be a path outside web server folder tree')
133 public static function saveOptions($data)
135 $storagepath = defaults($data, 'storagepath', '');
136 if ($storagepath === '' || !is_dir($storagepath)) {
138 'storagepath' => L10n::t('Enter a valid existing folder')
141 Config::set('storage', 'filesystem_path', $storagepath);