Passed
Push — master ( d65b86...ea640b )
by Aimeos
22:07 queued 14:23
created

Redis::setMultiple()   B

Complexity

Conditions 8
Paths 30

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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