Completed
Push — master ( fc7743...6ad0d8 )
by Nazar
04:33
created

FileSystem::del()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 5
nop 1
dl 0
loc 17
rs 8.8571
c 0
b 0
f 0
ccs 8
cts 8
cp 1
crap 5
1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2017, 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 false|string
19
	 */
20 84
	protected function get_absolute_path ($path) {
21 84
		$path = str_replace(['/', '\\'], '/', $path);
22 84
		if (strpos($path, '/..') !== false) {
23 2
			return false;
24
		}
25 84
		return CACHE.'/'.$path;
26
	}
27
	/**
28
	 * @inheritdoc
29
	 */
30 84
	public function get ($item) {
31 84
		$path_in_filesystem = $this->get_absolute_path($item);
32 84
		if (!$path_in_filesystem || !is_file($path_in_filesystem)) {
33 60
			return false;
34
		}
35 82
		$cache = file_get_contents($path_in_filesystem);
36 82
		$cache = _json_decode($cache);
37 82
		if (json_last_error() === JSON_ERROR_NONE) {
38 82
			return $cache;
39
		}
40 2
		unlink($path_in_filesystem);
41 2
		return false;
42
	}
43
	/**
44
	 * @inheritdoc
45
	 */
46 60
	public function set ($item, $data) {
47 60
		$path_in_filesystem = $this->get_absolute_path($item);
48 60
		if (!$path_in_filesystem) {
49 2
			return false;
50
		}
51 60
		if (mb_strpos($item, '/') !== false) {
52 60
			$path = mb_substr($item, 0, mb_strrpos($item, '/'));
53 60
			if (!is_dir(CACHE."/$path")) {
54
				/** @noinspection MkdirRaceConditionInspection */
55 22
				@mkdir(CACHE."/$path", 0770, true);
56
			}
57 60
			unset($path);
58
		}
59 60
		if (!file_exists($path_in_filesystem) || is_writable($path_in_filesystem)) {
60 60
			$random = uniqid($path_in_filesystem, true);
61
			return
62 60
				(bool)file_put_contents($random, _json_encode($data), LOCK_EX | FILE_BINARY) &&
63 60
				rename($random, $path_in_filesystem);
64
		}
65
		trigger_error("File $path_in_filesystem not available for writing", E_USER_WARNING);
66
		return false;
67
	}
68
	/**
69
	 * @inheritdoc
70
	 */
71 60
	public function del ($item) {
72 60
		$path_in_filesystem = $this->get_absolute_path($item);
73 60
		if (!$path_in_filesystem) {
74 2
			return false;
75
		}
76 60
		if (is_dir($path_in_filesystem)) {
77
			/**
78
			 * Rename to random name in order to immediately invalidate nested elements, actual deletion done right after this
79
			 */
80 8
			$new_path = $path_in_filesystem.md5(random_bytes(1000));
81
			/**
82
			 * Sometimes concurrent deletion might happen, so we need to silent error and actually remove directory only when renaming was successful
83
			 */
84 8
			return @rename($path_in_filesystem, $new_path) ? rmdir_recursive($new_path) : !is_dir($path_in_filesystem);
85
		}
86 60
		return file_exists($path_in_filesystem) ? @unlink($path_in_filesystem) : true;
87
	}
88
	/**
89
	 * @inheritdoc
90
	 */
91 4
	public function clean () {
92 4
		$ok         = true;
93 4
		$dirs_to_rm = [];
94
		/**
95
		 * Remove root files and rename root directories for instant cache cleaning
96
		 */
97 4
		$random_key = md5(random_bytes(1000));
98 4
		get_files_list(
99 4
			CACHE,
100 4
			false,
101 4
			'fd',
102 4
			true,
103 4
			false,
104 4
			false,
105 4
			false,
106 4
			true,
107 4
			function ($item) use (&$ok, &$dirs_to_rm, $random_key) {
108 4
				if (is_writable($item)) {
109 4
					if (is_dir($item)) {
110 4
						rename($item, "$item$random_key");
111 4
						$dirs_to_rm[] = "$item$random_key";
112
					} else {
113 4
						@unlink($item);
114
					}
115
				} else {
116
					$ok = false;
117
				}
118 4
			}
119
		);
120
		/**
121
		 * Then remove all renamed directories
122
		 */
123 4
		foreach ($dirs_to_rm as $dir) {
124 4
			$ok = rmdir_recursive($dir) && $ok;
125
		}
126 4
		return $ok;
127
	}
128
}
129