Issues (1061)

Sources/CacheAPI-smf.php (2 issues)

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
	/**
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))
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
		$ttl = $ttl !== null ? $ttl : $this->ttl;
91
92
		// Work around Zend's opcode caching (PHP 5.5+), they would cache older files for a couple of seconds
93
		// causing newer files to take effect a while later.
94
		if (function_exists('opcache_invalidate'))
95
			opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
96
97
		if (function_exists('apc_delete_file'))
98
			@apc_delete_file($cachedir . '/data_' . $key . '.php');
99
100
		// Otherwise custom cache?
101
		if ($value === null)
102
			@unlink($cachedir . '/data_' . $key . '.php');
103
		else
104
		{
105
			$cache_data = '<' . '?' . 'php if (!defined(\'SMF\')) die; if (' . (time() + $ttl) . ' < time()) $expired = true; else{$expired = false; $value = \'' . addcslashes($value, "\0" . '\\\'') . '\';}' . '?' . '>';
106
107
			// Write out the cache file, check that the cache write was successful; all the data must be written
108
			// If it fails due to low diskspace, or other, remove the cache file
109
			$fileSize = file_put_contents($cachedir . '/data_' . $key . '.php', $cache_data, LOCK_EX);
110
			if ($fileSize !== strlen($cache_data))
111
			{
112
				@unlink($cachedir . '/data_' . $key . '.php');
113
				return false;
114
			}
115
			else
116
				return true;
117
		}
118
	}
119
120
	/**
121
	 * {@inheritDoc}
122
	 */
123
	public function cleanCache($type = '')
124
	{
125
		$cachedir = $this->cachedir;
126
127
		// No directory = no game.
128
		if (!is_dir($cachedir))
129
			return;
130
131
		// Remove the files in SMF's own disk cache, if any
132
		$dh = opendir($cachedir);
133
		while ($file = readdir($dh))
0 ignored issues
show
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

133
		while ($file = readdir(/** @scrutinizer ignore-type */ $dh))
Loading history...
134
		{
135
			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
136
				@unlink($cachedir . '/' . $file);
137
		}
138
		closedir($dh);
0 ignored issues
show
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

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