| 1 |
liveuser |
1 |
# Cache
|
|
|
2 |
|
|
|
3 |
[](https://travis-ci.org/reactphp/cache)
|
|
|
4 |
|
|
|
5 |
Async, [Promise](https://github.com/reactphp/promise)-based cache interface
|
|
|
6 |
for [ReactPHP](https://reactphp.org/).
|
|
|
7 |
|
|
|
8 |
The cache component provides a
|
|
|
9 |
[Promise](https://github.com/reactphp/promise)-based
|
|
|
10 |
[`CacheInterface`](#cacheinterface) and an in-memory [`ArrayCache`](#arraycache)
|
|
|
11 |
implementation of that.
|
|
|
12 |
This allows consumers to type hint against the interface and third parties to
|
|
|
13 |
provide alternate implementations.
|
|
|
14 |
This project is heavily inspired by
|
|
|
15 |
[PSR-16: Common Interface for Caching Libraries](https://www.php-fig.org/psr/psr-16/),
|
|
|
16 |
but uses an interface more suited for async, non-blocking applications.
|
|
|
17 |
|
|
|
18 |
**Table of Contents**
|
|
|
19 |
|
|
|
20 |
* [Usage](#usage)
|
|
|
21 |
* [CacheInterface](#cacheinterface)
|
|
|
22 |
* [get()](#get)
|
|
|
23 |
* [set()](#set)
|
|
|
24 |
* [delete()](#delete)
|
|
|
25 |
* [getMultiple()](#getmultiple)
|
|
|
26 |
* [setMultiple()](#setmultiple)
|
|
|
27 |
* [deleteMultiple()](#deletemultiple)
|
|
|
28 |
* [clear()](#clear)
|
|
|
29 |
* [has()](#has)
|
|
|
30 |
* [ArrayCache](#arraycache)
|
|
|
31 |
* [Common usage](#common-usage)
|
|
|
32 |
* [Fallback get](#fallback-get)
|
|
|
33 |
* [Fallback-get-and-set](#fallback-get-and-set)
|
|
|
34 |
* [Install](#install)
|
|
|
35 |
* [Tests](#tests)
|
|
|
36 |
* [License](#license)
|
|
|
37 |
|
|
|
38 |
## Usage
|
|
|
39 |
|
|
|
40 |
### CacheInterface
|
|
|
41 |
|
|
|
42 |
The `CacheInterface` describes the main interface of this component.
|
|
|
43 |
This allows consumers to type hint against the interface and third parties to
|
|
|
44 |
provide alternate implementations.
|
|
|
45 |
|
|
|
46 |
#### get()
|
|
|
47 |
|
|
|
48 |
The `get(string $key, mixed $default = null): PromiseInterface<mixed>` method can be used to
|
|
|
49 |
retrieve an item from the cache.
|
|
|
50 |
|
|
|
51 |
This method will resolve with the cached value on success or with the
|
|
|
52 |
given `$default` value when no item can be found or when an error occurs.
|
|
|
53 |
Similarly, an expired cache item (once the time-to-live is expired) is
|
|
|
54 |
considered a cache miss.
|
|
|
55 |
|
|
|
56 |
```php
|
|
|
57 |
$cache
|
|
|
58 |
->get('foo')
|
|
|
59 |
->then('var_dump');
|
|
|
60 |
```
|
|
|
61 |
|
|
|
62 |
This example fetches the value of the key `foo` and passes it to the
|
|
|
63 |
`var_dump` function. You can use any of the composition provided by
|
|
|
64 |
[promises](https://github.com/reactphp/promise).
|
|
|
65 |
|
|
|
66 |
#### set()
|
|
|
67 |
|
|
|
68 |
The `set(string $key, mixed $value, ?float $ttl = null): PromiseInterface<bool>` method can be used to
|
|
|
69 |
store an item in the cache.
|
|
|
70 |
|
|
|
71 |
This method will resolve with `true` on success or `false` when an error
|
|
|
72 |
occurs. If the cache implementation has to go over the network to store
|
|
|
73 |
it, it may take a while.
|
|
|
74 |
|
|
|
75 |
The optional `$ttl` parameter sets the maximum time-to-live in seconds
|
|
|
76 |
for this cache item. If this parameter is omitted (or `null`), the item
|
|
|
77 |
will stay in the cache for as long as the underlying implementation
|
|
|
78 |
supports. Trying to access an expired cache item results in a cache miss,
|
|
|
79 |
see also [`get()`](#get).
|
|
|
80 |
|
|
|
81 |
```php
|
|
|
82 |
$cache->set('foo', 'bar', 60);
|
|
|
83 |
```
|
|
|
84 |
|
|
|
85 |
This example eventually sets the value of the key `foo` to `bar`. If it
|
|
|
86 |
already exists, it is overridden.
|
|
|
87 |
|
|
|
88 |
This interface does not enforce any particular TTL resolution, so special
|
|
|
89 |
care may have to be taken if you rely on very high precision with
|
|
|
90 |
millisecond accuracy or below. Cache implementations SHOULD work on a
|
|
|
91 |
best effort basis and SHOULD provide at least second accuracy unless
|
|
|
92 |
otherwise noted. Many existing cache implementations are known to provide
|
|
|
93 |
microsecond or millisecond accuracy, but it's generally not recommended
|
|
|
94 |
to rely on this high precision.
|
|
|
95 |
|
|
|
96 |
This interface suggests that cache implementations SHOULD use a monotonic
|
|
|
97 |
time source if available. Given that a monotonic time source is only
|
|
|
98 |
available as of PHP 7.3 by default, cache implementations MAY fall back
|
|
|
99 |
to using wall-clock time.
|
|
|
100 |
While this does not affect many common use cases, this is an important
|
|
|
101 |
distinction for programs that rely on a high time precision or on systems
|
|
|
102 |
that are subject to discontinuous time adjustments (time jumps).
|
|
|
103 |
This means that if you store a cache item with a TTL of 30s and then
|
|
|
104 |
adjust your system time forward by 20s, the cache item SHOULD still
|
|
|
105 |
expire in 30s.
|
|
|
106 |
|
|
|
107 |
#### delete()
|
|
|
108 |
|
|
|
109 |
The `delete(string $key): PromiseInterface<bool>` method can be used to
|
|
|
110 |
delete an item from the cache.
|
|
|
111 |
|
|
|
112 |
This method will resolve with `true` on success or `false` when an error
|
|
|
113 |
occurs. When no item for `$key` is found in the cache, it also resolves
|
|
|
114 |
to `true`. If the cache implementation has to go over the network to
|
|
|
115 |
delete it, it may take a while.
|
|
|
116 |
|
|
|
117 |
```php
|
|
|
118 |
$cache->delete('foo');
|
|
|
119 |
```
|
|
|
120 |
|
|
|
121 |
This example eventually deletes the key `foo` from the cache. As with
|
|
|
122 |
`set()`, this may not happen instantly and a promise is returned to
|
|
|
123 |
provide guarantees whether or not the item has been removed from cache.
|
|
|
124 |
|
|
|
125 |
#### getMultiple()
|
|
|
126 |
|
|
|
127 |
The `getMultiple(string[] $keys, mixed $default = null): PromiseInterface<array>` method can be used to
|
|
|
128 |
retrieve multiple cache items by their unique keys.
|
|
|
129 |
|
|
|
130 |
This method will resolve with an array of cached values on success or with the
|
|
|
131 |
given `$default` value when an item can not be found or when an error occurs.
|
|
|
132 |
Similarly, an expired cache item (once the time-to-live is expired) is
|
|
|
133 |
considered a cache miss.
|
|
|
134 |
|
|
|
135 |
```php
|
|
|
136 |
$cache->getMultiple(array('name', 'age'))->then(function (array $values) {
|
|
|
137 |
$name = $values['name'] ?? 'User';
|
|
|
138 |
$age = $values['age'] ?? 'n/a';
|
|
|
139 |
|
|
|
140 |
echo $name . ' is ' . $age . PHP_EOL;
|
|
|
141 |
});
|
|
|
142 |
```
|
|
|
143 |
|
|
|
144 |
This example fetches the cache items for the `name` and `age` keys and
|
|
|
145 |
prints some example output. You can use any of the composition provided
|
|
|
146 |
by [promises](https://github.com/reactphp/promise).
|
|
|
147 |
|
|
|
148 |
#### setMultiple()
|
|
|
149 |
|
|
|
150 |
The `setMultiple(array $values, ?float $ttl = null): PromiseInterface<bool>` method can be used to
|
|
|
151 |
persist a set of key => value pairs in the cache, with an optional TTL.
|
|
|
152 |
|
|
|
153 |
This method will resolve with `true` on success or `false` when an error
|
|
|
154 |
occurs. If the cache implementation has to go over the network to store
|
|
|
155 |
it, it may take a while.
|
|
|
156 |
|
|
|
157 |
The optional `$ttl` parameter sets the maximum time-to-live in seconds
|
|
|
158 |
for these cache items. If this parameter is omitted (or `null`), these items
|
|
|
159 |
will stay in the cache for as long as the underlying implementation
|
|
|
160 |
supports. Trying to access an expired cache items results in a cache miss,
|
|
|
161 |
see also [`getMultiple()`](#getmultiple).
|
|
|
162 |
|
|
|
163 |
```php
|
|
|
164 |
$cache->setMultiple(array('foo' => 1, 'bar' => 2), 60);
|
|
|
165 |
```
|
|
|
166 |
|
|
|
167 |
This example eventually sets the list of values - the key `foo` to `1` value
|
|
|
168 |
and the key `bar` to `2`. If some of the keys already exist, they are overridden.
|
|
|
169 |
|
|
|
170 |
#### deleteMultiple()
|
|
|
171 |
|
|
|
172 |
The `setMultiple(string[] $keys): PromiseInterface<bool>` method can be used to
|
|
|
173 |
delete multiple cache items in a single operation.
|
|
|
174 |
|
|
|
175 |
This method will resolve with `true` on success or `false` when an error
|
|
|
176 |
occurs. When no items for `$keys` are found in the cache, it also resolves
|
|
|
177 |
to `true`. If the cache implementation has to go over the network to
|
|
|
178 |
delete it, it may take a while.
|
|
|
179 |
|
|
|
180 |
```php
|
|
|
181 |
$cache->deleteMultiple(array('foo', 'bar, 'baz'));
|
|
|
182 |
```
|
|
|
183 |
|
|
|
184 |
This example eventually deletes keys `foo`, `bar` and `baz` from the cache.
|
|
|
185 |
As with `setMultiple()`, this may not happen instantly and a promise is returned to
|
|
|
186 |
provide guarantees whether or not the item has been removed from cache.
|
|
|
187 |
|
|
|
188 |
#### clear()
|
|
|
189 |
|
|
|
190 |
The `clear(): PromiseInterface<bool>` method can be used to
|
|
|
191 |
wipe clean the entire cache.
|
|
|
192 |
|
|
|
193 |
This method will resolve with `true` on success or `false` when an error
|
|
|
194 |
occurs. If the cache implementation has to go over the network to
|
|
|
195 |
delete it, it may take a while.
|
|
|
196 |
|
|
|
197 |
```php
|
|
|
198 |
$cache->clear();
|
|
|
199 |
```
|
|
|
200 |
|
|
|
201 |
This example eventually deletes all keys from the cache. As with `deleteMultiple()`,
|
|
|
202 |
this may not happen instantly and a promise is returned to provide guarantees
|
|
|
203 |
whether or not all the items have been removed from cache.
|
|
|
204 |
|
|
|
205 |
#### has()
|
|
|
206 |
|
|
|
207 |
The `has(string $key): PromiseInterface<bool>` method can be used to
|
|
|
208 |
determine whether an item is present in the cache.
|
|
|
209 |
|
|
|
210 |
This method will resolve with `true` on success or `false` when no item can be found
|
|
|
211 |
or when an error occurs. Similarly, an expired cache item (once the time-to-live
|
|
|
212 |
is expired) is considered a cache miss.
|
|
|
213 |
|
|
|
214 |
```php
|
|
|
215 |
$cache
|
|
|
216 |
->has('foo')
|
|
|
217 |
->then('var_dump');
|
|
|
218 |
```
|
|
|
219 |
|
|
|
220 |
This example checks if the value of the key `foo` is set in the cache and passes
|
|
|
221 |
the result to the `var_dump` function. You can use any of the composition provided by
|
|
|
222 |
[promises](https://github.com/reactphp/promise).
|
|
|
223 |
|
|
|
224 |
NOTE: It is recommended that has() is only to be used for cache warming type purposes
|
|
|
225 |
and not to be used within your live applications operations for get/set, as this method
|
|
|
226 |
is subject to a race condition where your has() will return true and immediately after,
|
|
|
227 |
another script can remove it making the state of your app out of date.
|
|
|
228 |
|
|
|
229 |
### ArrayCache
|
|
|
230 |
|
|
|
231 |
The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface).
|
|
|
232 |
|
|
|
233 |
```php
|
|
|
234 |
$cache = new ArrayCache();
|
|
|
235 |
|
|
|
236 |
$cache->set('foo', 'bar');
|
|
|
237 |
```
|
|
|
238 |
|
|
|
239 |
Its constructor accepts an optional `?int $limit` parameter to limit the
|
|
|
240 |
maximum number of entries to store in the LRU cache. If you add more
|
|
|
241 |
entries to this instance, it will automatically take care of removing
|
|
|
242 |
the one that was least recently used (LRU).
|
|
|
243 |
|
|
|
244 |
For example, this snippet will overwrite the first value and only store
|
|
|
245 |
the last two entries:
|
|
|
246 |
|
|
|
247 |
```php
|
|
|
248 |
$cache = new ArrayCache(2);
|
|
|
249 |
|
|
|
250 |
$cache->set('foo', '1');
|
|
|
251 |
$cache->set('bar', '2');
|
|
|
252 |
$cache->set('baz', '3');
|
|
|
253 |
```
|
|
|
254 |
|
|
|
255 |
This cache implementation is known to rely on wall-clock time to schedule
|
|
|
256 |
future cache expiration times when using any version before PHP 7.3,
|
|
|
257 |
because a monotonic time source is only available as of PHP 7.3 (`hrtime()`).
|
|
|
258 |
While this does not affect many common use cases, this is an important
|
|
|
259 |
distinction for programs that rely on a high time precision or on systems
|
|
|
260 |
that are subject to discontinuous time adjustments (time jumps).
|
|
|
261 |
This means that if you store a cache item with a TTL of 30s on PHP < 7.3
|
|
|
262 |
and then adjust your system time forward by 20s, the cache item may
|
|
|
263 |
expire in 10s. See also [`set()`](#set) for more details.
|
|
|
264 |
|
|
|
265 |
## Common usage
|
|
|
266 |
|
|
|
267 |
### Fallback get
|
|
|
268 |
|
|
|
269 |
A common use case of caches is to attempt fetching a cached value and as a
|
|
|
270 |
fallback retrieve it from the original data source if not found. Here is an
|
|
|
271 |
example of that:
|
|
|
272 |
|
|
|
273 |
```php
|
|
|
274 |
$cache
|
|
|
275 |
->get('foo')
|
|
|
276 |
->then(function ($result) {
|
|
|
277 |
if ($result === null) {
|
|
|
278 |
return getFooFromDb();
|
|
|
279 |
}
|
|
|
280 |
|
|
|
281 |
return $result;
|
|
|
282 |
})
|
|
|
283 |
->then('var_dump');
|
|
|
284 |
```
|
|
|
285 |
|
|
|
286 |
First an attempt is made to retrieve the value of `foo`. A callback function is
|
|
|
287 |
registered that will call `getFooFromDb` when the resulting value is null.
|
|
|
288 |
`getFooFromDb` is a function (can be any PHP callable) that will be called if the
|
|
|
289 |
key does not exist in the cache.
|
|
|
290 |
|
|
|
291 |
`getFooFromDb` can handle the missing key by returning a promise for the
|
|
|
292 |
actual value from the database (or any other data source). As a result, this
|
|
|
293 |
chain will correctly fall back, and provide the value in both cases.
|
|
|
294 |
|
|
|
295 |
### Fallback get and set
|
|
|
296 |
|
|
|
297 |
To expand on the fallback get example, often you want to set the value on the
|
|
|
298 |
cache after fetching it from the data source.
|
|
|
299 |
|
|
|
300 |
```php
|
|
|
301 |
$cache
|
|
|
302 |
->get('foo')
|
|
|
303 |
->then(function ($result) {
|
|
|
304 |
if ($result === null) {
|
|
|
305 |
return $this->getAndCacheFooFromDb();
|
|
|
306 |
}
|
|
|
307 |
|
|
|
308 |
return $result;
|
|
|
309 |
})
|
|
|
310 |
->then('var_dump');
|
|
|
311 |
|
|
|
312 |
public function getAndCacheFooFromDb()
|
|
|
313 |
{
|
|
|
314 |
return $this->db
|
|
|
315 |
->get('foo')
|
|
|
316 |
->then(array($this, 'cacheFooFromDb'));
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
public function cacheFooFromDb($foo)
|
|
|
320 |
{
|
|
|
321 |
$this->cache->set('foo', $foo);
|
|
|
322 |
|
|
|
323 |
return $foo;
|
|
|
324 |
}
|
|
|
325 |
```
|
|
|
326 |
|
|
|
327 |
By using chaining you can easily conditionally cache the value if it is
|
|
|
328 |
fetched from the database.
|
|
|
329 |
|
|
|
330 |
## Install
|
|
|
331 |
|
|
|
332 |
The recommended way to install this library is [through Composer](https://getcomposer.org).
|
|
|
333 |
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
|
|
|
334 |
|
|
|
335 |
This project follows [SemVer](https://semver.org/).
|
|
|
336 |
This will install the latest supported version:
|
|
|
337 |
|
|
|
338 |
```bash
|
|
|
339 |
$ composer require react/cache:^1.1
|
|
|
340 |
```
|
|
|
341 |
|
|
|
342 |
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
|
|
|
343 |
|
|
|
344 |
This project aims to run on any platform and thus does not require any PHP
|
|
|
345 |
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
|
|
|
346 |
HHVM.
|
|
|
347 |
It's *highly recommended to use PHP 7+* for this project.
|
|
|
348 |
|
|
|
349 |
## Tests
|
|
|
350 |
|
|
|
351 |
To run the test suite, you first need to clone this repo and then install all
|
|
|
352 |
dependencies [through Composer](https://getcomposer.org):
|
|
|
353 |
|
|
|
354 |
```bash
|
|
|
355 |
$ composer install
|
|
|
356 |
```
|
|
|
357 |
|
|
|
358 |
To run the test suite, go to the project root and run:
|
|
|
359 |
|
|
|
360 |
```bash
|
|
|
361 |
$ php vendor/bin/phpunit
|
|
|
362 |
```
|
|
|
363 |
|
|
|
364 |
## License
|
|
|
365 |
|
|
|
366 |
MIT, see [LICENSE file](LICENSE).
|