]> git.mxchange.org Git - friendica.git/blob - src/Model/Storage/Filesystem.php
Shorten "PConfiguration" to "PConfig" again, since the Wrapper is gone
[friendica.git] / src / Model / Storage / Filesystem.php
1 <?php
2 /**
3  * @file src/Model/Storage/Filesystem.php
4  * Storage backend system
5  */
6
7 namespace Friendica\Model\Storage;
8
9 use Friendica\Core\Config\IConfig;
10 use Friendica\Core\L10n;
11 use Friendica\Util\Strings;
12 use Psr\Log\LoggerInterface;
13
14 /**
15  * Filesystem based storage backend
16  *
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.
23  */
24 class Filesystem extends AbstractStorage
25 {
26         const NAME = 'Filesystem';
27
28         // Default base folder
29         const DEFAULT_BASE_FOLDER = 'storage';
30
31         /** @var IConfig */
32         private $config;
33
34         /** @var string */
35         private $basePath;
36
37         /**
38          * Filesystem constructor.
39          *
40          * @param IConfig         $config
41          * @param LoggerInterface $logger
42          * @param L10n            $l10n
43          */
44         public function __construct(IConfig $config, LoggerInterface $logger, L10n $l10n)
45         {
46                 parent::__construct($l10n, $logger);
47
48                 $this->config = $config;
49
50                 $path           = $this->config->get('storage', 'filesystem_path', self::DEFAULT_BASE_FOLDER);
51                 $this->basePath = rtrim($path, '/');
52         }
53
54         /**
55          * Split data ref and return file path
56          *
57          * @param string $reference Data reference
58          *
59          * @return string
60          */
61         private function pathForRef(string $reference)
62         {
63                 $fold1 = substr($reference, 0, 2);
64                 $fold2 = substr($reference, 2, 2);
65                 $file  = substr($reference, 4);
66
67                 return implode('/', [$this->basePath, $fold1, $fold2, $file]);
68         }
69
70
71         /**
72          * Create dirctory tree to store file, with .htaccess and index.html files
73          *
74          * @param string $file Path and filename
75          *
76          * @throws StorageException
77          */
78         private function createFoldersForFile(string $file)
79         {
80                 $path = dirname($file);
81
82                 if (!is_dir($path)) {
83                         if (!mkdir($path, 0770, true)) {
84                                 $this->logger->warning('Failed to create dir.', ['path' => $path]);
85                                 throw new StorageException($this->l10n->t('Filesystem storage failed to create "%s". Check you write permissions.', $path));
86                         }
87                 }
88
89                 while ($path !== $this->basePath) {
90                         if (!is_file($path . '/index.html')) {
91                                 file_put_contents($path . '/index.html', '');
92                         }
93                         chmod($path . '/index.html', 0660);
94                         chmod($path, 0770);
95                         $path = dirname($path);
96                 }
97                 if (!is_file($path . '/index.html')) {
98                         file_put_contents($path . '/index.html', '');
99                         chmod($path . '/index.html', 0660);
100                 }
101         }
102
103         /**
104          * @inheritDoc
105          */
106         public function get(string $reference)
107         {
108                 $file = $this->pathForRef($reference);
109                 if (!is_file($file)) {
110                         return '';
111                 }
112
113                 return file_get_contents($file);
114         }
115
116         /**
117          * @inheritDoc
118          */
119         public function put(string $data, string $reference = '')
120         {
121                 if ($reference === '') {
122                         $reference = Strings::getRandomHex();
123                 }
124                 $file = $this->pathForRef($reference);
125
126                 $this->createFoldersForFile($file);
127
128                 $result = file_put_contents($file, $data);
129
130                 // just in case the result is REALLY false, not zero or empty or anything else, throw the exception
131                 if ($result === false) {
132                         $this->logger->warning('Failed to write data.', ['file' => $file]);
133                         throw new StorageException($this->l10n->t('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
134                 }
135
136                 chmod($file, 0660);
137                 return $reference;
138         }
139
140         /**
141          * @inheritDoc
142          */
143         public function delete(string $reference)
144         {
145                 $file = $this->pathForRef($reference);
146                 // return true if file doesn't exists. we want to delete it: success with zero work!
147                 if (!is_file($file)) {
148                         return true;
149                 }
150                 return unlink($file);
151         }
152
153         /**
154          * @inheritDoc
155          */
156         public function getOptions()
157         {
158                 return [
159                         'storagepath' => [
160                                 'input',
161                                 $this->l10n->t('Storage base path'),
162                                 $this->basePath,
163                                 $this->l10n->t('Folder where uploaded files are saved. For maximum security, This should be a path outside web server folder tree')
164                         ]
165                 ];
166         }
167
168         /**
169          * @inheritDoc
170          */
171         public function saveOptions(array $data)
172         {
173                 $storagePath = $data['storagepath'] ?? '';
174                 if ($storagePath === '' || !is_dir($storagePath)) {
175                         return [
176                                 'storagepath' => $this->l10n->t('Enter a valid existing folder')
177                         ];
178                 };
179                 $this->config->set('storage', 'filesystem_path', $storagePath);
180                 $this->basePath = $storagePath;
181                 return [];
182         }
183
184         /**
185          * @inheritDoc
186          */
187         public static function getName()
188         {
189                 return self::NAME;
190         }
191 }