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

engine/classes/ElggDiskFilestore.php (1 issue)

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;
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 Brett Profitt
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);
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);
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 ([email protected]($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