Completed
Push — master ( 32eaad...6cdc53 )
by Nazar
04:10
created

FileSystem::set()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.3541

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 14
nc 7
nop 2
dl 0
loc 20
ccs 11
cts 14
cp 0.7856
crap 6.3541
rs 8.8571
c 1
b 0
f 0
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 38
	protected function get_absolute_path ($path) {
21 38
		$path      = str_replace(['/', '\\'], '/', $path);
22 38
		$parts     = array_filter(explode('/', $path), 'strlen');
23 38
		$absolutes = [];
24 38
		foreach ($parts as $part) {
25 38
			if ('.' == $part) {
26
				continue;
27
			}
28 38
			if ('..' == $part) {
29
				array_pop($absolutes);
30
			} else {
31 38
				$absolutes[] = $part;
32
			}
33
		}
34 38
		return CACHE.'/'.implode('/', $absolutes);
35
	}
36
	/**
37
	 * @inheritdoc
38
	 */
39 38
	function get ($item) {
40 38
		$path_in_filesystem = $this->get_absolute_path($item);
41
		if (
42 38
			strpos($path_in_filesystem, CACHE) !== 0 ||
43 38
			!is_file($path_in_filesystem)
44
		) {
45 18
			return false;
46
		}
47 36
		$cache = file_get_contents($path_in_filesystem);
48 36
		$cache = _json_decode($cache);
49 36
		if ($cache !== false) {
50 36
			return $cache;
51
		}
52
		unlink($path_in_filesystem);
53
		return false;
54
	}
55
	/**
56
	 * @inheritdoc
57
	 */
58 18
	function set ($item, $data) {
59 18
		$path_in_filesystem = $this->get_absolute_path($item);
60 18
		if (strpos($path_in_filesystem, CACHE) !== 0) {
61
			return false;
62
		}
63 18
		$data = _json_encode($data);
64 18
		if (mb_strpos($item, '/') !== false) {
65 18
			$path = mb_substr($item, 0, mb_strrpos($item, '/'));
66 18
			if (!is_dir(CACHE."/$path")) {
67
				/** @noinspection MkdirRaceConditionInspection */
68 16
				@mkdir(CACHE."/$path", 0770, true);
69
			}
70 18
			unset($path);
71
		}
72 18
		if (!file_exists($path_in_filesystem) || is_writable($path_in_filesystem)) {
73 18
			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 20
	function del ($item) {
82 20
		$path_in_filesystem = $this->get_absolute_path($item);
83 20
		if (strpos($path_in_filesystem, CACHE) !== 0) {
84
			return false;
85
		}
86 20
		if (is_dir($path_in_filesystem)) {
87
			/**
88
			 * Rename to random name in order to immediately invalidate nested elements, actual deletion done right after this
89
			 */
90 4
			$new_path = $path_in_filesystem.md5(random_bytes(1000));
91
			/**
92
			 * Sometimes concurrent deletion might happen, so we need to silent error and actually remove directory only when renaming was successful
93
			 */
94 4
			return @rename($path_in_filesystem, $new_path) ? rmdir_recursive($new_path) : !is_dir($path_in_filesystem);
95
		}
96 20
		return file_exists($path_in_filesystem) ? @unlink($path_in_filesystem) : true;
97
	}
98
	/**
99
	 * @inheritdoc
100
	 */
101 2
	function clean () {
102 2
		$ok         = true;
103 2
		$dirs_to_rm = [];
104
		/**
105
		 * Remove root files and rename root directories for instant cache cleaning
106
		 */
107 2
		$random_key = md5(random_bytes(1000));
108 2
		get_files_list(
109 2
			CACHE,
110 2
			false,
111 2
			'fd',
112 2
			true,
113 2
			false,
114 2
			false,
115 2
			false,
116 2
			true,
117 2
			function ($item) use (&$ok, &$dirs_to_rm, $random_key) {
118 2
				if (is_writable($item)) {
119 2
					if (is_dir($item)) {
120 2
						rename($item, "$item$random_key");
121 2
						$dirs_to_rm[] = "$item$random_key";
122
					} else {
123 2
						@unlink($item);
124
					}
125
				} else {
126
					$ok = false;
127
				}
128 2
			}
129
		);
130
		/**
131
		 * Then remove all renamed directories
132
		 */
133 2
		foreach ($dirs_to_rm as $dir) {
134 2
			$ok = rmdir_recursive($dir) && $ok;
135
		}
136 2
		return $ok;
137
	}
138
}
139