1 | <?php |
||||
2 | /** |
||||
3 | * A filestore that uses disk as storage. |
||||
4 | * |
||||
5 | * @warning This should be used by a wrapper class |
||||
6 | * like {@link \ElggFile}. |
||||
7 | * |
||||
8 | * @package Elgg.Core |
||||
9 | * @subpackage FileStore.Disk |
||||
10 | */ |
||||
11 | class ElggDiskFilestore extends \ElggFilestore { |
||||
12 | /** |
||||
13 | * Directory root. |
||||
14 | */ |
||||
15 | protected $dir_root; |
||||
16 | |||||
17 | /** |
||||
18 | * Number of entries per matrix dir. |
||||
19 | * You almost certainly don't want to change this. |
||||
20 | */ |
||||
21 | const BUCKET_SIZE = 5000; |
||||
22 | |||||
23 | /** |
||||
24 | * Construct a disk filestore using the given directory root. |
||||
25 | * |
||||
26 | * @param string $directory_root Root directory, must end in "/" |
||||
27 | */ |
||||
28 | 80 | public function __construct($directory_root = "") { |
|||
29 | 80 | if ($directory_root) { |
|||
30 | 78 | $this->dir_root = $directory_root; |
|||
31 | } else { |
||||
32 | 2 | $this->dir_root = _elgg_config()->dataroot; |
|||
33 | } |
||||
34 | 80 | } |
|||
35 | |||||
36 | /** |
||||
37 | * Open a file for reading, writing, or both. |
||||
38 | * |
||||
39 | * @note All files are opened binary safe. |
||||
40 | * @note This will try to create the a directory if it doesn't exist and is opened |
||||
41 | * in write or append mode. |
||||
42 | * |
||||
43 | * @param \ElggFile $file The file to open |
||||
44 | * @param string $mode read, write, or append. |
||||
45 | * |
||||
46 | * @throws InvalidParameterException |
||||
47 | * @return resource File pointer resource |
||||
48 | */ |
||||
49 | 63 | public function open(\ElggFile $file, $mode) { |
|||
50 | 63 | $fullname = $this->getFilenameOnFilestore($file); |
|||
51 | |||||
52 | // Split into path and name |
||||
53 | 63 | $ls = strrpos($fullname, "/"); |
|||
54 | 63 | if ($ls === false) { |
|||
55 | $ls = 0; |
||||
56 | } |
||||
57 | |||||
58 | 63 | $path = substr($fullname, 0, $ls); |
|||
59 | |||||
60 | 63 | if (($mode === 'read') && (!file_exists($fullname))) { |
|||
61 | return false; |
||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||||
62 | } |
||||
63 | |||||
64 | // Try to create the dir for valid write modes |
||||
65 | 63 | if ($mode == 'write' || $mode == 'append') { |
|||
66 | try { |
||||
67 | 63 | $this->makeDirectoryRoot($path); |
|||
68 | } catch (Exception $e) { |
||||
69 | _elgg_services()->logger->warn("Couldn't create directory: $path"); |
||||
70 | return false; |
||||
0 ignored issues
–
show
|
|||||
71 | } |
||||
72 | } |
||||
73 | |||||
74 | 63 | switch ($mode) { |
|||
75 | case "read" : |
||||
76 | $mode = "rb"; |
||||
77 | break; |
||||
78 | case "write" : |
||||
79 | 63 | $mode = "w+b"; |
|||
80 | 63 | break; |
|||
81 | case "append" : |
||||
82 | $mode = "a+b"; |
||||
83 | break; |
||||
84 | default: |
||||
85 | $msg = "Unrecognized file mode '" . $mode . "'"; |
||||
86 | throw new \InvalidParameterException($msg); |
||||
87 | } |
||||
88 | |||||
89 | 63 | return fopen($fullname, $mode); |
|||
0 ignored issues
–
show
The expression
return fopen($fullname, $mode) could also return false which is incompatible with the documented return type resource . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.
Loading history...
|
|||||
90 | |||||
91 | } |
||||
92 | |||||
93 | /** |
||||
94 | * Write data to a file. |
||||
95 | * |
||||
96 | * @param resource $f File pointer resource |
||||
97 | * @param mixed $data The data to write. |
||||
98 | * |
||||
99 | * @return false|int |
||||
100 | */ |
||||
101 | 19 | public function write($f, $data) { |
|||
102 | 19 | return fwrite($f, $data); |
|||
103 | } |
||||
104 | |||||
105 | /** |
||||
106 | * Read data from a file. |
||||
107 | * |
||||
108 | * @param resource $f File pointer resource |
||||
109 | * @param int $length The number of bytes to read |
||||
110 | * @param int $offset The number of bytes to start after |
||||
111 | * |
||||
112 | * @return mixed Contents of file or false on fail. |
||||
113 | */ |
||||
114 | public function read($f, $length, $offset = 0) { |
||||
115 | if ($offset) { |
||||
116 | $this->seek($f, $offset); |
||||
117 | } |
||||
118 | |||||
119 | return fread($f, $length); |
||||
120 | } |
||||
121 | |||||
122 | /** |
||||
123 | * Close a file pointer |
||||
124 | * |
||||
125 | * @param resource $f A file pointer resource |
||||
126 | * |
||||
127 | * @return bool |
||||
128 | */ |
||||
129 | 63 | public function close($f) { |
|||
130 | 63 | return fclose($f); |
|||
131 | } |
||||
132 | |||||
133 | /** |
||||
134 | * Delete an \ElggFile file. |
||||
135 | * |
||||
136 | * @param \ElggFile $file File to delete |
||||
137 | * @param bool $follow_symlinks If true, will also delete the target file if the current file is a symlink |
||||
138 | * @return bool |
||||
139 | */ |
||||
140 | 64 | public function delete(\ElggFile $file, $follow_symlinks = true) { |
|||
141 | 64 | $filename = $this->getFilenameOnFilestore($file); |
|||
142 | 64 | if (file_exists($filename) || is_link($filename)) { |
|||
143 | 58 | if ($follow_symlinks && is_link($filename) && file_exists($filename)) { |
|||
144 | $target = readlink($filename); |
||||
145 | file_exists($target) && unlink($target); |
||||
146 | } |
||||
147 | 58 | return unlink($filename); |
|||
148 | } else { |
||||
149 | 51 | return true; |
|||
150 | } |
||||
151 | } |
||||
152 | |||||
153 | /** |
||||
154 | * Seek to the specified position. |
||||
155 | * |
||||
156 | * @param resource $f File resource |
||||
157 | * @param int $position Position in bytes |
||||
158 | * |
||||
159 | * @return int 0 for success, or -1 |
||||
160 | */ |
||||
161 | public function seek($f, $position) { |
||||
162 | return fseek($f, $position); |
||||
163 | } |
||||
164 | |||||
165 | /** |
||||
166 | * Return the current location of the internal pointer |
||||
167 | * |
||||
168 | * @param resource $f File pointer resource |
||||
169 | * |
||||
170 | * @return int|false |
||||
171 | */ |
||||
172 | public function tell($f) { |
||||
173 | return ftell($f); |
||||
174 | } |
||||
175 | |||||
176 | /** |
||||
177 | * Tests for end of file on a file pointer |
||||
178 | * |
||||
179 | * @param resource $f File pointer resource |
||||
180 | * |
||||
181 | * @return bool |
||||
182 | */ |
||||
183 | public function eof($f) { |
||||
184 | return feof($f); |
||||
185 | } |
||||
186 | |||||
187 | /** |
||||
188 | * Returns the file size of an \ElggFile file. |
||||
189 | * |
||||
190 | * @param \ElggFile $file File object |
||||
191 | * |
||||
192 | * @return int The file size |
||||
193 | */ |
||||
194 | 1 | public function getFileSize(\ElggFile $file) { |
|||
195 | 1 | return filesize($this->getFilenameOnFilestore($file)); |
|||
196 | } |
||||
197 | |||||
198 | /** |
||||
199 | * Get the filename as saved on disk for an \ElggFile object |
||||
200 | * |
||||
201 | * Returns an empty string if no filename set |
||||
202 | * |
||||
203 | * @param \ElggFile $file File object |
||||
204 | * |
||||
205 | * @return string The full path of where the file is stored |
||||
206 | * @throws InvalidParameterException |
||||
207 | */ |
||||
208 | 91 | public function getFilenameOnFilestore(\ElggFile $file) { |
|||
209 | 91 | $owner_guid = $file->getOwnerGuid(); |
|||
210 | 91 | if (!$owner_guid) { |
|||
211 | $owner_guid = _elgg_services()->session->getLoggedInUserGuid(); |
||||
212 | } |
||||
213 | |||||
214 | 91 | if (!$owner_guid) { |
|||
215 | $msg = "File " . $file->getFilename() . " (file guid:" . $file->guid . ") is missing an owner!"; |
||||
216 | throw new \InvalidParameterException($msg); |
||||
217 | } |
||||
218 | |||||
219 | 91 | $filename = $file->getFilename(); |
|||
220 | 91 | if (!$filename) { |
|||
221 | return ''; |
||||
222 | } |
||||
223 | |||||
224 | 91 | $dir = new \Elgg\EntityDirLocator($owner_guid); |
|||
225 | |||||
226 | 91 | return $this->dir_root . $dir . $file->getFilename(); |
|||
227 | } |
||||
228 | |||||
229 | /** |
||||
230 | * Returns the contents of the \ElggFile file. |
||||
231 | * |
||||
232 | * @param \ElggFile $file File object |
||||
233 | * |
||||
234 | * @return string |
||||
235 | */ |
||||
236 | 7 | public function grabFile(\ElggFile $file) { |
|||
237 | 7 | return file_get_contents($file->getFilenameOnFilestore()); |
|||
238 | } |
||||
239 | |||||
240 | /** |
||||
241 | * Tests if an \ElggFile file exists. |
||||
242 | * |
||||
243 | * @param \ElggFile $file File object |
||||
244 | * |
||||
245 | * @return bool |
||||
246 | */ |
||||
247 | 89 | public function exists(\ElggFile $file) { |
|||
248 | 89 | if (!$file->getFilename()) { |
|||
249 | 3 | return false; |
|||
250 | } |
||||
251 | 88 | return file_exists($this->getFilenameOnFilestore($file)); |
|||
252 | } |
||||
253 | |||||
254 | /** |
||||
255 | * Returns the size of all data stored under a directory in the disk store. |
||||
256 | * |
||||
257 | * @param string $prefix The prefix to check under. |
||||
258 | * @param string $container_guid The guid of the entity whose data you want to check. |
||||
259 | * |
||||
260 | * @return int|false |
||||
261 | */ |
||||
262 | public function getSize($prefix, $container_guid) { |
||||
263 | if ($container_guid) { |
||||
264 | $dir = new \Elgg\EntityDirLocator($container_guid); |
||||
0 ignored issues
–
show
$container_guid of type string is incompatible with the type integer expected by parameter $guid of Elgg\EntityDirLocator::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
265 | return get_dir_size($this->dir_root . $dir . $prefix); |
||||
266 | } else { |
||||
267 | return false; |
||||
268 | } |
||||
269 | } |
||||
270 | |||||
271 | /** |
||||
272 | * Create a directory $dirroot |
||||
273 | * |
||||
274 | * @param string $dirroot The full path of the directory to create |
||||
275 | * |
||||
276 | * @throws IOException |
||||
277 | * @return true |
||||
278 | */ |
||||
279 | 63 | protected function makeDirectoryRoot($dirroot) { |
|||
280 | 63 | if (!file_exists($dirroot)) { |
|||
281 | 52 | if (!@mkdir($dirroot, 0700, true)) { |
|||
282 | throw new \IOException("Could not make " . $dirroot); |
||||
283 | } |
||||
284 | } |
||||
285 | |||||
286 | 63 | return true; |
|||
287 | } |
||||
288 | |||||
289 | /** |
||||
290 | * Returns a list of attributes to save to the database when saving |
||||
291 | * the \ElggFile object using this file store. |
||||
292 | * |
||||
293 | * @return array |
||||
294 | */ |
||||
295 | public function getParameters() { |
||||
296 | return ["dir_root" => $this->dir_root]; |
||||
297 | } |
||||
298 | |||||
299 | /** |
||||
300 | * Sets parameters that should be saved to database. |
||||
301 | * |
||||
302 | * @param array $parameters Set parameters to save to DB for this filestore. |
||||
303 | * |
||||
304 | * @return bool |
||||
305 | */ |
||||
306 | public function setParameters(array $parameters) { |
||||
307 | if (isset($parameters['dir_root'])) { |
||||
308 | $this->dir_root = $parameters['dir_root']; |
||||
309 | return true; |
||||
310 | } |
||||
311 | |||||
312 | return false; |
||||
313 | } |
||||
314 | } |
||||
315 |