Passed
Push — development ( 8f3e46...8433eb )
by Spuds
01:17 queued 31s
created

Filebased::opcacheReset()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
dl 0
loc 12
rs 9.2222
c 1
b 0
f 0
cc 6
nc 4
nop 1
ccs 4
cts 4
cp 1
crap 6
1
<?php
2
3
/**
4
 * This file contains functions that deal with getting and setting cache values.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * @version 2.0 dev
11
 *
12
 */
13
14
namespace ElkArte\Cache\CacheMethod;
15
16
use UnexpectedValueException;
17
18
/**
19
 * Filebased caching is the fallback if nothing else is available, it simply
20
 * uses the filesystem to store queries results in order to try to reduce the
21
 * number of queries per time period.
22
 *
23
 * The performance gain may or may not exist depending on many factors.
24
 *
25
 * It requires the CACHEDIR constant to be defined and pointing to a writable directory.
26
 */
27
class Filebased extends AbstractCacheMethod
28
{
29
	/** {@inheritDoc} */
30
	protected $title = 'File-based caching';
31
32
	/** {@inheritDoc} */
33
	protected $prefix = 'data_';
34
35
	/** @var string File extension. */
36
	protected $ext = 'php';
37
38
	/**
39
	 * Obtain from the parent class the variables necessary
40
	 * to help the tests stay running smoothly.
41
	 *
42
	 * @param string $key
43
	 *
44
	 * @return string
45
	 */
46
	public function getFileName($key)
47
	{
48
		return $this->prefix . '_' . $key . '.' . $this->ext;
49
	}
50
51
	/**
52
	 * {@inheritDoc}
53
	 */
54
	public function exists($key)
55 4
	{
56
		return $this->fileFunc->fileExists(CACHEDIR . '/' . $this->getFileName($key));
57 4
	}
58
59
	/**
60
	 * {@inheritDoc}
61
	 */
62
	public function put($key, $value, $ttl = 120)
63 2
	{
64
		$fName = $this->getFileName($key);
65 2
66
		// Clearing this data
67
		if ($value === null)
68
		{
69
			$this->fileFunc->delete(CACHEDIR . '/' . $fName);
70
		}
71 4
		// Or stashing it away
72
		else
73 4
		{
74
			$cache_data = "<?php '" . json_encode(['expiration' => time() + $ttl, 'data' => $value]) . "';";
75
76 4
			// Write out the cache file, check that the cache write was successful; all the data must be written
77
			// If it fails due to low diskspace, or other, remove the cache file
78 2
			if (file_put_contents(CACHEDIR . '/' . $fName, $cache_data, LOCK_EX) !== strlen($cache_data))
79
			{
80
				$this->fileFunc->delete(CACHEDIR . '/' . $fName);
81
			}
82
		}
83 4
84
		$this->opcacheReset($fName);
85
	}
86
87 4
	/**
88
	 * {@inheritDoc}
89
	 */
90
	public function get($key, $ttl = 120)
91
	{
92 4
		$return = null;
93
		$fName = $this->getFileName($key);
94
95
		if ($this->fileFunc->fileExists(CACHEDIR . '/' . $fName))
96
		{
97 4
			// Even though it exists, we may not be able to access the file
98
			$value = json_decode(substr(@file_get_contents(CACHEDIR . '/' . $fName), 7, -2), false);
0 ignored issues
show
Bug introduced by
It seems like @file_get_contents(ElkAr...ACHEDIR . '/' . $fName) can also be of type false; however, parameter $string of substr() does only seem to accept string, 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

98
			$value = json_decode(substr(/** @scrutinizer ignore-type */ @file_get_contents(CACHEDIR . '/' . $fName), 7, -2), false);
Loading history...
99 4
100 4
			if ($value === null || $value->expiration < time())
101
			{
102 4
				$this->fileFunc->delete(CACHEDIR . '/' . $fName);
103
			}
104
			else
105 4
			{
106
				$return = $value->data;
107 4
			}
108
109
			unset($value);
110
			$this->is_miss = $return === null;
111
112
			return $return;
113
		}
114 4
115
		$this->is_miss = true;
116
117 4
		return $return;
118 4
	}
119
120 4
	/**
121
	 * Resets the opcache for a specific file.
122
	 *
123 4
	 * If opcache is switched on, and we can use it, immediately invalidates that opcode cache
124
	 * after a file is written so that future includes are not using a stale opcode cached file.
125 4
	 *
126
	 * @param string $fName The name of the cached file.
127
	 */
128
	private function opcacheReset($fName)
129
	{
130
		if (extension_loaded('Zend OPcache') && ini_get('opcache.enable'))
131 2
		{
132
			$opcache = ini_get('opcache.restrict_api');
133
			if ($opcache === false || $opcache === '')
134
			{
135 2
				opcache_invalidate(CACHEDIR . '/' . $fName, true);
136
			}
137 2
			elseif (stripos(BOARDDIR, $opcache) !== 0)
138
			{
139 2
				opcache_invalidate(CACHEDIR . '/' . $fName, true);
140
			}
141 2
		}
142
	}
143
144
	/**
145
	 * {@inheritDoc}
146
	 */
147
	public function clean($type = '')
148
	{
149 2
		try
150
		{
151
			$files = new \FilesystemIterator(CACHEDIR, \FilesystemIterator::SKIP_DOTS);
152
153
			foreach ($files as $file)
154 2
			{
155
				if ($file->getFilename() === 'index.php')
156 2
				{
157
					continue;
158
				}
159
160
				if ($file->getFilename() === '.htaccess')
161
				{
162 2
					continue;
163
				}
164 2
165
				if ($file->getExtension() !== $this->ext)
166
				{
167
					continue;
168
				}
169
170
				$this->fileFunc->delete($file->getPathname());
171
			}
172
		}
173
		catch (UnexpectedValueException)
174
		{
175
			// @todo
176
		}
177
	}
178
179
	/**
180
	 * {@inheritDoc}
181
	 */
182 2
	public function fixkey($key)
183
	{
184 2
		return strtr($key, ':/', '-_');
185
	}
186 2
187 2
	/**
188
	 * {@inheritDoc}
189
	 */
190
	public function isAvailable()
191
	{
192
		return $this->fileFunc->isDir(CACHEDIR) && $this->fileFunc->isWritable(CACHEDIR);
193
	}
194
195
	/**
196
	 * {@inheritDoc}
197
	 */
198
	public function details()
199
	{
200
		return ['title' => $this->title, 'version' => 'N/A'];
201
	}
202
203
	/**
204
	 * Adds the settings to the settings page.
205
	 *
206
	 * Used by integrate_modify_cache_settings added in the title method
207
	 *
208
	 * @param array $config_vars
209
	 */
210
	public function settings(&$config_vars)
211
	{
212
		global $txt;
213
214
		$config_vars[] = ['cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir', 'force_div_id' => 'filebased_cachedir'];
215
	}
216
}
217