Commit | Line | Data |
---|---|---|
62704f33 SH |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * The library file for the file cache store. | |
19 | * | |
20 | * This file is part of the file cache store, it contains the API for interacting with an instance of the store. | |
21 | * This is used as a default cache store within the Cache API. It should never be deleted. | |
22 | * | |
6fec1820 | 23 | * @package cachestore_file |
62704f33 SH |
24 | * @category cache |
25 | * @copyright 2012 Sam Hemelryk | |
26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
27 | */ | |
28 | ||
29 | /** | |
30 | * The file store class. | |
31 | * | |
32 | * Configuration options | |
33 | * path: string: path to the cache directory, if left empty one will be created in the cache directory | |
34 | * autocreate: true, false | |
35 | * prescan: true, false | |
36 | * | |
37 | * @copyright 2012 Sam Hemelryk | |
38 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
39 | */ | |
75cde6b9 | 40 | class cachestore_file extends cache_store implements cache_is_key_aware { |
62704f33 SH |
41 | |
42 | /** | |
43 | * The name of the store. | |
44 | * @var string | |
45 | */ | |
46 | protected $name; | |
47 | ||
48 | /** | |
49 | * The path to use for the file storage. | |
50 | * @var string | |
51 | */ | |
52 | protected $path = null; | |
53 | ||
54 | /** | |
55 | * Set to true when a prescan has been performed. | |
56 | * @var bool | |
57 | */ | |
58 | protected $prescan = false; | |
59 | ||
08aaa637 SH |
60 | /** |
61 | * Set to true if we should store files within a single directory. | |
62 | * By default we use a nested structure in order to reduce the chance of conflicts and avoid any file system | |
63 | * limitations such as maximum files per directory. | |
64 | * @var bool | |
65 | */ | |
66 | protected $singledirectory = false; | |
67 | ||
62704f33 SH |
68 | /** |
69 | * Set to true when the path should be automatically created if it does not yet exist. | |
70 | * @var bool | |
71 | */ | |
72 | protected $autocreate = false; | |
73 | ||
74 | /** | |
75 | * Set to true if a custom path is being used. | |
76 | * @var bool | |
77 | */ | |
78 | protected $custompath = false; | |
79 | ||
80 | /** | |
81 | * An array of keys we are sure about presently. | |
82 | * @var array | |
83 | */ | |
84 | protected $keys = array(); | |
85 | ||
86 | /** | |
87 | * True when the store is ready to be initialised. | |
88 | * @var bool | |
89 | */ | |
90 | protected $isready = false; | |
91 | ||
62704f33 SH |
92 | /** |
93 | * The cache definition this instance has been initialised with. | |
94 | * @var cache_definition | |
95 | */ | |
96 | protected $definition; | |
97 | ||
98 | /** | |
99 | * Constructs the store instance. | |
100 | * | |
101 | * Noting that this function is not an initialisation. It is used to prepare the store for use. | |
102 | * The store will be initialised when required and will be provided with a cache_definition at that time. | |
103 | * | |
104 | * @param string $name | |
105 | * @param array $configuration | |
106 | */ | |
107 | public function __construct($name, array $configuration = array()) { | |
108 | $this->name = $name; | |
109 | if (array_key_exists('path', $configuration) && $configuration['path'] !== '') { | |
110 | $this->custompath = true; | |
111 | $this->autocreate = !empty($configuration['autocreate']); | |
112 | $path = (string)$configuration['path']; | |
113 | if (!is_dir($path)) { | |
114 | if ($this->autocreate) { | |
115 | if (!make_writable_directory($path, false)) { | |
116 | $path = false; | |
117 | debugging('Error trying to autocreate file store path. '.$path, DEBUG_DEVELOPER); | |
118 | } | |
119 | } else { | |
120 | $path = false; | |
121 | debugging('The given file cache store path does not exist. '.$path, DEBUG_DEVELOPER); | |
122 | } | |
123 | } | |
124 | if ($path !== false && !is_writable($path)) { | |
125 | $path = false; | |
126 | debugging('The given file cache store path is not writable. '.$path, DEBUG_DEVELOPER); | |
127 | } | |
128 | } else { | |
6fec1820 | 129 | $path = make_cache_directory('cachestore_file/'.preg_replace('#[^a-zA-Z0-9\.\-_]+#', '', $name)); |
62704f33 SH |
130 | } |
131 | $this->isready = $path !== false; | |
132 | $this->path = $path; | |
08aaa637 SH |
133 | // Check if we should prescan the directory. |
134 | if (array_key_exists('prescan', $configuration)) { | |
702651c7 | 135 | $this->prescan = (bool)$configuration['prescan']; |
08aaa637 SH |
136 | } else { |
137 | // Default is no, we should not prescan. | |
702651c7 | 138 | $this->prescan = false; |
08aaa637 SH |
139 | } |
140 | // Check if we should be storing in a single directory. | |
141 | if (array_key_exists('singledirectory', $configuration)) { | |
702651c7 | 142 | $this->singledirectory = (bool)$configuration['singledirectory']; |
08aaa637 SH |
143 | } else { |
144 | // Default: No, we will use multiple directories. | |
702651c7 | 145 | $this->singledirectory = false; |
08aaa637 | 146 | } |
62704f33 SH |
147 | } |
148 | ||
149 | /** | |
150 | * Returns true if this store instance is ready to be used. | |
151 | * @return bool | |
152 | */ | |
153 | public function is_ready() { | |
154 | return ($this->path !== null); | |
155 | } | |
156 | ||
157 | /** | |
158 | * Returns true once this instance has been initialised. | |
159 | * | |
160 | * @return bool | |
161 | */ | |
162 | public function is_initialised() { | |
163 | return true; | |
164 | } | |
165 | ||
166 | /** | |
167 | * Returns the supported features as a combined int. | |
168 | * | |
169 | * @param array $configuration | |
170 | * @return int | |
171 | */ | |
172 | public static function get_supported_features(array $configuration = array()) { | |
173 | $supported = self::SUPPORTS_DATA_GUARANTEE + | |
174 | self::SUPPORTS_NATIVE_TTL; | |
175 | return $supported; | |
176 | } | |
177 | ||
178 | /** | |
179 | * Returns the supported modes as a combined int. | |
180 | * | |
181 | * @param array $configuration | |
182 | * @return int | |
183 | */ | |
184 | public static function get_supported_modes(array $configuration = array()) { | |
185 | return self::MODE_APPLICATION + self::MODE_SESSION; | |
186 | } | |
187 | ||
188 | /** | |
189 | * Returns true if the store requirements are met. | |
190 | * | |
191 | * @return bool | |
192 | */ | |
193 | public static function are_requirements_met() { | |
194 | return true; | |
195 | } | |
196 | ||
197 | /** | |
198 | * Returns true if the given mode is supported by this store. | |
199 | * | |
200 | * @param int $mode One of cache_store::MODE_* | |
201 | * @return bool | |
202 | */ | |
203 | public static function is_supported_mode($mode) { | |
204 | return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION); | |
205 | } | |
206 | ||
62704f33 SH |
207 | /** |
208 | * Initialises the cache. | |
209 | * | |
210 | * Once this has been done the cache is all set to be used. | |
211 | * | |
212 | * @param cache_definition $definition | |
213 | */ | |
214 | public function initialise(cache_definition $definition) { | |
215 | $this->definition = $definition; | |
216 | $hash = preg_replace('#[^a-zA-Z0-9]+#', '_', $this->definition->get_id()); | |
217 | $this->path .= '/'.$hash; | |
218 | make_writable_directory($this->path); | |
219 | if ($this->prescan && $definition->get_mode() !== self::MODE_REQUEST) { | |
220 | $this->prescan = false; | |
221 | } | |
222 | if ($this->prescan) { | |
08aaa637 SH |
223 | $this->prescan_keys(); |
224 | } | |
225 | } | |
226 | ||
227 | /** | |
228 | * Pre-scan the cache to see which keys are present. | |
229 | */ | |
230 | protected function prescan_keys() { | |
758dbdf8 SH |
231 | $files = glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT); |
232 | if (is_array($files)) { | |
233 | foreach ($files as $filename) { | |
234 | $this->keys[basename($filename)] = filemtime($filename); | |
235 | } | |
08aaa637 SH |
236 | } |
237 | } | |
238 | ||
239 | /** | |
240 | * Gets a pattern suitable for use with glob to find all keys in the cache. | |
241 | * @return string The pattern. | |
242 | */ | |
243 | protected function glob_keys_pattern() { | |
244 | if ($this->singledirectory) { | |
245 | return $this->path . '/*.cache'; | |
246 | } else { | |
702651c7 | 247 | return $this->path . '/*/*.cache'; |
08aaa637 SH |
248 | } |
249 | } | |
250 | ||
251 | /** | |
252 | * Returns the file path to use for the given key. | |
253 | * | |
254 | * @param string $key The key to generate a file path for. | |
255 | * @param bool $create If set to the true the directory structure the key requires will be created. | |
256 | * @return string The full path to the file that stores a particular cache key. | |
257 | */ | |
258 | protected function file_path_for_key($key, $create = false) { | |
259 | if ($this->singledirectory) { | |
260 | // Its a single directory, easy, just the store instances path + the file name. | |
261 | return $this->path . '/' . $key . '.cache'; | |
262 | } else { | |
702651c7 SH |
263 | // We are using a single subdirectory to achieve 1 level. |
264 | $subdir = substr($key, 0, 3); | |
265 | $dir = $this->path . '/' . $subdir; | |
08aaa637 SH |
266 | if ($create) { |
267 | // Create the directory. This function does it recursivily! | |
268 | make_writable_directory($dir); | |
62704f33 | 269 | } |
08aaa637 | 270 | return $dir . '/' . $key . '.cache'; |
62704f33 SH |
271 | } |
272 | } | |
273 | ||
274 | /** | |
275 | * Retrieves an item from the cache store given its key. | |
276 | * | |
277 | * @param string $key The key to retrieve | |
278 | * @return mixed The data that was associated with the key, or false if the key did not exist. | |
279 | */ | |
280 | public function get($key) { | |
281 | $filename = $key.'.cache'; | |
08aaa637 | 282 | $file = $this->file_path_for_key($key); |
62704f33 SH |
283 | $ttl = $this->definition->get_ttl(); |
284 | if ($ttl) { | |
285 | $maxtime = cache::now() - $ttl; | |
286 | } | |
287 | $readfile = false; | |
288 | if ($this->prescan && array_key_exists($key, $this->keys)) { | |
289 | if (!$ttl || $this->keys[$filename] >= $maxtime && file_exists($file)) { | |
290 | $readfile = true; | |
291 | } else { | |
292 | $this->delete($key); | |
293 | } | |
294 | } else if (file_exists($file) && (!$ttl || filemtime($file) >= $maxtime)) { | |
295 | $readfile = true; | |
296 | } | |
297 | if (!$readfile) { | |
298 | return false; | |
299 | } | |
170f821b | 300 | // Check the filesize first, likely not needed but important none the less. |
62704f33 SH |
301 | $filesize = filesize($file); |
302 | if (!$filesize) { | |
303 | return false; | |
304 | } | |
305 | // Open ensuring the file for writing, truncating it and setting the pointer to the start. | |
306 | if (!$handle = fopen($file, 'rb')) { | |
307 | return false; | |
308 | } | |
309 | // Lock it up! | |
170f821b | 310 | // We don't care if this succeeds or not, on some systems it will, on some it won't, meah either way. |
62704f33 SH |
311 | flock($handle, LOCK_SH); |
312 | // HACK ALERT | |
313 | // There is a problem when reading from the file during PHPUNIT tests. For one reason or another the filesize is not correct | |
314 | // Doesn't happen during normal operation, just during unit tests. | |
170f821b | 315 | // Read it. |
62704f33 | 316 | $data = fread($handle, $filesize+128); |
170f821b | 317 | // Unlock it. |
62704f33 SH |
318 | flock($handle, LOCK_UN); |
319 | // Return it unserialised. | |
320 | return $this->prep_data_after_read($data); | |
321 | } | |
322 | ||
323 | /** | |
324 | * Retrieves several items from the cache store in a single transaction. | |
325 | * | |
326 | * If not all of the items are available in the cache then the data value for those that are missing will be set to false. | |
327 | * | |
328 | * @param array $keys The array of keys to retrieve | |
329 | * @return array An array of items from the cache. There will be an item for each key, those that were not in the store will | |
330 | * be set to false. | |
331 | */ | |
332 | public function get_many($keys) { | |
333 | $result = array(); | |
334 | foreach ($keys as $key) { | |
335 | $result[$key] = $this->get($key); | |
336 | } | |
337 | return $result; | |
338 | } | |
170f821b | 339 | |
62704f33 SH |
340 | /** |
341 | * Deletes an item from the cache store. | |
342 | * | |
343 | * @param string $key The key to delete. | |
344 | * @return bool Returns true if the operation was a success, false otherwise. | |
345 | */ | |
346 | public function delete($key) { | |
347 | $filename = $key.'.cache'; | |
08aaa637 | 348 | $file = $this->file_path_for_key($key); |
f86ce2dc MS |
349 | |
350 | if (@unlink($file)) { | |
351 | unset($this->keys[$filename]); | |
352 | return true; | |
353 | } | |
354 | ||
355 | return false; | |
62704f33 SH |
356 | } |
357 | ||
358 | /** | |
359 | * Deletes several keys from the cache in a single action. | |
360 | * | |
361 | * @param array $keys The keys to delete | |
362 | * @return int The number of items successfully deleted. | |
363 | */ | |
364 | public function delete_many(array $keys) { | |
365 | $count = 0; | |
366 | foreach ($keys as $key) { | |
367 | if ($this->delete($key)) { | |
368 | $count++; | |
369 | } | |
370 | } | |
371 | return $count; | |
372 | } | |
373 | ||
374 | /** | |
375 | * Sets an item in the cache given its key and data value. | |
376 | * | |
377 | * @param string $key The key to use. | |
378 | * @param mixed $data The data to set. | |
379 | * @return bool True if the operation was a success false otherwise. | |
380 | */ | |
381 | public function set($key, $data) { | |
382 | $this->ensure_path_exists(); | |
383 | $filename = $key.'.cache'; | |
08aaa637 | 384 | $file = $this->file_path_for_key($key, true); |
62704f33 SH |
385 | $result = $this->write_file($file, $this->prep_data_before_save($data)); |
386 | if (!$result) { | |
387 | // Couldn't write the file. | |
388 | return false; | |
389 | } | |
170f821b | 390 | // Record the key if required. |
62704f33 SH |
391 | if ($this->prescan) { |
392 | $this->keys[$filename] = cache::now() + 1; | |
393 | } | |
170f821b | 394 | // Return true.. it all worked **miracles**. |
62704f33 SH |
395 | return true; |
396 | } | |
397 | ||
398 | /** | |
399 | * Prepares data to be stored in a file. | |
400 | * | |
401 | * @param mixed $data | |
402 | * @return string | |
403 | */ | |
404 | protected function prep_data_before_save($data) { | |
405 | return serialize($data); | |
406 | } | |
407 | ||
408 | /** | |
409 | * Prepares the data it has been read from the cache. Undoing what was done in prep_data_before_save. | |
410 | * | |
411 | * @param string $data | |
412 | * @return mixed | |
413 | * @throws coding_exception | |
414 | */ | |
415 | protected function prep_data_after_read($data) { | |
416 | $result = @unserialize($data); | |
417 | if ($result === false) { | |
418 | throw new coding_exception('Failed to unserialise data from file. Either failed to read, or failed to write.'); | |
419 | } | |
420 | return $result; | |
421 | } | |
422 | ||
423 | /** | |
424 | * Sets many items in the cache in a single transaction. | |
425 | * | |
426 | * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two | |
427 | * keys, 'key' and 'value'. | |
428 | * @return int The number of items successfully set. It is up to the developer to check this matches the number of items | |
429 | * sent ... if they care that is. | |
430 | */ | |
431 | public function set_many(array $keyvaluearray) { | |
432 | $count = 0; | |
433 | foreach ($keyvaluearray as $pair) { | |
434 | if ($this->set($pair['key'], $pair['value'])) { | |
435 | $count++; | |
436 | } | |
437 | } | |
438 | return $count; | |
439 | } | |
440 | ||
441 | /** | |
442 | * Checks if the store has a record for the given key and returns true if so. | |
443 | * | |
444 | * @param string $key | |
445 | * @return bool | |
446 | */ | |
447 | public function has($key) { | |
448 | $filename = $key.'.cache'; | |
62704f33 SH |
449 | $maxtime = cache::now() - $this->definition->get_ttl(); |
450 | if ($this->prescan) { | |
451 | return array_key_exists($filename, $this->keys) && $this->keys[$filename] >= $maxtime; | |
452 | } | |
08aaa637 | 453 | $file = $this->file_path_for_key($key); |
62704f33 SH |
454 | return (file_exists($file) && ($this->definition->get_ttl() == 0 || filemtime($file) >= $maxtime)); |
455 | } | |
456 | ||
457 | /** | |
458 | * Returns true if the store contains records for all of the given keys. | |
459 | * | |
460 | * @param array $keys | |
461 | * @return bool | |
462 | */ | |
463 | public function has_all(array $keys) { | |
464 | foreach ($keys as $key) { | |
465 | if (!$this->has($key)) { | |
466 | return false; | |
467 | } | |
468 | } | |
469 | return true; | |
470 | } | |
471 | ||
472 | /** | |
473 | * Returns true if the store contains records for any of the given keys. | |
474 | * | |
475 | * @param array $keys | |
476 | * @return bool | |
477 | */ | |
478 | public function has_any(array $keys) { | |
479 | foreach ($keys as $key) { | |
480 | if ($this->has($key)) { | |
481 | return true; | |
482 | } | |
483 | } | |
484 | return false; | |
485 | } | |
170f821b | 486 | |
62704f33 SH |
487 | /** |
488 | * Purges the cache deleting all items within it. | |
489 | * | |
490 | * @return boolean True on success. False otherwise. | |
491 | */ | |
492 | public function purge() { | |
758dbdf8 SH |
493 | $files = glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT); |
494 | if (is_array($files)) { | |
495 | foreach ($files as $filename) { | |
496 | @unlink($filename); | |
497 | } | |
62704f33 SH |
498 | } |
499 | $this->keys = array(); | |
500 | return true; | |
501 | } | |
502 | ||
d4d2f27c MS |
503 | /** |
504 | * Given the data from the add instance form this function creates a configuration array. | |
505 | * | |
506 | * @param stdClass $data | |
507 | * @return array | |
508 | */ | |
509 | public static function config_get_configuration_array($data) { | |
510 | $config = array(); | |
511 | ||
d4d2f27c MS |
512 | if (isset($data->path)) { |
513 | $config['path'] = $data->path; | |
514 | } | |
515 | if (isset($data->autocreate)) { | |
516 | $config['autocreate'] = $data->autocreate; | |
517 | } | |
49c497ff SH |
518 | if (isset($data->singledirectory)) { |
519 | $config['singledirectory'] = $data->singledirectory; | |
520 | } | |
d4d2f27c MS |
521 | if (isset($data->prescan)) { |
522 | $config['prescan'] = $data->prescan; | |
523 | } | |
524 | ||
525 | return $config; | |
526 | } | |
527 | ||
81ede547 SH |
528 | /** |
529 | * Allows the cache store to set its data against the edit form before it is shown to the user. | |
530 | * | |
531 | * @param moodleform $editform | |
532 | * @param array $config | |
533 | */ | |
534 | public static function config_set_edit_form_data(moodleform $editform, array $config) { | |
535 | $data = array(); | |
536 | if (!empty($config['path'])) { | |
537 | $data['path'] = $config['path']; | |
538 | } | |
d837df0d SH |
539 | if (isset($config['autocreate'])) { |
540 | $data['autocreate'] = (bool)$config['autocreate']; | |
81ede547 | 541 | } |
d837df0d SH |
542 | if (isset($config['singledirectory'])) { |
543 | $data['singledirectory'] = (bool)$config['singledirectory']; | |
544 | } | |
545 | if (isset($config['prescan'])) { | |
546 | $data['prescan'] = (bool)$config['prescan']; | |
81ede547 SH |
547 | } |
548 | $editform->set_data($data); | |
549 | } | |
550 | ||
62704f33 SH |
551 | /** |
552 | * Checks to make sure that the path for the file cache exists. | |
553 | * | |
554 | * @return bool | |
555 | * @throws coding_exception | |
556 | */ | |
557 | protected function ensure_path_exists() { | |
558 | if (!is_writable($this->path)) { | |
559 | if ($this->custompath && !$this->autocreate) { | |
170f821b | 560 | throw new coding_exception('File store path does not exist. It must exist and be writable by the web server.'); |
62704f33 SH |
561 | } |
562 | if (!make_writable_directory($this->path, false)) { | |
563 | throw new coding_exception('File store path does not exist and can not be created.'); | |
564 | } | |
565 | } | |
566 | return true; | |
567 | } | |
568 | ||
62704f33 SH |
569 | /** |
570 | * Performs any necessary clean up when the store instance is being deleted. | |
571 | * | |
572 | * 1. Purges the cache directory. | |
573 | * 2. Deletes the directory we created for this cache instances data. | |
574 | */ | |
575 | public function cleanup() { | |
576 | $this->purge(); | |
577 | @rmdir($this->path); | |
578 | } | |
579 | ||
580 | /** | |
581 | * Generates an instance of the cache store that can be used for testing. | |
582 | * | |
583 | * Returns an instance of the cache store, or false if one cannot be created. | |
584 | * | |
585 | * @param cache_definition $definition | |
6fec1820 | 586 | * @return cachestore_file |
62704f33 SH |
587 | */ |
588 | public static function initialise_test_instance(cache_definition $definition) { | |
589 | $name = 'File test'; | |
6fec1820 SH |
590 | $path = make_cache_directory('cachestore_file_test'); |
591 | $cache = new cachestore_file($name, array('path' => $path)); | |
62704f33 SH |
592 | $cache->initialise($definition); |
593 | return $cache; | |
594 | } | |
595 | ||
596 | /** | |
597 | * Writes your madness to a file. | |
598 | * | |
599 | * There are several things going on in this function to try to ensure what we don't end up with partial writes etc. | |
600 | * 1. Files for writing are opened with the mode xb, the file must be created and can not already exist. | |
34c84c72 | 601 | * 2. Renaming, data is written to a temporary file, where it can be verified using md5 and is then renamed. |
62704f33 SH |
602 | * |
603 | * @param string $file Absolute file path | |
604 | * @param string $content The content to write. | |
605 | * @return bool | |
606 | */ | |
607 | protected function write_file($file, $content) { | |
608 | // Generate a temp file that is going to be unique. We'll rename it at the end to the desired file name. | |
609 | // in this way we avoid partial writes. | |
610 | $path = dirname($file); | |
611 | while (true) { | |
612 | $tempfile = $path.'/'.uniqid(sesskey().'.', true) . '.temp'; | |
613 | if (!file_exists($tempfile)) { | |
614 | break; | |
615 | } | |
616 | } | |
617 | ||
62704f33 SH |
618 | // Open the file with mode=x. This acts to create and open the file for writing only. |
619 | // If the file already exists this will return false. | |
620 | // We also force binary. | |
621 | $handle = @fopen($tempfile, 'xb+'); | |
622 | if ($handle === false) { | |
623 | // File already exists... lock already exists, return false. | |
624 | return false; | |
625 | } | |
62704f33 SH |
626 | fwrite($handle, $content); |
627 | fflush($handle); | |
628 | // Close the handle, we're done. | |
629 | fclose($handle); | |
630 | ||
62704f33 SH |
631 | if (md5_file($tempfile) !== md5($content)) { |
632 | // The md5 of the content of the file must match the md5 of the content given to be written. | |
633 | @unlink($tempfile); | |
634 | return false; | |
635 | } | |
636 | ||
637 | // Finally rename the temp file to the desired file, returning the true|false result. | |
638 | $result = rename($tempfile, $file); | |
639 | if (!$result) { | |
640 | // Failed to rename, don't leave files lying around. | |
641 | @unlink($tempfile); | |
642 | } | |
643 | return $result; | |
644 | } | |
34c84c72 SH |
645 | |
646 | /** | |
647 | * Returns the name of this instance. | |
648 | * @return string | |
649 | */ | |
650 | public function my_name() { | |
651 | return $this->name; | |
652 | } | |
bc8f1957 | 653 | } |