Completed
Push — release-2.1 ( 4bdd41...11eb43 )
by Mathias
07:36
created

smf_cache::getVersion()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
12
 */
13
14
if (!defined('SMF'))
15
	die('Hacking attempt...');
16
17
/**
18
 * Our Cache API class
19
 * @package cacheAPI
20
 */
21
class smf_cache extends cache_api
22
{
23
	/**
24
	 * @var string The path to the current $cachedir directory.
25
	 */
26
	private $cachedir = null;
27
28
	/**
29
	 * {@inheritDoc}
30
	 */
31
	public function __construct()
32
	{
33
		parent::__construct();
34
35
		// Set our default cachedir.
36
		$this->setCachedir();
37
	}
38
39
	/**
40
	 * {@inheritDoc}
41
	 */
42 View Code Duplication
	public function isSupported($test = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
	{
44
		$supported = is_writable($this->cachedir);
45
46
		if ($test)
47
			return $supported;
48
		return parent::isSupported() && $supported;
49
	}
50
51
	/**
52
	 * {@inheritDoc}
53
	 */
54
	public function getData($key, $ttl = null)
55
	{
56
		$key = $this->prefix . strtr($key, ':/', '-_');
57
		$cachedir = $this->cachedir;
58
59
		// SMF Data returns $value and $expired.  $expired has a unix timestamp of when this expires.
60
		if (file_exists($cachedir . '/data_' . $key . '.php') && filesize($cachedir . '/data_' . $key . '.php') > 10)
61
		{
62
			// Work around Zend's opcode caching (PHP 5.5+), they would cache older files for a couple of seconds
63
			// causing newer files to take effect a while later.
64
			if (function_exists('opcache_invalidate'))
65
				opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
66
67
			if (function_exists('apc_delete_file'))
68
				@apc_delete_file($cachedir . '/data_' . $key . '.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
69
70
			// php will cache file_exists et all, we can't 100% depend on its results so proceed with caution
71
			@include($cachedir . '/data_' . $key . '.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
72
			if (!empty($expired) && isset($value))
0 ignored issues
show
Bug introduced by
The variable $expired seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
Bug introduced by
The variable $value seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
73
			{
74
				@unlink($cachedir . '/data_' . $key . '.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
75
				unset($value);
76
			}
77
		}
78
79
		return !empty($value) ? $value : null;
80
	}
81
82
	/**
83
	 * {@inheritDoc}
84
	 */
85
	public function putData($key, $value, $ttl = null)
86
	{
87
		$key = $this->prefix . strtr($key, ':/', '-_');
88
		$cachedir = $this->cachedir;
89
90
		// Work around Zend's opcode caching (PHP 5.5+), they would cache older files for a couple of seconds
91
		// causing newer files to take effect a while later.
92
		if (function_exists('opcache_invalidate'))
93
			opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
94
95
		if (function_exists('apc_delete_file'))
96
			@apc_delete_file($cachedir . '/data_' . $key . '.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
97
98
		// Otherwise custom cache?
99
		if ($value === null)
100
			@unlink($cachedir . '/data_' . $key . '.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
101
		else
102
		{
103
			$cache_data = '<' . '?' . 'php if (!defined(\'SMF\')) die; if (' . (time() + $ttl) . ' < time()) $expired = true; else{$expired = false; $value = \'' . addcslashes($value, '\\\'') . '\';}' . '?' . '>';
104
105
			// Write out the cache file, check that the cache write was successful; all the data must be written
106
			// If it fails due to low diskspace, or other, remove the cache file
107
			$fileSize = file_put_contents($cachedir . '/data_' . $key . '.php', $cache_data, LOCK_EX);
108
			if ($fileSize !== strlen($cache_data))
109
			{
110
				@unlink($cachedir . '/data_' . $key . '.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
111
				return false;
112
			}
113
			else
114
				return true;
115
		}
116
	}
117
118
	/**
119
	 * {@inheritDoc}
120
	 */
121
	public function cleanCache($type = '')
122
	{
123
		$cachedir = $this->cachedir;
124
125
		// No directory = no game.
126
		if (!is_dir($cachedir))
127
			return;
128
129
		// Remove the files in SMF's own disk cache, if any
130
		$dh = opendir($cachedir);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dh. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
131
		while ($file = readdir($dh))
132
		{
133
			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
134
				@unlink($cachedir . '/' . $file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
135
		}
136
		closedir($dh);
137
138
		// Make this invalid.
139
		$this->invalidateCache();
140
141
		return true;
142
	}
143
144
	/**
145
	 * {@inheritDoc}
146
	 */
147
	public function invalidateCache()
148
	{
149
		// We don't worry about $cachedir here, since the key is based on the real $cachedir.
150
		parent::invalidateCache();
151
152
		// Since SMF is file based, be sure to clear the statcache.
153
		clearstatcache();
154
155
		return true;
156
	}
157
158
	/**
159
	 * {@inheritDoc}
160
	 */
161 View Code Duplication
	public function cacheSettings(array &$config_vars)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
	{
163
		global $context, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
164
165
		$config_vars[] = $txt['cache_smf_settings'];
166
		$config_vars[] = array('cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir');
167
168
		if (!isset($context['settings_post_javascript']))
169
			$context['settings_post_javascript'] = '';
170
171
		$context['settings_post_javascript'] .= '
172
			$("#cache_accelerator").change(function (e) {
173
				var cache_type = e.currentTarget.value;
174
				$("#cachedir").prop("disabled", cache_type != "smf");
175
			});';
176
	}
177
178
	/**
179
	 * Sets the $cachedir or uses the SMF default $cachedir..
180
	 *
181
	 * @access public
182
	 * @param string $dir A valid path
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dir not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
183
	 * @return boolean If this was successful or not.
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
184
	 */
185 View Code Duplication
	public function setCachedir($dir = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
	{
187
		global $cachedir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
188
189
		// If its invalid, use SMF's.
190
		if (is_null($dir) || !is_writable($dir))
191
			$this->cachedir = $cachedir;
192
		else
193
			$this->cachedir = $dir;
194
	}
195
196
	/**
197
	 * Gets the current $cachedir.
198
	 *
199
	 * @access public
200
	 * @return string the value of $ttl.
201
	 */
202
	public function getCachedir()
203
	{
204
		return $this->cachedir;
205
	}
206
207
	/**
208
	 * {@inheritDoc}
209
	 */
210
	public function getVersion()
211
	{
212
		global $forum_version;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
213
214
		return isset($forum_version) ? $forum_version : '2.1';
215
	}
216
}
217
218
?>