Test Failed
Push — master ( 13777c...12dee8 )
by Fabio
05:22
created

TMemCache   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 294
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 294
rs 9.2
c 0
b 0
f 0
wmc 40
lcom 1
cbo 6

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 7 2
B init() 0 34 7
B loadConfig() 0 33 10
A getHost() 0 4 1
A setHost() 0 8 2
A getPort() 0 4 1
A setPort() 0 8 2
A getUseMemcached() 0 4 1
A setUseMemcached() 0 6 3
A getThreshold() 0 4 1
A setThreshold() 0 8 2
A getMinSavings() 0 4 1
A setMinSavings() 0 8 2
A getValue() 0 4 1
A setValue() 0 4 1
A addValue() 0 4 1
A deleteValue() 0 4 1
A flush() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like TMemCache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TMemCache, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * TMemCache class file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @author Carl G. Mathisen <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 * @package Prado\Caching
10
 */
11
12
namespace Prado\Caching;
13
14
use Prado\Exceptions\TConfigurationException;
15
use Prado\Exceptions\TInvalidOperationException;
16
use Prado\Prado;
17
use Prado\TPropertyValue;
18
use Prado\Xml\TXmlElement;
19
20
/**
21
 * TMemCache class
22
 *
23
 * TMemCache implements a cache application module based on {@link http://www.danga.com/memcached/ memcached}.
24
 *
25
 * TMemCache can be configured with the Host and Port properties, which
26
 * specify the host and port of the memcache server to be used.
27
 * By default, they take the value 'localhost' and 11211, respectively.
28
 * These properties must be set before {@link init} is invoked.
29
 *
30
 * The following basic cache operations are implemented:
31
 * - {@link get} : retrieve the value with a key (if any) from cache
32
 * - {@link set} : store the value with a key into cache
33
 * - {@link add} : store the value only if cache does not have this key
34
 * - {@link delete} : delete the value with the specified key from cache
35
 * - {@link flush} : delete all values from cache
36
 *
37
 * Each value is associated with an expiration time. The {@link get} operation
38
 * ensures that any expired value will not be returned. The expiration time can
39
 * be specified by the number of seconds (maximum 60*60*24*30)
40
 * or a UNIX timestamp. A expiration time 0 represents never expire.
41
 *
42
 * By definition, cache does not ensure the existence of a value
43
 * even if it never expires. Cache is not meant to be an persistent storage.
44
 *
45
 * Also note, there is no security measure to protected data in memcache.
46
 * All data in memcache can be accessed by any process running in the system.
47
 *
48
 * To use this module, the memcached PHP extension must be loaded.
49
 *
50
 * Some usage examples of TMemCache are as follows,
51
 * <code>
52
 * $cache=new TMemCache;  // TMemCache may also be loaded as a Prado application module
53
 * $cache->init(null);
54
 * $cache->add('object',$object);
55
 * $object2=$cache->get('object');
56
 * </code>
57
 *
58
 * You can configure TMemCache two different ways. If you only need one memcache server
59
 * you may use the method as follows.
60
 * <code>
61
 * <module id="cache" class="System.Caching.TMemCache" Host="localhost" Port="11211" />
62
 * </code>
63
 *
64
 * If you want a more complex configuration, you may use the method as follows.
65
 * <code>
66
 * <module id="cache" classs="System.Caching.TMemCache">
67
 *     <server Host="localhost" Port="11211" Weight="1" Timeout="300" RetryInterval="15" />
68
 *     <server Host="anotherhost" Port="11211" Weight="1" Timeout="300" RetryInterval="15" />
69
 * </module>
70
 * </code>
71
 *
72
 * If loaded, TMemCache will register itself with {@link TApplication} as the
73
 * cache module. It can be accessed via {@link TApplication::getCache()}.
74
 *
75
 * TMemCache may be configured in application configuration file as follows
76
 * <code>
77
 * <module id="cache" class="System.Caching.TMemCache" Host="localhost" Port="11211" />
78
 * </code>
79
 * where {@link getHost Host} and {@link getPort Port} are configurable properties
80
 * of TMemCache.
81
 *
82
 * Automatic compression of values may be used (using zlib extension) by setting {@link getThreshold Threshold} and {@link getMinSavings MinSavings} properties.
83
 * NB : MemCache server(s) must be restarted to apply settings.
84
 *
85
 * @author Qiang Xue <[email protected]>
86
 * @package Prado\Caching
87
 * @since 3.0
88
 */
89
class TMemCache extends TCache
90
{
91
	/**
92
	 * @var bool if the module is initialized
93
	 */
94
	private $_initialized = false;
95
	/**
96
	 * @var Memcache the Memcache instance
97
	 */
98
	private $_cache;
99
	/**
100
	 * @var string host name of the memcache server
101
	 */
102
	private $_host = 'localhost';
103
	/**
104
	 * @var int the port number of the memcache server
105
	 */
106
	private $_port = 11211;
107
108
	private $_timeout = 360;
109
110
	/**
111
	 * @var int Controls the minimum value length before attempting to compress automatically.
112
	 */
113
	private $_threshold = 0;
114
115
	/**
116
	 * @var float Specifies the minimum amount of savings to actually store the value compressed. The supplied value must be between 0 and 1. Default value is 0.2 giving a minimum 20% compression savings.
117
	 */
118
	private $_minSavings = 0.0;
119
120
	/**
121
	 * @var array list of servers available
122
	 */
123
	private $_servers = [];
124
125
	/**
126
	 * Destructor.
127
	 * Disconnect the memcache server.
128
	 */
129
	public function __destruct()
130
	{
131
		if ($this->_cache !== null) {
132
			// Quit() is available only for memcached >= 2
133
			// $this->_cache->quit();
134
		}
135
	}
136
137
	/**
138
	 * Initializes this module.
139
	 * This method is required by the IModule interface. It makes sure that
140
	 * UniquePrefix has been set, creates a Memcache instance and connects
141
	 * to the memcache server.
142
	 * @param TXmlElement $config configuration for this module, can be null
143
	 * @throws TConfigurationException if memcache extension is not installed or memcache sever connection fails
144
	 */
145
	public function init($config)
146
	{
147
		if (!extension_loaded('memcached')) {
148
			throw new TConfigurationException('memcached_extension_required');
149
		}
150
151
		$this->_cache = new \Memcached;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Memcached() of type object<Memcached> is incompatible with the declared type object<Prado\Caching\Memcache> of property $_cache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
152
		$this->loadConfig($config);
153
		if (count($this->_servers)) {
154
			foreach ($this->_servers as $server) {
155
				Prado::trace('Adding server ' . $server['Host'] . ' from serverlist', '\Prado\Caching\TMemCache');
156
				if ($this->_cache->addServer(
157
					$server['Host'],
158
					$server['Port'],
159
					$server['Persistent'],
160
					$server['Weight'],
161
					$server['Timeout'],
162
					$server['RetryInterval']
163
				) === false) {
164
					throw new TConfigurationException('memcache_connection_failed', $server['Host'], $server['Port']);
165
				}
166
			}
167
		} else {
168
			Prado::trace('Adding server ' . $this->_host, '\Prado\Caching\TMemCache');
169
			if ($this->_cache->addServer($this->_host, $this->_port) === false) {
170
				throw new TConfigurationException('memcache_connection_failed', $this->_host, $this->_port);
171
			}
172
		}
173
		if ($this->_threshold !== 0) {
174
			$this->_cache->setCompressThreshold($this->_threshold, $this->_minSavings);
175
		}
176
		$this->_initialized = true;
177
		parent::init($config);
178
	}
179
180
	/**
181
	 * Loads configuration from an XML element
182
	 * @param TXmlElement $xml configuration node
183
	 * @throws TConfigurationException if log route class or type is not specified
184
	 */
185
	private function loadConfig($xml)
186
	{
187
		if ($xml instanceof TXmlElement) {
188
			foreach ($xml->getElementsByTagName('server') as $serverConfig) {
189
				$properties = $serverConfig->getAttributes();
190
				if (($host = $properties->remove('Host')) === null) {
191
					throw new TConfigurationException('memcache_serverhost_required');
192
				}
193
				if (($port = $properties->remove('Port')) === null) {
194
					throw new TConfigurationException('memcache_serverport_required');
195
				}
196
				if (!is_numeric($port)) {
197
					throw new TConfigurationException('memcache_serverport_invalid');
198
				}
199
				$server = ['Host' => $host, 'Port' => $port, 'Weight' => 1, 'Timeout' => 1800, 'RetryInterval' => 15, 'Persistent' => true];
200
				$checks = [
201
					'Weight' => 'memcache_serverweight_invalid',
202
					'Timeout' => 'memcache_servertimeout_invalid',
203
					'RetryInterval' => 'memcach_serverretryinterval_invalid'
204
				];
205
				foreach ($checks as $property => $exception) {
206
					$value = $properties->remove($property);
207
					if ($value !== null && is_numeric($value)) {
208
						$server[$property] = $value;
209
					} elseif ($value !== null) {
210
						throw new TConfigurationException($exception);
211
					}
212
				}
213
				$server['Persistent'] = TPropertyValue::ensureBoolean($properties->remove('Persistent'));
214
				$this->_servers[] = $server;
215
			}
216
		}
217
	}
218
219
	/**
220
	 * @return string host name of the memcache server
221
	 */
222
	public function getHost()
223
	{
224
		return $this->_host;
225
	}
226
227
	/**
228
	 * @param string $value host name of the memcache server
229
	 * @throws TInvalidOperationException if the module is already initialized
230
	 */
231
	public function setHost($value)
232
	{
233
		if ($this->_initialized) {
234
			throw new TInvalidOperationException('memcache_host_unchangeable');
235
		} else {
236
			$this->_host = $value;
237
		}
238
	}
239
240
	/**
241
	 * @return int port number of the memcache server
242
	 */
243
	public function getPort()
244
	{
245
		return $this->_port;
246
	}
247
248
	/**
249
	 * @param int $value port number of the memcache server
250
	 * @throws TInvalidOperationException if the module is already initialized
251
	 */
252
	public function setPort($value)
253
	{
254
		if ($this->_initialized) {
255
			throw new TInvalidOperationException('memcache_port_unchangeable');
256
		} else {
257
			$this->_port = TPropertyValue::ensureInteger($value);
258
		}
259
	}
260
261
	/**
262
	 * @return bool if memcached is used instead of memcache
263
	 * @deprecated since Prado 4.1, only memecached is available
264
	 */
265
	public function getUseMemcached()
266
	{
267
		return true;
268
	}
269
270
	/**
271
	 * @param string $value if memcached instead memcache
272
	 * @throws TInvalidOperationException if the module is already initialized or usage of the old, unsupported memcache extension has been requested
273
	 * @deprecated since Prado 4.1, only memecached is available
274
	 */
275
	public function setUseMemcached($value)
276
	{
277
		if ($this->_initialized || $value === false) {
278
			throw new TInvalidOperationException('memcache_host_unchangeable');
279
		}
280
	}
281
282
	/**
283
	 * @return int minimum value length before attempting to compress
284
	 */
285
	public function getThreshold()
286
	{
287
		return $this->_threshold;
288
	}
289
290
	/**
291
	 * @param int $value minimum value length before attempting to compress
292
	 * @throws TInvalidOperationException if the module is already initialized
293
	 */
294
	public function setThreshold($value)
295
	{
296
		if ($this->_initialized) {
297
			throw new TInvalidOperationException('memcache_threshold_unchangeable');
298
		} else {
299
			$this->_threshold = TPropertyValue::ensureInteger($value);
300
		}
301
	}
302
303
	/**
304
	 * @return float minimum amount of savings to actually store the value compressed
305
	 */
306
	public function getMinSavings()
307
	{
308
		return $this->_minSavings;
309
	}
310
311
	/**
312
	 * @param float $value minimum amount of savings to actually store the value compressed
313
	 * @throws TInvalidOperationException if the module is already initialized
314
	 */
315
	public function setMinSavings($value)
316
	{
317
		if ($this->_initialized) {
318
			throw new TInvalidOperationException('memcache_min_savings_unchangeable');
319
		} else {
320
			$this->_minSavings = TPropertyValue::ensureFloat($value);
321
		}
322
	}
323
324
	/**
325
	 * Retrieves a value from cache with a specified key.
326
	 * This is the implementation of the method declared in the parent class.
327
	 * @param string $key a unique key identifying the cached value
328
	 * @return string the value stored in cache, false if the value is not in the cache or expired.
329
	 */
330
	protected function getValue($key)
331
	{
332
		return $this->_cache->get($key);
333
	}
334
335
	/**
336
	 * Stores a value identified by a key in cache.
337
	 * This is the implementation of the method declared in the parent class.
338
	 *
339
	 * @param string $key the key identifying the value to be cached
340
	 * @param string $value the value to be cached
341
	 * @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
342
	 * @return bool true if the value is successfully stored into cache, false otherwise
343
	 */
344
	protected function setValue($key, $value, $expire)
345
	{
346
		return $this->_cache->set($key, $value, $expire);
347
	}
348
349
	/**
350
	 * Stores a value identified by a key into cache if the cache does not contain this key.
351
	 * This is the implementation of the method declared in the parent class.
352
	 *
353
	 * @param string $key the key identifying the value to be cached
354
	 * @param string $value the value to be cached
355
	 * @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
356
	 * @return bool true if the value is successfully stored into cache, false otherwise
357
	 */
358
	protected function addValue($key, $value, $expire)
359
	{
360
		$this->_cache->add($key, $value, $expire);
361
	}
362
363
	/**
364
	 * Deletes a value with the specified key from cache
365
	 * This is the implementation of the method declared in the parent class.
366
	 * @param string $key the key of the value to be deleted
367
	 * @return bool if no error happens during deletion
368
	 */
369
	protected function deleteValue($key)
370
	{
371
		return $this->_cache->delete($key);
372
	}
373
374
	/**
375
	 * Deletes all values from cache.
376
	 * Be careful of performing this operation if the cache is shared by multiple applications.
377
	 */
378
	public function flush()
379
	{
380
		return $this->_cache->flush();
381
	}
382
}
383