Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/ElggDiskFilestore.php (4 issues)

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
The expression return false returns the type false which is incompatible with the documented return type resource.
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
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type resource.
Loading history...
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
Bug Best Practice introduced by
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 ignore-type  annotation

264
			$dir = new \Elgg\EntityDirLocator(/** @scrutinizer ignore-type */ $container_guid);
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