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

smf_cache::cleanCache()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 2
b 0
f 0
nc 3
nop 1
dl 0
loc 16
rs 10
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($filename)
53
	{
54
		if (($fp = fopen($filename, 'rb')) !== false)
55
		{
56
			$string = '';
57
			while (!feof($fp))
58
				$string .= fread($fp, 8192);
59
60
			fclose($fp);
61
62
			return $string;
63
		}
64
65
		return false;
66
	}
67
68
	private function writeFile($filename, $string)
69
	{
70
		$tempfile = $filename . uniqid(rand(), true);
71
		if (($fp = fopen($tempfile, 'wb')) !== false)
72
		{
73
			$bytes = 0;
74
			$pieces = str_split($string, 8192);
75
			foreach ($pieces as $piece)
76
			{
77
				if (($val = fwrite($fp, $piece, 8192)) !== false)
78
					$bytes += $val;
79
				else
80
					break;
81
			}
82
			fflush($fp);
83
			fclose($fp);
84
			if ($bytes === strlen($string))
85
				rename($tempfile, $filename);
86
87
			return $bytes;
88
		}
89
90
		return false;
91
	}
92
93
	/**
94
	 * {@inheritDoc}
95
	 */
96
	public function getData($key, $ttl = null)
97
	{
98
		$file = sprintf('%s/data_%s.cache',
99
			$this->cachedir,
100
			$this->prefix . strtr($key, ':/', '-_')
101
		);
102
103
		// SMF Data returns $value and $expired.  $expired has a unix timestamp of when this expires.
104
		if (file_exists($file) && ($raw = $this->readFile($file)) !== false)
105
		{
106
			if (($value = smf_json_decode($raw, true, false)) !== array() && $value['expiration'] >= time())
107
				return $value['value'];
108
			else
109
				unlink($file);
110
		}
111
112
		return null;
113
	}
114
115
	/**
116
	 * {@inheritDoc}
117
	 */
118
	public function putData($key, $value, $ttl = null)
119
	{
120
		$file = sprintf('%s/data_%s.cache',
121
			$this->cachedir,
122
			$this->prefix . strtr($key, ':/', '-_')
123
		);
124
		$ttl = $ttl !== null ? $ttl : $this->ttl;
125
126
		if ($value === null)
127
			@unlink($file);
128
		else
129
		{
130
			$cache_data = json_encode(
131
				array(
132
					'expiration' => time() + $ttl,
133
					'value' => $value
134
				),
135
				JSON_NUMERIC_CHECK
136
			);
137
138
			// Write out the cache file, check that the cache write was successful; all the data must be written
139
			// If it fails due to low diskspace, or other, remove the cache file
140
			return $this->writeFile($file, $cache_data) !== strlen($cache_data);
141
		}
142
	}
143
144
	/**
145
	 * {@inheritDoc}
146
	 */
147
	public function cleanCache($type = '')
148
	{
149
		// No directory = no game.
150
		if (!is_dir($this->cachedir))
151
			return;
152
153
		// Remove the files in SMF's own disk cache, if any
154
		$files = new GlobIterator($this->cachedir . '/' . $type . '*.cache', FilesystemIterator::NEW_CURRENT_AND_KEY);
155
156
		foreach ($files as $file => $info)
157
			unlink($this->cachedir . '/' . $file);
158
159
		// Make this invalid.
160
		$this->invalidateCache();
161
162
		return true;
163
	}
164
165
	/**
166
	 * {@inheritDoc}
167
	 */
168
	public function invalidateCache()
169
	{
170
		// We don't worry about $cachedir here, since the key is based on the real $cachedir.
171
		parent::invalidateCache();
172
173
		// Since SMF is file based, be sure to clear the statcache.
174
		clearstatcache();
175
176
		return true;
177
	}
178
179
	/**
180
	 * {@inheritDoc}
181
	 */
182
	public function cacheSettings(array &$config_vars)
183
	{
184
		global $context, $txt;
185
186
		$config_vars[] = $txt['cache_smf_settings'];
187
		$config_vars[] = array('cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir');
188
189
		if (!isset($context['settings_post_javascript']))
190
			$context['settings_post_javascript'] = '';
191
192
		$context['settings_post_javascript'] .= '
193
			$("#cache_accelerator").change(function (e) {
194
				var cache_type = e.currentTarget.value;
195
				$("#cachedir").prop("disabled", cache_type != "smf");
196
			});';
197
	}
198
199
	/**
200
	 * Sets the $cachedir or uses the SMF default $cachedir..
201
	 *
202
	 * @access public
203
	 * @param string $dir A valid path
204
	 * @return boolean If this was successful or not.
205
	 */
206
	public function setCachedir($dir = null)
207
	{
208
		global $cachedir;
209
210
		// If its invalid, use SMF's.
211
		if (is_null($dir) || !is_writable($dir))
212
			$this->cachedir = $cachedir;
213
		else
214
			$this->cachedir = $dir;
215
	}
216
217
	/**
218
	 * Gets the current $cachedir.
219
	 *
220
	 * @access public
221
	 * @return string the value of $ttl.
222
	 */
223
	public function getCachedir()
224
	{
225
		return $this->cachedir;
226
	}
227
228
	/**
229
	 * {@inheritDoc}
230
	 */
231
	public function getVersion()
232
	{
233
		return SMF_VERSION;
234
	}
235
}
236
237
?>