]> git.mxchange.org Git - friendica.git/blob - src/Core/Storage/Type/Filesystem.php
Replace own VoidLogger with PSR-Standard NullLogger()
[friendica.git] / src / Core / Storage / Type / Filesystem.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Core\Storage\Type;
23
24 use Exception;
25 use Friendica\Core\Storage\Exception\ReferenceStorageException;
26 use Friendica\Core\Storage\Exception\StorageException;
27 use Friendica\Core\Storage\Capability\ICanWriteToStorage;
28 use Friendica\Util\Strings;
29
30 /**
31  * Filesystem based storage backend
32  *
33  * This class manage data on filesystem.
34  * Base folder for storage is set in storage.filesystem_path.
35  * Best would be for storage folder to be outside webserver folder, we are using a
36  * folder relative to code tree root as default to ease things for users in shared hostings.
37  * Each new resource gets a value as reference and is saved in a
38  * folder tree stucture created from that value.
39  */
40 class Filesystem implements ICanWriteToStorage
41 {
42         const NAME = 'Filesystem';
43
44         /** @var string */
45         private $basePath;
46
47         /**
48          * Filesystem constructor.
49          *
50          * @param string $filesystemPath
51          *
52          * @throws StorageException in case the path doesn't exist or isn't writeable
53          */
54         public function __construct(string $filesystemPath = FilesystemConfig::DEFAULT_BASE_FOLDER)
55         {
56                 $path           = $filesystemPath;
57                 $this->basePath = rtrim($path, '/');
58
59                 if (!is_dir($this->basePath) || !is_writable($this->basePath)) {
60                         throw new StorageException(sprintf('Path "%s" does not exist or is not writeable.', $this->basePath));
61                 }
62         }
63
64         /**
65          * Split data ref and return file path
66          *
67          * @param string $reference Data reference
68          *
69          * @return string
70          */
71         private function pathForRef(string $reference): string
72         {
73                 $fold1 = substr($reference, 0, 2);
74                 $fold2 = substr($reference, 2, 2);
75                 $file  = substr($reference, 4);
76
77                 return implode('/', [$this->basePath, $fold1, $fold2, $file]);
78         }
79
80
81         /**
82          * Create directory tree to store file, with .htaccess and index.html files
83          *
84          * @param string $file Path and filename
85          *
86          * @throws StorageException
87          */
88         private function createFoldersForFile(string $file)
89         {
90                 $path = dirname($file);
91
92                 if (!is_dir($path)) {
93                         if (!mkdir($path, 0770, true)) {
94                                 throw new StorageException(sprintf('Filesystem storage failed to create "%s". Check you write permissions.', $path));
95                         }
96                 }
97
98                 while ($path !== $this->basePath) {
99                         if (!is_file($path . '/index.html')) {
100                                 file_put_contents($path . '/index.html', '');
101                         }
102                         chmod($path . '/index.html', 0660);
103                         chmod($path, 0770);
104                         $path = dirname($path);
105                 }
106                 if (!is_file($path . '/index.html')) {
107                         file_put_contents($path . '/index.html', '');
108                         chmod($path . '/index.html', 0660);
109                 }
110         }
111
112         /**
113          * @inheritDoc
114          */
115         public function get(string $reference): string
116         {
117                 $file = $this->pathForRef($reference);
118                 if (!is_file($file)) {
119                         throw new ReferenceStorageException(sprintf('Filesystem storage failed to get the file %s, The file is invalid', $reference));
120                 }
121
122                 $result = file_get_contents($file);
123
124                 if ($result === false) {
125                         throw new StorageException(sprintf('Filesystem storage failed to get data to "%s". Check your write permissions', $file));
126                 }
127
128                 return $result;
129         }
130
131         /**
132          * @inheritDoc
133          */
134         public function put(string $data, string $reference = ''): string
135         {
136                 if ($reference === '') {
137                         try {
138                                 $reference = Strings::getRandomHex();
139                         } catch (Exception $exception) {
140                                 throw new StorageException('Filesystem storage failed to generate a random hex', $exception->getCode(), $exception);
141                         }
142                 }
143                 $file = $this->pathForRef($reference);
144
145                 $this->createFoldersForFile($file);
146
147                 $result = file_put_contents($file, $data);
148
149                 // just in case the result is REALLY false, not zero or empty or anything else, throw the exception
150                 if ($result === false) {
151                         throw new StorageException(sprintf('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
152                 }
153
154                 chmod($file, 0660);
155                 return $reference;
156         }
157
158         /**
159          * @inheritDoc
160          */
161         public function delete(string $reference)
162         {
163                 $file = $this->pathForRef($reference);
164                 if (!is_file($file)) {
165                         throw new ReferenceStorageException(sprintf('File with reference "%s" doesn\'t exist', $reference));
166                 }
167
168                 if (!unlink($file)) {
169                         throw new StorageException(sprintf('Cannot delete with file with reference "%s"', $reference));
170                 }
171         }
172
173         /**
174          * @inheritDoc
175          */
176         public static function getName(): string
177         {
178                 return self::NAME;
179         }
180
181         public function __toString(): string
182         {
183                 return self::getName();
184         }
185 }