Completed
Push — master ( b349f6...45fe22 )
by Nazar
03:59
created

FileSystem::del()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 5
eloc 9
c 1
b 1
f 0
nc 5
nop 1
dl 0
loc 18
rs 8.8571
1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\Cache;
9
/**
10
 * Provides cache functionality based on file system structure.
11
 */
12
class FileSystem extends _Abstract {
13
	/**
14
	 * Like realpath() but works even if files does not exists
15
	 *
16
	 * @param string $path
17
	 *
18
	 * @return string
19
	 */
20
	protected function get_absolute_path ($path) {
21
		$path      = str_replace(['/', '\\'], '/', $path);
22
		$parts     = array_filter(explode('/', $path), 'strlen');
23
		$absolutes = [];
24
		foreach ($parts as $part) {
25
			if ('.' == $part) {
26
				continue;
27
			}
28
			if ('..' == $part) {
29
				array_pop($absolutes);
30
			} else {
31
				$absolutes[] = $part;
32
			}
33
		}
34
		return CACHE.'/'.implode('/', $absolutes);
35
	}
36
	/**
37
	 * @inheritdoc
38
	 */
39
	function get ($item) {
40
		$path_in_filesystem = $this->get_absolute_path($item);
41
		if (
42
			strpos($path_in_filesystem, CACHE) !== 0 ||
43
			!is_file($path_in_filesystem)
44
		) {
45
			file_put_contents(DIR.'/test', var_export([$path_in_filesystem, CACHE, strpos($path_in_filesystem, CACHE) !== 0, !is_file($path_in_filesystem)], true), FILE_APPEND);
46
			return false;
47
		}
48
		$cache = file_get_contents($path_in_filesystem);
49
		$cache = @_json_decode($cache);
50
		if ($cache !== false) {
51
			return $cache;
52
		}
53
		unlink($path_in_filesystem);
54
		return false;
55
	}
56
	/**
57
	 * @inheritdoc
58
	 */
59
	function set ($item, $data) {
60
		$path_in_filesystem = $this->get_absolute_path($item);
61
		if (strpos($path_in_filesystem, CACHE) !== 0) {
62
			return false;
63
		}
64
		$data = @_json_encode($data);
65
		if (mb_strpos($item, '/') !== false) {
66
			$path = mb_substr($item, 0, mb_strrpos($item, '/'));
67
			if (!is_dir(CACHE."/$path")) {
68
				@mkdir(CACHE."/$path", 0770, true);
69
			}
70
			unset($path);
71
		}
72
		if (!file_exists($path_in_filesystem) || is_writable($path_in_filesystem)) {
73
			return (bool)file_put_contents($path_in_filesystem, $data, LOCK_EX | FILE_BINARY);
74
		}
75
		trigger_error("File $path_in_filesystem not available for writing", E_USER_WARNING);
76
		return false;
77
	}
78
	/**
79
	 * @inheritdoc
80
	 */
81
	function del ($item) {
82
		file_put_contents(DIR.'/test', "Deleted $item\n".print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), true), FILE_APPEND);
83
		$path_in_filesystem = $this->get_absolute_path($item);
84
		if (strpos($path_in_filesystem, CACHE) !== 0) {
85
			return false;
86
		}
87
		if (is_dir($path_in_filesystem)) {
88
			/**
89
			 * Rename to random name in order to immediately invalidate nested elements, actual deletion done right after this
90
			 */
91
			$new_path = $path_in_filesystem.md5(random_bytes(1000));
92
			/**
93
			 * Sometimes concurrent deletion might happen, so we need to silent error and actually remove directory only when renaming was successful
94
			 */
95
			return @rename($path_in_filesystem, $new_path) ? rmdir_recursive($new_path) : !is_dir($path_in_filesystem);
96
		}
97
		return file_exists($path_in_filesystem) ? @unlink($path_in_filesystem) : true;
98
	}
99
	/**
100
	 * @inheritdoc
101
	 */
102
	function clean () {
103
		$ok         = true;
104
		$dirs_to_rm = [];
105
		/**
106
		 * Remove root files and rename root directories for instant cache cleaning
107
		 */
108
		$random_key = md5(random_bytes(1000));
109
		get_files_list(
110
			CACHE,
111
			false,
112
			'fd',
113
			true,
114
			false,
115
			false,
116
			false,
117
			true,
118
			function ($item) use (&$ok, &$dirs_to_rm, $random_key) {
119
				if (is_writable($item)) {
120
					if (is_dir($item)) {
121
						rename($item, "$item$random_key");
122
						$dirs_to_rm[] = "$item$random_key";
123
					} else {
124
						@unlink($item);
125
					}
126
				} else {
127
					$ok = false;
128
				}
129
			}
130
		);
131
		/**
132
		 * Then remove all renamed directories
133
		 */
134
		foreach ($dirs_to_rm as $dir) {
135
			$ok = rmdir_recursive($dir) && $ok;
136
		}
137
		return $ok;
138
	}
139
}
140