Passed
Push — master ( 6c3449...f809d1 )
by Aimeos
02:11
created

Redis   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 67
dl 0
loc 253
rs 9.2
c 2
b 0
f 0
wmc 40

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 4
A set() 0 17 5
B getMultiple() 0 23 7
A deleteMultiple() 0 11 4
A deleteByTags() 0 24 6
B setMultiple() 0 30 9
A clear() 0 3 2
A get() 0 7 2
A has() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Redis 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.

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 Redis, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @license LGPLv3, http://www.gnu.org/licenses/lgpl.html
5
 * @copyright Metaways Infosystems GmbH, 2014
6
 * @copyright Aimeos (aimeos.org), 2015-2018
7
 * @package MW
8
 * @subpackage Cache
9
 */
10
11
12
namespace Aimeos\MW\Cache;
13
14
15
/**
16
 * Redis cache class.
17
 *
18
 * @package MW
19
 * @subpackage Cache
20
 */
21
class Redis
22
	extends \Aimeos\MW\Cache\Base
23
	implements \Aimeos\MW\Cache\Iface
24
{
25
	private $client;
26
	private $siteid;
27
28
29
	/**
30
	 * Initializes the object instance.
31
	 *
32
	 * @param array $config Configuration for Predis client if instance should be created
33
	 * @param Predis\Client $client Predis client instance
0 ignored issues
show
Bug introduced by
The type Aimeos\MW\Cache\Predis\Client was not found. Did you mean Predis\Client? If so, make sure to prefix the type with \.
Loading history...
34
	 */
35
	public function __construct( array $config, \Predis\Client $client )
36
	{
37
		$this->client = $client;
38
		$this->siteid = ( isset( $config['siteid'] ) ? $config['siteid'] . '-' : null );
39
40
		if( isset( $config['auth'] ) && !$this->client->auth( $config['auth'] ) ) {
41
			throw new \Aimeos\MW\Cache\Exception( 'Authentication failed for Redis' );
42
		}
43
	}
44
45
46
	/**
47
	 * Removes all entries of the site from the cache.
48
	 *
49
	 * @inheritDoc
50
	 *
51
	 * As Redis does only provider up to 20 different databases, this isn't enough
52
	 * to use one of them for each site. Alternatively, using the KEYS command
53
	 * to fetch all cache keys of a site and delete them afterwards can take very
54
	 * long for billions of keys. Therefore, flush() clears the cache entries of
55
	 * all sites.
56
	 *
57
	 * @return bool True on success and false on failure
58
	 */
59
	public function clear() : bool
60
	{
61
		return $this->client->flushdb() === 'OK' ? true : false;
62
	}
63
64
65
	/**
66
	 * Removes the cache entries identified by the given keys.
67
	 *
68
	 * @inheritDoc
69
	 *
70
	 * @param iterable $keys List of key strings that identify the cache entries that should be removed
71
	 * @return bool True if the items were successfully removed. False if there was an error.
72
	 * @throws \Psr\SimpleCache\InvalidArgumentException
73
	 */
74
	public function deleteMultiple( iterable $keys ) : bool
75
	{
76
		if( empty( $keys ) ) {
77
			return true;
78
		}
79
80
		foreach( $keys as $idx => $key ) {
81
			$keys[$idx] = $this->siteid . $key;
82
		}
83
84
		return $this->client->del( $keys ) === 'OK' ? true : false;
85
	}
86
87
88
	/**
89
	 * Removes the cache entries identified by the given tags.
90
	 *
91
	 * @inheritDoc
92
	 *
93
	 * @param iterable $tags List of tag strings that are associated to one or more cache entries that should be removed
94
	 * @return bool True if the items were successfully removed. False if there was an error.
95
	 * @throws \Psr\SimpleCache\InvalidArgumentException
96
	 */
97
	public function deleteByTags( iterable $tags ) : bool
98
	{
99
		if( empty( $tags ) ) {
100
			return true;
101
		}
102
103
		$result = $tagKeys = [];
104
		$pipe = $this->client->pipeline();
105
106
		foreach( $tags as $tag )
107
		{
108
			$tag = $this->siteid . 'tag:' . $tag;
109
			$pipe->smembers( $tag );
110
			$tagKeys[] = $tag;
111
		}
112
113
		foreach( $pipe->execute() as $keys )
114
		{
115
			foreach( $keys as $key ) {
116
				$result[$key] = null;
117
			}
118
		}
119
120
		return $this->client->del( array_merge( array_keys( $result ), $tagKeys ) ) === 'OK' ? true : false;
121
	}
122
123
124
	/**
125
	 * Returns the cached value for the given key.
126
	 *
127
	 * @inheritDoc
128
	 *
129
	 * @param string $key Path to the requested value like product/id/123
130
	 * @param mixed $default Value returned if requested key isn't found
131
	 * @return mixed Value associated to the requested key. If no value for the
132
	 *	key is found in the cache, the given default value is returned
133
	 * @throws \Psr\SimpleCache\InvalidArgumentException
134
	 */
135
	public function get( string $key, $default = null )
136
	{
137
		if( ( $result = $this->client->get( $this->siteid . $key ) ) === null ) {
138
			return $default;
139
		}
140
141
		return $result;
142
	}
143
144
145
	/**
146
	 * Returns the cached values for the given cache keys if available.
147
	 *
148
	 * @inheritDoc
149
	 *
150
	 * @param iterable $keys List of key strings for the requested cache entries
151
	 * @param mixed $default Default value to return for keys that do not exist
152
	 * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
153
	 * @throws \Psr\SimpleCache\InvalidArgumentException
154
	 */
155
	public function getMultiple( iterable $keys, $default = null ) : iterable
156
	{
157
		$result = $actkeys = [];
158
159
		foreach( $keys as $idx => $key ) {
160
			$actkeys[$idx] = $this->siteid . $key;
161
		}
162
163
		foreach( $this->client->mget( $actkeys ) as $idx => $value )
164
		{
165
			if( $value !== null && isset( $keys[$idx] ) ) {
166
				$result[ $keys[$idx] ] = $value;
167
			}
168
		}
169
170
		foreach( $keys as $key )
171
		{
172
			if( !isset( $result[$key] ) ) {
173
				$result[$key] = $default;
174
			}
175
		}
176
177
		return $result;
178
	}
179
180
181
	/**
182
	 * Determines whether an item is present in the cache.
183
	 *
184
	 * @inheritDoc
185
	 *
186
	 * @param string $key The cache item key
187
	 * @return bool True if cache entry is available, false if not
188
	 * @throws \Psr\SimpleCache\InvalidArgumentException
189
	 */
190
	public function has( string $key ) : bool
191
	{
192
		return (bool) $this->client->exists();
193
	}
194
195
196
	/**
197
	 * Sets the value for the given key in the cache.
198
	 *
199
	 * @inheritDoc
200
	 *
201
	 * @param string $key Key string for the given value like product/id/123
202
	 * @param mixed $value Value string that should be stored for the given key
203
	 * @param \DateInterval|int|string|null $expires Date interval object,
204
	 *  date/time string in "YYYY-MM-DD HH:mm:ss" format or as integer TTL value
205
	 *  when the cache entry will expiry
206
	 * @param iterable $tags List of tag strings that should be assoicated to the cache entry
207
	 * @return bool True on success and false on failure.
208
	 * @throws \Psr\SimpleCache\InvalidArgumentException
209
	 */
210
	public function set( string $key, $value, $expires = null, iterable $tags = [] ) : bool
211
	{
212
		$key = $this->siteid . $key;
213
		$pipe = $this->client->pipeline();
214
		$pipe->set( $key, $value );
215
216
		foreach( $tags as $tag ) {
217
			$pipe->sadd( $this->siteid . 'tag:' . $tag, $key );
218
		}
219
220
		if( is_string( $expires ) ) {
221
			$pipe->expireat( $key, date_create( $expires )->getTimestamp() );
222
		} elseif( is_int( $expires ) ) {
223
			$pipe->expireat( $key, $expires );
224
		}
225
226
		return $pipe->execute() === 'OK' ? true : false;
0 ignored issues
show
introduced by
The condition $pipe->execute() === 'OK' is always false.
Loading history...
227
	}
228
229
230
	/**
231
	 * Adds or overwrites the given key/value pairs in the cache, which is much
232
	 * more efficient than setting them one by one using the set() method.
233
	 *
234
	 * @inheritDoc
235
	 *
236
	 * @param iterable $pairs Associative list of key/value pairs. Both must be a string
237
	 * @param \DateInterval|int|string|null $expires Date interval object,
238
	 *  date/time string in "YYYY-MM-DD HH:mm:ss" format or as integer TTL value
239
	 *  when the cache entry will expiry
240
	 * @param iterable $tags List of tags that should be associated to the cache entries
241
	 * @return bool True on success and false on failure.
242
	 * @throws \Psr\SimpleCache\InvalidArgumentException
243
	 */
244
	public function setMultiple( iterable $pairs, $expires = null, iterable $tags = [] ) : bool
245
	{
246
		$actpairs = [];
247
		$pipe = $this->client->pipeline();
248
249
		foreach( $pairs as $key => $value ) {
250
			$actpairs[ $this->siteid . $key ] = $value;
251
		}
252
253
		$pipe->mset( $actpairs );
254
255
		foreach( $pairs as $key => $value )
256
		{
257
			if( $expires instanceof \DateInterval ) {
258
				$pipe->expireat( $this->siteid . $key, date_create()->add( $expires )->getTimestamp() );
259
			} elseif( is_string( $expires ) ) {
260
				$pipe->expireat( $this->siteid . $key, date_create( $expires )->getTimestamp() );
261
			} elseif( is_int( $expires ) ) {
262
				$pipe->expire( $this->siteid . $key, $expires );
263
			}
264
		}
265
266
		foreach( $tags as $key => $tagList )
267
		{
268
			foreach( (array) $tagList as $tag ) {
269
				$pipe->sadd( $this->siteid . 'tag:' . $tag, $this->siteid . $key );
270
			}
271
		}
272
273
		return $pipe->execute() === 'OK' ? true : false;
0 ignored issues
show
introduced by
The condition $pipe->execute() === 'OK' is always false.
Loading history...
274
	}
275
}
276