TEtcdCache   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 39
c 2
b 0
f 0
dl 0
loc 180
ccs 0
cts 75
cp 0
rs 10
wmc 17

13 Methods

Rating   Name   Duplication   Size   Complexity  
A request() 0 12 1
A flush() 0 4 1
A init() 0 6 2
A addValue() 0 8 2
A getHost() 0 3 1
A setHost() 0 3 1
A setValue() 0 8 2
A deleteValue() 0 4 1
A getPort() 0 3 1
A setDir() 0 3 1
A getDir() 0 3 1
A setPort() 0 3 1
A getValue() 0 4 2
1
<?php
2
3
/**
4
 * TEtcdCache class file
5
 *
6
 * @author LANDWEHR Computer und Software GmbH <[email protected]>
7
 * @link https://github.com/pradosoft/prado4
8
 * @license https://github.com/pradosoft/prado4/blob/master/LICENSE
9
 */
10
11
namespace Prado\Caching;
12
13
use Prado\TPropertyValue;
14
use Prado\Exceptions\TConfigurationException;
15
16
/**
17
 * TEtcdCache class
18
 *
19
 * TEtcdCache implements a cache application module based on the distributed,
20
 * consistent key-value store {@see https://github.com/coreos/etcd etcd}.
21
 * etcd is high performance key-value store written in Go which uses the Raft
22
 * consensus algorithm to manage a highly-available replicated log.
23
 *
24
 * By definition, cache does not ensure the existence of a value
25
 * even if it never expires. Cache is not meant to be an persistent storage.
26
 *
27
 * To use this module, an etcd instance must be running and reachable on the host
28
 * specified by {@see setHost} and the port specified by {@see setPort} which
29
 * default to 'localhost:2379'. All values are stored within a directory set by
30
 * {@see setDir} which defaults to 'pradocache'.
31
 *
32
 * TEtcdCache only supports etcd API v2 and uses cURL to fire the HTTP
33
 * GET/PUT/DELETE commands, thus the PHP cURL extension is also needed.
34
 *
35
 * Some usage examples of TEtcdCache are as follows,
36
 * ```php
37
 * $cache = new TEtcdCache(); // TEtcdCache may also be loaded as a Prado application module
38
 * $cache->init(null);
39
 * $cache->add('value', $value);
40
 * $value = $cache->get('value');
41
 * ```
42
 *
43
 * If loaded, TEtcdCache will register itself with {@see \Prado\TApplication} as the
44
 * cache module. It can be accessed via {@see \Prado\TApplication::getCache()}.
45
 *
46
 * TEtcdCache may be configured in application configuration file as follows
47
 * ```php
48
 * <module id="cache" class="Prado\Caching\TEtcdCache" Host="localhost" Port="2379" Dir="pradocache" />
49
 * ```
50
 *
51
 * @author LANDWEHR Computer und Software GmbH <[email protected]>
52
 * @since 4.0
53
 */
54
class TEtcdCache extends TCache
55
{
56
	/**
57
	 * @var string the etcd host
58
	 */
59
	protected $_host = 'localhost';
60
61
	/**
62
	 * @var int the etcd port
63
	 */
64
	protected $_port = 2379;
65
66
	/**
67
	 * @var string the directory to store values in
68
	 */
69
	protected $_dir = 'pradocache';
70
71
	/**
72
	 * Initializes this module.
73
	 * This method is required by the IModule interface.
74
	 * @param \Prado\Xml\TXmlElement $config configuration for this module, can be null
75
	 * @throws TConfigurationException if cURL extension is not installed
76
	 */
77
	public function init($config)
78
	{
79
		if (!function_exists('curl_version')) {
80
			throw new TConfigurationException('curl_extension_required');
81
		}
82
		parent::init($config);
83
	}
84
85
	/**
86
	 * Gets the host the etcd instance is running on, defaults to 'localhost'.
87
	 * @return string the etcd host
88
	 */
89
	public function getHost()
90
	{
91
		return $this->_host;
92
	}
93
94
	/**
95
	 * Sets the host the etcd instance is running on.
96
	 * @param string $value the etcd host
97
	 */
98
	public function setHost($value)
99
	{
100
		$this->_host = TPropertyValue::ensureString($value);
101
	}
102
103
	/**
104
	 * Gets the port the etcd instance is running on, defaults to 2379.
105
	 * @return int the etcd port
106
	 */
107
	public function getPort()
108
	{
109
		return $this->_port;
110
	}
111
112
	/**
113
	 * Sets the port the etcd instance is running on.
114
	 * @param int $value the etcd port
115
	 */
116
	public function setPort($value)
117
	{
118
		$this->_port = TPropertyValue::ensureInteger($value);
119
	}
120
121
	/**
122
	 * Sets the directory to store values in, defaults to 'pradocache'.
123
	 * @return string the directory to store values in
124
	 */
125
	public function getDir()
126
	{
127
		return $this->_dir;
128
	}
129
130
	/**
131
	 * Gets the directory to store values in.
132
	 * @param string $value the directory to store values in
133
	 */
134
	public function setDir($value)
135
	{
136
		$this->_dir = TPropertyValue::ensureString($value);
137
	}
138
139
	/**
140
	 * Retrieves a value from cache with a specified key.
141
	 * This is the implementation of the method declared in the parent class.
142
	 * @param string $key a unique key identifying the cached value
143
	 * @return false|string the value stored in cache, false if the value is not in the cache or expired.
144
	 */
145
	protected function getValue($key)
146
	{
147
		$result = $this->request('GET', $this->_dir . '/' . $key);
148
		return property_exists($result, 'errorCode') ? false : unserialize($result->node->value);
149
	}
150
151
	/**
152
	 * Stores a value identified by a key in cache.
153
	 * This is the implementation of the method declared in the parent class.
154
	 *
155
	 * @param string $key the key identifying the value to be cached
156
	 * @param string $value the value to be cached
157
	 * @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
158
	 * @return bool true if the value is successfully stored into cache, false otherwise
159
	 */
160
	protected function setValue($key, $value, $expire)
161
	{
162
		$value = ['value' => serialize($value)];
163
		if ($expire > 0) {
164
			$value['ttl'] = $expire;
165
		}
166
		$result = $this->request('PUT', $this->_dir . '/' . $key, $value);
167
		return !property_exists($result, 'errorCode');
168
	}
169
170
	/**
171
	 * Stores a value identified by a key into cache if the cache does not contain this key.
172
	 * This is the implementation of the method declared in the parent class.
173
	 *
174
	 * @param string $key the key identifying the value to be cached
175
	 * @param string $value the value to be cached
176
	 * @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
177
	 * @return bool true if the value is successfully stored into cache, false otherwise
178
	 */
179
	protected function addValue($key, $value, $expire)
180
	{
181
		$value = ['value' => serialize($value), 'prevExist' => 'false'];
182
		if ($expire > 0) {
183
			$value['ttl'] = $expire;
184
		}
185
		$result = $this->request('PUT', $this->_dir . '/' . $key, $value);
186
		return !property_exists($result, 'errorCode');
187
	}
188
189
	/**
190
	 * Deletes a value with the specified key from cache
191
	 * This is the implementation of the method declared in the parent class.
192
	 * @param string $key the key of the value to be deleted
193
	 * @return bool if no error happens during deletion
194
	 */
195
	protected function deleteValue($key)
196
	{
197
		$this->request('DELETE', $this->_dir . '/' . $key);
198
		return true;
199
	}
200
201
	/**
202
	 * Deletes all values from cache.
203
	 * Be careful of performing this operation if the cache is shared by multiple applications.
204
	 * @return bool if no error happens during flush
205
	 */
206
	public function flush()
207
	{
208
		$this->request('DELETE', $this->_dir . '?recursive=true');
209
		return true;
210
	}
211
212
	/**
213
	 * This method does the actual cURL request by generating the method specific
214
	 * URL, setting the cURL options and adding additional request parameters.
215
	 * The etcd always returns a JSON string which is decoded and returned to
216
	 * the calling method.
217
	 * @param string $method the HTTP method for the request (GET,PUT,DELETE)
218
	 * @param string $key the the key to perform the action on (includes the directory)
219
	 * @param array $value the additional post data to send with the request
220
	 * @return \stdClass the response from the etcd instance
221
	 */
222
	protected function request($method, $key, $value = [])
223
	{
224
		$curl = curl_init("http://{$this->_host}:{$this->_port}/v2/keys/{$key}");
225
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
226
		curl_setopt($curl, CURLOPT_HEADER, false);
227
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
228
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
229
		curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
230
		curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($value));
231
		$response = curl_exec($curl);
232
		curl_close($curl);
233
		return json_decode($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() 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

233
		return json_decode(/** @scrutinizer ignore-type */ $response);
Loading history...
234
	}
235
}
236