smf_cache   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 62
dl 0
loc 192
rs 9.68
c 0
b 0
f 0
wmc 34

10 Methods

Rating   Name   Duplication   Size   Complexity  
A putData() 0 30 5
A cacheSettings() 0 11 2
B cleanCache() 0 21 9
B getData() 0 26 8
A setCachedir() 0 9 3
A invalidateCache() 0 9 1
A __construct() 0 6 1
A getCachedir() 0 3 1
A isSupported() 0 7 3
A getVersion() 0 3 1
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2019 Simple Machines and individual contributors
9
 * @license http://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
	/**
53
	 * {@inheritDoc}
54
	 */
55
	public function getData($key, $ttl = null)
56
	{
57
		$key = $this->prefix . strtr($key, ':/', '-_');
58
		$cachedir = $this->cachedir;
59
60
		// SMF Data returns $value and $expired.  $expired has a unix timestamp of when this expires.
61
		if (file_exists($cachedir . '/data_' . $key . '.php') && filesize($cachedir . '/data_' . $key . '.php') > 10)
62
		{
63
			// Work around Zend's opcode caching (PHP 5.5+), they would cache older files for a couple of seconds
64
			// causing newer files to take effect a while later.
65
			if (function_exists('opcache_invalidate'))
66
				opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
67
68
			if (function_exists('apc_delete_file'))
69
				@apc_delete_file($cachedir . '/data_' . $key . '.php');
70
71
			// php will cache file_exists et all, we can't 100% depend on its results so proceed with caution
72
			@include($cachedir . '/data_' . $key . '.php');
73
			if (!empty($expired) && isset($value))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $expired seems to never exist and therefore empty should always be true.
Loading history...
Comprehensibility Best Practice introduced by
The variable $value seems to never exist and therefore isset should always be false.
Loading history...
74
			{
75
				@unlink($cachedir . '/data_' . $key . '.php');
76
				unset($value);
77
			}
78
		}
79
80
		return !empty($value) ? $value : null;
81
	}
82
83
	/**
84
	 * {@inheritDoc}
85
	 */
86
	public function putData($key, $value, $ttl = null)
87
	{
88
		$key = $this->prefix . strtr($key, ':/', '-_');
89
		$cachedir = $this->cachedir;
90
91
		// Work around Zend's opcode caching (PHP 5.5+), they would cache older files for a couple of seconds
92
		// causing newer files to take effect a while later.
93
		if (function_exists('opcache_invalidate'))
94
			opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
95
96
		if (function_exists('apc_delete_file'))
97
			@apc_delete_file($cachedir . '/data_' . $key . '.php');
98
99
		// Otherwise custom cache?
100
		if ($value === null)
101
			@unlink($cachedir . '/data_' . $key . '.php');
102
		else
103
		{
104
			$cache_data = '<' . '?' . 'php if (!defined(\'SMF\')) die; if (' . (time() + $ttl) . ' < time()) $expired = true; else{$expired = false; $value = \'' . addcslashes($value, "\0" . '\\\'') . '\';}' . '?' . '>';
105
106
			// Write out the cache file, check that the cache write was successful; all the data must be written
107
			// If it fails due to low diskspace, or other, remove the cache file
108
			$fileSize = file_put_contents($cachedir . '/data_' . $key . '.php', $cache_data, LOCK_EX);
109
			if ($fileSize !== strlen($cache_data))
110
			{
111
				@unlink($cachedir . '/data_' . $key . '.php');
112
				return false;
113
			}
114
			else
115
				return true;
116
		}
117
	}
118
119
	/**
120
	 * {@inheritDoc}
121
	 */
122
	public function cleanCache($type = '')
123
	{
124
		$cachedir = $this->cachedir;
125
126
		// No directory = no game.
127
		if (!is_dir($cachedir))
128
			return;
129
130
		// Remove the files in SMF's own disk cache, if any
131
		$dh = opendir($cachedir);
132
		while ($file = readdir($dh))
0 ignored issues
show
Bug introduced by
It seems like $dh can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
		while ($file = readdir(/** @scrutinizer ignore-type */ $dh))
Loading history...
133
		{
134
			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
135
				@unlink($cachedir . '/' . $file);
136
		}
137
		closedir($dh);
0 ignored issues
show
Bug introduced by
It seems like $dh can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

137
		closedir(/** @scrutinizer ignore-type */ $dh);
Loading history...
138
139
		// Make this invalid.
140
		$this->invalidateCache();
141
142
		return true;
143
	}
144
145
	/**
146
	 * {@inheritDoc}
147
	 */
148
	public function invalidateCache()
149
	{
150
		// We don't worry about $cachedir here, since the key is based on the real $cachedir.
151
		parent::invalidateCache();
152
153
		// Since SMF is file based, be sure to clear the statcache.
154
		clearstatcache();
155
156
		return true;
157
	}
158
159
	/**
160
	 * {@inheritDoc}
161
	 */
162
	public function cacheSettings(array &$config_vars)
163
	{
164
		global $context, $txt;
165
166
		$config_vars[] = $txt['cache_smf_settings'];
167
		$config_vars[] = array('cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir');
168
169
		if (!isset($context['settings_post_javascript']))
170
			$context['settings_post_javascript'] = '';
171
172
		$context['settings_post_javascript'] .= '
173
			$("#cache_accelerator").change(function (e) {
174
				var cache_type = e.currentTarget.value;
175
				$("#cachedir").prop("disabled", cache_type != "smf");
176
			});';
177
	}
178
179
	/**
180
	 * Sets the $cachedir or uses the SMF default $cachedir..
181
	 *
182
	 * @access public
183
	 * @param string $dir A valid path
184
	 * @return boolean If this was successful or not.
185
	 */
186
	public function setCachedir($dir = null)
187
	{
188
		global $cachedir;
189
190
		// If its invalid, use SMF's.
191
		if (is_null($dir) || !is_writable($dir))
192
			$this->cachedir = $cachedir;
193
		else
194
			$this->cachedir = $dir;
195
	}
196
197
	/**
198
	 * Gets the current $cachedir.
199
	 *
200
	 * @access public
201
	 * @return string the value of $ttl.
202
	 */
203
	public function getCachedir()
204
	{
205
		return $this->cachedir;
206
	}
207
208
	/**
209
	 * {@inheritDoc}
210
	 */
211
	public function getVersion()
212
	{
213
		return SMF_VERSION;
214
	}
215
}
216
217
?>