Completed
Push — master ( 80c358...ebe41b )
by Nazar
04:41
created

FileSystem   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 95.08%

Importance

Changes 2
Bugs 2 Features 0
Metric Value
dl 0
loc 115
rs 10
c 2
b 2
f 0
ccs 58
cts 61
cp 0.9508
wmc 22
lcom 1
cbo 1

5 Methods

Rating   Name   Duplication   Size   Complexity  
A get_absolute_path() 0 7 2
A get() 0 13 4
B set() 0 20 6
B del() 0 17 5
B clean() 0 37 5
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
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

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