]> git.mxchange.org Git - friendica.git/blob - src/Model/Storage/Filesystem.php
Fix overly strict return value for revokeFollow methods
[friendica.git] / src / Model / Storage / 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\Model\Storage;
23
24 use Exception;
25 use Friendica\Util\Strings;
26
27 /**
28  * Filesystem based storage backend
29  *
30  * This class manage data on filesystem.
31  * Base folder for storage is set in storage.filesystem_path.
32  * Best would be for storage folder to be outside webserver folder, we are using a
33  * folder relative to code tree root as default to ease things for users in shared hostings.
34  * Each new resource gets a value as reference and is saved in a
35  * folder tree stucture created from that value.
36  */
37 class Filesystem implements IWritableStorage
38 {
39         const NAME = 'Filesystem';
40
41         /** @var string */
42         private $basePath;
43
44         /**
45          * Filesystem constructor.
46          *
47          * @param string $filesystemPath
48          *
49          * @throws StorageException in case the path doesn't exist or isn't writeable
50          */
51         public function __construct(string $filesystemPath = FilesystemConfig::DEFAULT_BASE_FOLDER)
52         {
53                 $path           = $filesystemPath;
54                 $this->basePath = rtrim($path, '/');
55
56                 if (!is_dir($this->basePath) || !is_writable($this->basePath)) {
57                         throw new StorageException(sprintf('Path "%s" does not exist or is not writeable.', $this->basePath));
58                 }
59         }
60
61         /**
62          * Split data ref and return file path
63          *
64          * @param string $reference Data reference
65          *
66          * @return string
67          */
68         private function pathForRef(string $reference): string
69         {
70                 $fold1 = substr($reference, 0, 2);
71                 $fold2 = substr($reference, 2, 2);
72                 $file  = substr($reference, 4);
73
74                 return implode('/', [$this->basePath, $fold1, $fold2, $file]);
75         }
76
77
78         /**
79          * Create directory tree to store file, with .htaccess and index.html files
80          *
81          * @param string $file Path and filename
82          *
83          * @throws StorageException
84          */
85         private function createFoldersForFile(string $file)
86         {
87                 $path = dirname($file);
88
89                 if (!is_dir($path)) {
90                         if (!mkdir($path, 0770, true)) {
91                                 throw new StorageException(sprintf('Filesystem storage failed to create "%s". Check you write permissions.', $path));
92                         }
93                 }
94
95                 while ($path !== $this->basePath) {
96                         if (!is_file($path . '/index.html')) {
97                                 file_put_contents($path . '/index.html', '');
98                         }
99                         chmod($path . '/index.html', 0660);
100                         chmod($path, 0770);
101                         $path = dirname($path);
102                 }
103                 if (!is_file($path . '/index.html')) {
104                         file_put_contents($path . '/index.html', '');
105                         chmod($path . '/index.html', 0660);
106                 }
107         }
108
109         /**
110          * @inheritDoc
111          */
112         public function get(string $reference): string
113         {
114                 $file = $this->pathForRef($reference);
115                 if (!is_file($file)) {
116                         throw new ReferenceStorageException(sprintf('Filesystem storage failed to get the file %s, The file is invalid', $reference));
117                 }
118
119                 $result = file_get_contents($file);
120
121                 if ($result === false) {
122                         throw new StorageException(sprintf('Filesystem storage failed to get data to "%s". Check your write permissions', $file));
123                 }
124
125                 return $result;
126         }
127
128         /**
129          * @inheritDoc
130          */
131         public function put(string $data, string $reference = ''): string
132         {
133                 if ($reference === '') {
134                         try {
135                                 $reference = Strings::getRandomHex();
136                         } catch (Exception $exception) {
137                                 throw new StorageException('Filesystem storage failed to generate a random hex', $exception->getCode(), $exception);
138                         }
139                 }
140                 $file = $this->pathForRef($reference);
141
142                 $this->createFoldersForFile($file);
143
144                 $result = file_put_contents($file, $data);
145
146                 // just in case the result is REALLY false, not zero or empty or anything else, throw the exception
147                 if ($result === false) {
148                         throw new StorageException(sprintf('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
149                 }
150
151                 chmod($file, 0660);
152                 return $reference;
153         }
154
155         /**
156          * @inheritDoc
157          */
158         public function delete(string $reference)
159         {
160                 $file = $this->pathForRef($reference);
161                 if (!is_file($file)) {
162                         throw new ReferenceStorageException(sprintf('File with reference "%s" doesn\'t exist', $reference));
163                 }
164
165                 if (!unlink($file)) {
166                         throw new StorageException(sprintf('Cannot delete with file with reference "%s"', $reference));
167                 }
168         }
169
170         /**
171          * @inheritDoc
172          */
173         public static function getName(): string
174         {
175                 return self::NAME;
176         }
177
178         public function __toString()
179         {
180                 return self::getName();
181         }
182 }