Completed
Push — 2.0-dev ( 574eeb...e9e84c )
by George
12s
created

AbstractCacheItemPool::deleteMultiple()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 6

Duplication

Lines 9
Ratio 64.29 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 9
loc 14
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
crap 12
1
<?php
2
/**
3
 * Part of the Joomla Framework Cache Package
4
 *
5
 * @copyright  Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
6
 * @license    GNU General Public License version 2 or later; see LICENSE
7
 */
8
9
namespace Joomla\Cache;
10
11
use Joomla\Cache\Exception\InvalidArgumentException;
12
use Joomla\Cache\Item\HasExpirationDateInterface;
13
use Psr\Cache\CacheItemInterface;
14
use Psr\SimpleCache\CacheInterface;
15
16
/**
17
 * Joomla! Caching Class
18
 *
19
 * @since  1.0
20
 */
21
abstract class AbstractCacheItemPool implements CacheItemPoolInterface, CacheInterface
22
{
23
	/**
24
	 * The options for the cache object.
25
	 *
26
	 * @var    array|\ArrayAccess
27
	 * @since  1.0
28
	 */
29
	protected $options;
30
31
	/**
32
	 * The deferred items to store
33
	 *
34
	 * @var    Item\Item[]
35
	 * @since  1.0
36
	 */
37
	private $deferred = [];
38
39
	/**
40
	 * Constructor.
41
	 *
42
	 * @param   array|\ArrayAccess  $options  An options array, or an object that implements \ArrayAccess
43
	 *
44
	 * @since   1.0
45
	 * @throws  InvalidArgumentException
46
	 */
47 130
	public function __construct($options = [])
48
	{
49 130
		if (!($options instanceof \ArrayAccess || is_array($options)))
50 130
		{
51
			throw new InvalidArgumentException(sprintf('%s requires an options array or an object that implements \\ArrayAccess', get_class($this)));
52
		}
53
54 130
		$this->options = $options;
55 130
	}
56
57
	/**
58
	 * Returns a traversable set of cache items.
59
	 *
60
	 * @param   string[]  $keys  An indexed array of keys of items to retrieve.
61
	 *
62
	 * @return  array  A traversable collection of Cache Items keyed by the cache keys of each item.
63
	 *                 A Cache item will be returned for each key, even if that key is not found.
64
	 *
65
	 * @since   __DEPLOY_VERSION__
66
	 */
67 16
	public function getItems(array $keys = [])
68
	{
69 16
		$result = [];
70
71 16
		foreach ($keys as $key)
72
		{
73 16
			$result[$key] = $this->getItem($key);
74 16
		}
75
76 16
		return $result;
77
	}
78
79
	/**
80
	 * Get an option from the Cache instance.
81
	 *
82
	 * @param   string  $key  The name of the option to get.
83
	 *
84
	 * @return  mixed  The option value.
85
	 *
86
	 * @since   1.0
87
	 */
88 14
	public function getOption($key)
89
	{
90 14
		return isset($this->options[$key]) ? $this->options[$key] : null;
91
	}
92
93
	/**
94
	 * Removes multiple items from the pool.
95
	 *
96
	 * @param   array  $keys  An array of keys that should be removed from the pool.
97
	 *
98
	 * @return  boolean  True if the items were successfully removed. False if there was an error.
99
	 *
100
	 * @since   __DEPLOY_VERSION__
101
	 */
102 12
	public function deleteItems(array $keys)
103
	{
104 12
		foreach ($keys as $key)
105
		{
106 12
			if (!$this->deleteItem($key))
107 12
			{
108
				return false;
109
			}
110 12
		}
111
112 12
		return true;
113
	}
114
115
	/**
116
	 * Set an option for the Cache instance.
117
	 *
118
	 * @param   string  $key    The name of the option to set.
119
	 * @param   mixed   $value  The option value to set.
120
	 *
121
	 * @return  $this
122
	 *
123
	 * @since   1.0
124
	 */
125 7
	public function setOption($key, $value)
126
	{
127 7
		$this->options[$key] = $value;
128
129 7
		return $this;
130
	}
131
132
	/**
133
	 * Sets a cache item to be persisted later.
134
	 *
135
	 * @param   CacheItemInterface  $item  The cache item to save.
136
	 *
137
	 * @return  boolean  False if the item could not be queued or if a commit was attempted and failed. True otherwise.
138
	 *
139
	 * @since   __DEPLOY_VERSION__
140
	 */
141 21
	public function saveDeferred(CacheItemInterface $item)
142
	{
143 21
		$this->deferred[$item->getKey()] = $item;
144
145 21
		return true;
146
	}
147
148
	/**
149
	 * Persists any deferred cache items.
150
	 *
151
	 * @return  boolean  True if all not-yet-saved items were successfully saved or there were none. False otherwise.
152
	 *
153
	 * @since   __DEPLOY_VERSION__
154
	 */
155 21
	public function commit()
156
	{
157 21
		$result = true;
158
159 21
		foreach ($this->deferred as $key => $deferred)
160
		{
161 21
			$saveResult = $this->save($deferred);
162
163 21
			if (true === $saveResult)
164 21
			{
165 21
				unset($this->deferred[$key]);
166 21
			}
167
168 21
			$result = $result && $saveResult;
169 21
		}
170
171 21
		return $result;
172
	}
173
174
	/**
175
	 * Fetches a value from the cache.
176
	 *
177
	 * @param   string  $key      The unique key of this item in the cache.
178
	 * @param   mixed   $default  Default value to return if the key does not exist.
179
	 *
180
	 * @return  mixed  The value of the item from the cache, or $default in case of cache miss.
181
	 *
182
	 * @since   __DEPLOY_VERSION__
183
	 */
184 24
	public function get($key, $default = null)
185
	{
186 24
		$item = $this->getItem($key);
187
188 24
		if (!$item->isHit())
189 24
		{
190 12
			return $default;
191
		}
192
193 24
		return $item->get();
194
	}
195
196
	/**
197
	 * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
198
	 *
199
	 * @param   string                 $key    The key of the item to store.
200
	 * @param   mixed                  $value  The value of the item to store, must be serializable.
201
	 * @param   null|int|DateInterval  $ttl    Optional. The TTL value of this item. If no value is sent and
202
	 *                                         the driver supports TTL then the library may set a default value
203
	 *                                         for it or let the driver take care of that.
204
	 *
205
	 * @return  boolean True on success and false on failure.
206
	 *
207
	 * @since   __DEPLOY_VERSION__
208
	 */
209 31
	public function set($key, $value, $ttl = null)
210
	{
211 31
		$item = $this->getItem($key);
212 31
		$item->set($value);
213 31
		$item->expiresAfter($ttl);
214
215 31
		return $this->save($item);
216
	}
217
218
	/**
219
	 * Delete an item from the cache by its unique key.
220
	 *
221
	 * @param   string  $key  The unique cache key of the item to delete.
222
	 *
223
	 * @return  boolean  True if the item was successfully removed. False if there was an error.
224
	 *
225
	 * @since   __DEPLOY_VERSION__
226
	 */
227 7
	public function delete($key)
228
	{
229 7
		return $this->deleteItem($key);
230
	}
231
232
	/**
233
	 * Obtains multiple cache items by their unique keys.
234
	 *
235
	 * @param   iterable  $keys     A list of keys that can obtained in a single operation.
236
	 * @param   mixed     $default  Default value to return for keys that do not exist.
237
	 *
238
	 * @return  iterable  A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
239
	 *
240
	 * @since   __DEPLOY_VERSION__
241
	 * @throws  InvalidArgumentException
242
	 */
243 7
	public function getMultiple($keys, $default = null)
244
	{
245 7 View Code Duplication
		if (!is_array($keys))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246 7
		{
247
			if (!($keys instanceof \Traversable))
248
			{
249
				throw new InvalidArgumentException('$keys is neither an array nor Traversable');
250
			}
251
252
			$keys = iterator_to_array($keys, false);
253
		}
254
255 7
		$items = $this->getItems($keys);
256
257 7
		return $this->generateValues($default, $items);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->generateValues($default, $items); (Generator) is incompatible with the return type declared by the interface Psr\SimpleCache\CacheInterface::getMultiple of type Psr\SimpleCache\iterable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
258
	}
259
260
	/**
261
	 * Persists a set of key => value pairs in the cache, with an optional TTL.
262
	 *
263
	 * @param   iterable               $values  A list of key => value pairs for a multiple-set operation.
264
	 * @param   null|int|DateInterval  $ttl     Optional. The TTL value of this item. If no value is sent and
265
	 *                                          the driver supports TTL then the library may set a default value
266
	 *                                          for it or let the driver take care of that.
267
	 *
268
	 * @return  boolean  True on success and false on failure.
269
	 *
270
	 * @since   __DEPLOY_VERSION__
271
	 * @throws  InvalidArgumentException
272
	 */
273 14
	public function setMultiple($values, $ttl = null)
274
	{
275 14
		if (!is_array($values))
276 14
		{
277
			if (!$values instanceof \Traversable)
278
			{
279
				throw new InvalidArgumentException('$values is neither an array nor Traversable');
280
			}
281
		}
282
283 14
		$keys        = [];
284 14
		$arrayValues = [];
285
286 14
		foreach ($values as $key => $value)
287
		{
288 14
			if (is_int($key))
289 14
			{
290
				$key = (string) $key;
291
			}
292
293 14
			$keys[]            = $key;
294 14
			$arrayValues[$key] = $value;
295 14
		}
296
297 14
		$items       = $this->getItems($keys);
298 14
		$itemSuccess = true;
299
300
		/** @var $item CacheItemInterface */
301 14
		foreach ($items as $key => $item)
302
		{
303 14
			$item->set($arrayValues[$key]);
304 14
			$item->expiresAfter($ttl);
305
306 14
			$itemSuccess = $itemSuccess && $this->saveDeferred($item);
307 14
		}
308
309 14
		return $itemSuccess && $this->commit();
310
	}
311
312
	/**
313
	 * Deletes multiple cache items in a single operation.
314
	 *
315
	 * @param   iterable  $keys  A list of string-based keys to be deleted.
316
	 *
317
	 * @return  boolean  True if the items were successfully removed. False if there was an error.
318
	 *
319
	 * @since   __DEPLOY_VERSION__
320
	 * @throws  InvalidArgumentException
321
	 */
322
	public function deleteMultiple($keys)
323
	{
324 View Code Duplication
		if (!is_array($keys))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
325
		{
326
			if (!$keys instanceof \Traversable)
327
			{
328
				throw new InvalidArgumentException('$keys is neither an array nor Traversable');
329
			}
330
331
			$keys = iterator_to_array($keys, false);
332
		}
333
334
		return $this->deleteItems($keys);
335
	}
336
337
	/**
338
	 * Determines whether an item is present in the cache.
339
	 *
340
	 * NOTE: It is recommended that has() is only to be used for cache warming type purposes
341
	 * and not to be used within your live applications operations for get/set, as this method
342
	 * is subject to a race condition where your has() will return true and immediately after,
343
	 * another script can remove it making the state of your app out of date.
344
	 *
345
	 * @param   string  $key  The cache item key.
346
	 *
347
	 * @return  bool
348
	 *
349
	 * @since   __DEPLOY_VERSION__
350
	 * @throws  InvalidArgumentException
351
	 */
352 7
	public function has($key)
353
	{
354 7
		return $this->hasItem($key);
355
	}
356
357
	/**
358
	 * Converts a DateTime object from the cache item to the expiry time in seconds from the present
359
	 *
360
	 * @param   HasExpirationDateInterface  $item  The cache item
361
	 *
362
	 * @return  integer  The time in seconds until expiry
363
	 *
364
	 * @since   __DEPLOY_VERSION__
365
	 */
366 38
	protected function convertItemExpiryToSeconds(HasExpirationDateInterface $item)
367
	{
368 38
		$itemExpiry   = $item->getExpiration();
369 38
		$itemTimezone = $itemExpiry->getTimezone();
370 38
		$now          = new \DateTime('now', $itemTimezone);
371 38
		$interval     = $now->diff($itemExpiry);
372
373 38
		return (int) $interval->format('%i') * 60;
374
	}
375
376
	/**
377
	 * Generate the values for the PSR-16 getMultiple method
378
	 *
379
	 * @param   mixed  $default  Default value to return for keys that do not exist.
380
	 * @param   array  $items    The items to process
381
	 *
382
	 * @return  \Generator
383
	 *
384
	 * @since   __DEPLOY_VERSION__
385
	 */
386 7
	private function generateValues($default, $items)
387
	{
388
		/** @var $item CacheItemInterface */
389 7
		foreach ($items as $key => $item)
390
		{
391 7
			if (!$item->isHit())
392 7
			{
393 7
				yield $key => $default;
394 7
			}
395
			else
396
			{
397 6
				yield $key => $item->get();
398
			}
399 7
		}
400 7
	}
401
}
402