Completed
Push — master ( 531043...462e40 )
by Aimeos
01:36
created

Redis::setMultiple()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
cc 5
eloc 12
nc 12
nop 3
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-2016
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
0 ignored issues
show
Bug introduced by
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: deleteList, flush, getList, getListByTags, setList
Loading history...
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
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
	 * @throws \Aimeos\MW\Cache\Exception If the cache server doesn't respond
58
	 */
59
	public function clear()
60
	{
61
		$this->client->flushdb();
62
	}
63
64
65
	/**
66
	 * Removes the cache entries identified by the given keys.
67
	 *
68
	 * @inheritDoc
69
	 *
70
	 * @param array $keys List of key strings that identify the cache entries
71
	 * 	that should be removed
72
	 */
73
	public function deleteMultiple( $keys )
74
	{
75
		foreach( $keys as $idx => $key ) {
76
			$keys[$idx] = $this->siteid . $key;
77
		}
78
79
		$this->client->del( $keys );
80
	}
81
82
83
	/**
84
	 * Removes the cache entries identified by the given tags.
85
	 *
86
	 * @inheritDoc
87
	 *
88
	 * @param array $tags List of tag strings that are associated to one or more
89
	 * 	cache entries that should be removed
90
	 */
91
	public function deleteByTags( array $tags )
92
	{
93
		$result = $tagKeys = array();
94
		$pipe = $this->client->pipeline();
95
96
		foreach( $tags as $tag )
97
		{
98
			$tag = $this->siteid . 'tag:' . $tag;
99
			$pipe->smembers( $tag );
100
			$tagKeys[] = $tag;
101
		}
102
103
		foreach( $pipe->execute() as $keys )
104
		{
105
			foreach( $keys as $key ) {
106
				$result[$key] = null;
107
			}
108
		}
109
110
		$this->client->del( array_merge( array_keys( $result ), $tagKeys ) );
111
	}
112
113
114
	/**
115
	 * Returns the cached value for the given key.
116
	 *
117
	 * @inheritDoc
118
	 *
119
	 * @param string $key Path to the requested value like product/id/123
120
	 * @param mixed $default Value returned if requested key isn't found
121
	 * @return mixed Value associated to the requested key. If no value for the
122
	 *	key is found in the cache, the given default value is returned
123
	 */
124
	public function get( $key, $default = null )
125
	{
126
		if( ( $result = $this->client->get( $this->siteid . $key ) ) === null ) {
127
			return $default;
128
		}
129
130
		return $result;
131
	}
132
133
134
	/**
135
	 * Returns the cached values for the given cache keys if available.
136
	 *
137
	 * @inheritDoc
138
	 *
139
	 * @param iterable $keys List of key strings for the requested cache entries
140
	 * @param mixed $default Default value to return for keys that do not exist
141
	 * @return array Associative list of key/value pairs for the requested cache
142
	 * 	entries. If a cache entry doesn't exist, neither its key nor a value
143
	 * 	will be in the result list
144
	 */
145
	public function getMultiple( $keys, $default = null )
146
	{
147
		$result = $actkeys = array();
148
149
		foreach( $keys as $idx => $key ) {
150
			$actkeys[$idx] = $this->siteid . $key;
151
		}
152
153
		foreach( $this->client->mget( $actkeys ) as $idx => $value )
154
		{
155
			if( $value !== null && isset( $keys[$idx] ) ) {
156
				$result[ $keys[$idx] ] = $value;
157
			}
158
		}
159
160
		foreach( $keys as $key )
161
		{
162
			if( !isset( $result[$key] ) ) {
163
				$result[$key] = $default;
164
			}
165
		}
166
167
		return $result;
168
	}
169
170
171
	/**
172
	 * Returns the cached keys and values associated to the given tags if available.
173
	 *
174
	 * @inheritDoc
175
	 *
176
	 * @param array $tags List of tag strings associated to the requested cache entries
177
	 * @return array Associative list of key/value pairs for the requested cache
178
	 * 	entries. If a tag isn't associated to any cache entry, nothing is returned
179
	 * 	for that tag
180
	 */
181
	public function getMultipleByTags( array $tags )
182
	{
183
		$result = $actkeys = array();
184
		$len = strlen( $this->siteid );
185
		$pipe = $this->client->pipeline();
186
187
		foreach( $tags as $tag ) {
188
			$pipe->smembers( $this->siteid . 'tag:' . $tag );
189
		}
190
191
		foreach( $pipe->execute() as $keys )
192
		{
193
			foreach( $keys as $key ) {
194
				$actkeys[$key] = null;
195
			}
196
		}
197
198
		foreach( $this->client->mget( array_keys( $actkeys ) ) as $idx => $value )
199
		{
200
			if( isset( $keys[$idx] ) ) {
201
				$result[ substr( $keys[$idx], $len ) ] = $value;
202
			}
203
		}
204
205
		return $result;
206
	}
207
208
209
	/**
210
	 * Sets the value for the given key in the cache.
211
	 *
212
	 * @inheritDoc
213
	 *
214
	 * @param string $key Key string for the given value like product/id/123
215
	 * @param mixed $value Value string that should be stored for the given key
216
	 * @param int|string|null $expires Date/time string in "YYYY-MM-DD HH:mm:ss"
217
	 * 	format or as TTL value when the cache entry expires
218
	 * @param array $tags List of tag strings that should be assoicated to the
219
	 * 	given value in the cache
220
	 */
221
	public function set( $key, $value, $expires = null, array $tags = array() )
222
	{
223
		$key = $this->siteid . $key;
224
		$pipe = $this->client->pipeline();
225
		$pipe->set( $key, $value );
226
227
		foreach( $tags as $tag ) {
228
			$pipe->sadd( $this->siteid . 'tag:' . $tag, $key );
229
		}
230
231
		if( $expires !== null && ( $timestamp = strtotime( $expires ) ) !== false ) {
232
			$pipe->expireat( $key, $timestamp );
233
		}
234
235
		$pipe->execute();
236
	}
237
238
239
	/**
240
	 * Adds or overwrites the given key/value pairs in the cache, which is much
241
	 * more efficient than setting them one by one using the set() method.
242
	 *
243
	 * @inheritDoc
244
	 *
245
	 * @param iterable $pairs Associative list of key/value pairs. Both must be
246
	 * 	a string
247
	 * @param int|string|array $expires Associative list of keys and datetime
248
	 *  string or integer TTL pairs.
249
	 * @param array $tags Associative list of key/tag or key/tags pairs that
250
	 *  should be associated to the values identified by their key. The value
251
	 *  associated to the key can either be a tag string or an array of tag strings
252
	 */
253
	public function setMultiple( $pairs, $expires = null, array $tags = array() )
254
	{
255
		$actpairs = array();
256
		$pipe = $this->client->pipeline();
257
258
		foreach( $pairs as $key => $value ) {
259
			$actpairs[ $this->siteid . $key ] = $value;
260
		}
261
262
		$pipe->mset( $actpairs );
263
264
		foreach( $tags as $key => $tagList )
265
		{
266
			foreach( (array) $tagList as $tag ) {
267
				$pipe->sadd( $this->siteid . 'tag:' . $tag, $this->siteid . $key );
268
			}
269
		}
270
271
		foreach( $expires as $key => $datetime ) {
0 ignored issues
show
Bug introduced by
The expression $expires of type integer|string|array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
272
			$pipe->expireat( $this->siteid . $key, strtotime( $datetime ) );
273
		}
274
275
		$pipe->execute();
276
	}
277
}
278