Passed
Pull Request — release-2.1 (#6211)
by John
04:34
created

smf_cache   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 85
c 4
b 0
f 0
dl 0
loc 228
rs 9.76
wmc 33

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A isSupported() 0 7 3
A readFile() 0 20 4
A putData() 0 29 4
A cacheSettings() 0 11 2
A cleanCache() 0 16 3
A writeFile() 0 27 5
A getData() 0 17 5
A setCachedir() 0 9 3
A invalidateCache() 0 9 1
A getCachedir() 0 3 1
A getVersion() 0 3 1
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2020 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 RC2
12
 */
13
14
if (!defined('SMF'))
15
	die('Hacking attempt...');
16
17
/**
18
 * Our Cache API class
19
 *
20
 * @package cacheAPI
21
 */
22
class smf_cache extends cache_api
23
{
24
	/**
25
	 * @var string The path to the current $cachedir directory.
26
	 */
27
	private $cachedir = null;
28
29
	/**
30
	 * {@inheritDoc}
31
	 */
32
	public function __construct()
33
	{
34
		parent::__construct();
35
36
		// Set our default cachedir.
37
		$this->setCachedir();
38
	}
39
40
	/**
41
	 * {@inheritDoc}
42
	 */
43
	public function isSupported($test = false)
44
	{
45
		$supported = is_writable($this->cachedir);
46
47
		if ($test)
48
			return $supported;
49
		return parent::isSupported() && $supported;
50
	}
51
52
	private function readFile($file)
53
	{
54
		if (($fp = fopen($file, 'rb')) !== false)
55
		{
56
			if (!flock($fp, LOCK_SH))
57
			{
58
				fclose($fp);
59
				return false;
60
			}
61
			$string = '';
62
			while (!feof($fp))
63
				$string .= fread($fp, 8192);
64
65
			flock($fp, LOCK_UN);
66
			fclose($fp);
67
68
			return $string;
69
		}
70
71
		return false;
72
	}
73
74
	private function writeFile($file, $string)
75
	{
76
		if (($fp = fopen($file, 'cb')) !== false)
77
		{
78
			if (!flock($fp, LOCK_EX))
79
			{
80
				fclose($fp);
81
				return false;
82
			}
83
			ftruncate($fp, 0);
84
			$bytes = 0;
85
			$pieces = str_split($string, 8192);
86
			foreach ($pieces as $piece)
87
			{
88
				if (($val = fwrite($fp, $piece, 8192)) !== false)
89
					$bytes += $val;
90
				else
91
					return false;
92
			}
93
			fflush($fp);
94
			flock($fp, LOCK_UN);
95
			fclose($fp);
96
97
			return $bytes;
98
		}
99
100
		return false;
101
	}
102
103
	/**
104
	 * {@inheritDoc}
105
	 */
106
	public function getData($key, $ttl = null)
107
	{
108
		$file = sprintf('%s/data_%s.cache',
109
			$this->cachedir,
110
			$this->prefix . strtr($key, ':/', '-_')
111
		);
112
113
		// SMF Data returns $value and $expired.  $expired has a unix timestamp of when this expires.
114
		if (file_exists($file) && ($raw = $this->readFile($file)) !== false)
115
		{
116
			if (($value = smf_json_decode($raw, true, false)) !== array() && $value['expiration'] >= time())
117
				return $value['value'];
118
			else
119
				@unlink($file);
120
		}
121
122
		return null;
123
	}
124
125
	/**
126
	 * {@inheritDoc}
127
	 */
128
	public function putData($key, $value, $ttl = null)
129
	{
130
		$file = sprintf('%s/data_%s.cache',
131
			$this->cachedir,
132
			$this->prefix . strtr($key, ':/', '-_')
133
		);
134
		$ttl = $ttl !== null ? $ttl : $this->ttl;
135
136
		if ($value === null)
137
			@unlink($file);
138
		else
139
		{
140
			$cache_data = json_encode(
141
				array(
142
					'expiration' => time() + $ttl,
143
					'value' => $value
144
				),
145
				JSON_NUMERIC_CHECK
146
			);
147
148
			// Write out the cache file, check that the cache write was successful; all the data must be written
149
			// If it fails due to low diskspace, or other, remove the cache file
150
			if ($this->writeFile($file, $cache_data) !== strlen($cache_data))
151
			{
152
				@unlink($file);
153
				return false;
154
			}
155
			else
156
				return true;
157
		}
158
	}
159
160
	/**
161
	 * {@inheritDoc}
162
	 */
163
	public function cleanCache($type = '')
164
	{
165
		// No directory = no game.
166
		if (!is_dir($this->cachedir))
167
			return;
168
169
		// Remove the files in SMF's own disk cache, if any
170
		$files = new GlobIterator($this->cachedir . '/' . $type . '*.cache', FilesystemIterator::NEW_CURRENT_AND_KEY);
171
172
		foreach ($files as $file => $info)
173
			unlink($this->cachedir . '/' . $file);
174
175
		// Make this invalid.
176
		$this->invalidateCache();
177
178
		return true;
179
	}
180
181
	/**
182
	 * {@inheritDoc}
183
	 */
184
	public function invalidateCache()
185
	{
186
		// We don't worry about $cachedir here, since the key is based on the real $cachedir.
187
		parent::invalidateCache();
188
189
		// Since SMF is file based, be sure to clear the statcache.
190
		clearstatcache();
191
192
		return true;
193
	}
194
195
	/**
196
	 * {@inheritDoc}
197
	 */
198
	public function cacheSettings(array &$config_vars)
199
	{
200
		global $context, $txt;
201
202
		$config_vars[] = $txt['cache_smf_settings'];
203
		$config_vars[] = array('cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir');
204
205
		if (!isset($context['settings_post_javascript']))
206
			$context['settings_post_javascript'] = '';
207
208
		$context['settings_post_javascript'] .= '
209
			$("#cache_accelerator").change(function (e) {
210
				var cache_type = e.currentTarget.value;
211
				$("#cachedir").prop("disabled", cache_type != "smf");
212
			});';
213
	}
214
215
	/**
216
	 * Sets the $cachedir or uses the SMF default $cachedir..
217
	 *
218
	 * @access public
219
	 * @param string $dir A valid path
220
	 * @return boolean If this was successful or not.
221
	 */
222
	public function setCachedir($dir = null)
223
	{
224
		global $cachedir;
225
226
		// If its invalid, use SMF's.
227
		if (is_null($dir) || !is_writable($dir))
228
			$this->cachedir = $cachedir;
229
		else
230
			$this->cachedir = $dir;
231
	}
232
233
	/**
234
	 * Gets the current $cachedir.
235
	 *
236
	 * @access public
237
	 * @return string the value of $ttl.
238
	 */
239
	public function getCachedir()
240
	{
241
		return $this->cachedir;
242
	}
243
244
	/**
245
	 * {@inheritDoc}
246
	 */
247
	public function getVersion()
248
	{
249
		return SMF_VERSION;
250
	}
251
}
252
253
?>