Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,30 @@ or not the item has been removed from cache.

### ArrayCache

The `ArrayCache` provides an in-memory implementation of the
[`CacheInterface`](#cacheinterface).
The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface).

```php
$cache = new ArrayCache();

$cache->set('foo', 'bar');
```

Its constructor accepts an optional `?int $limit` parameter to limit the
maximum number of entries to store in the LRU cache. If you add more
entries to this instance, it will automatically take care of removing
the one that was least recently used (LRU).

For example, this snippet will overwrite the first value and only store
the last two entries:

```php
$cache = new ArrayCache(2);

$cache->set('foo', '1');
$cache->set('bar', '2');
$cache->set('baz', '3');
```

## Common usage

### Fallback get
Expand Down
49 changes: 48 additions & 1 deletion src/ArrayCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,67 @@

class ArrayCache implements CacheInterface
{
private $limit;
private $data = array();

/**
* The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface).
*
* ```php
* $cache = new ArrayCache();
*
* $cache->set('foo', 'bar');
* ```
*
* Its constructor accepts an optional `?int $limit` parameter to limit the
* maximum number of entries to store in the LRU cache. If you add more
* entries to this instance, it will automatically take care of removing
* the one that was least recently used (LRU).
*
* For example, this snippet will overwrite the first value and only store
* the last two entries:
*
* ```php
* $cache = new ArrayCache(2);
*
* $cache->set('foo', '1');
* $cache->set('bar', '2');
* $cache->set('baz', '3');
* ```
*
* @param int|null $limit maximum number of entries to store in the LRU cache
*/
public function __construct($limit = null)
{
$this->limit = $limit;
}

public function get($key)
{
if (!isset($this->data[$key])) {
return Promise\resolve();
}

return Promise\resolve($this->data[$key]);
// remove and append to end of array to keep track of LRU info
$value = $this->data[$key];
unset($this->data[$key]);
$this->data[$key] = $value;

return Promise\resolve($value);
}

public function set($key, $value)
{
// unset before setting to ensure this entry will be added to end of array
unset($this->data[$key]);
$this->data[$key] = $value;

// ensure size limit is not exceeded or remove first entry from array
if ($this->limit !== null && count($this->data) > $this->limit) {
reset($this->data);
unset($this->data[key($this->data)]);
}

return Promise\resolve(true);
}

Expand Down
48 changes: 48 additions & 0 deletions tests/ArrayCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,52 @@ public function removeShouldRemoveKey()
$this->expectCallableNever()
);
}

public function testLimitSizeToZeroDoesNotStoreAnyData()
{
$this->cache = new ArrayCache(0);

$this->cache->set('foo', 'bar');

$this->cache->get('foo')->then($this->expectCallableOnceWith(null));
}

public function testLimitSizeToOneWillOnlyReturnLastWrite()
{
$this->cache = new ArrayCache(1);

$this->cache->set('foo', '1');
$this->cache->set('bar', '2');

$this->cache->get('foo')->then($this->expectCallableOnceWith(null));
$this->cache->get('bar')->then($this->expectCallableOnceWith('2'));
}

public function testOverwriteWithLimitedSizeWillUpdateLRUInfo()
{
$this->cache = new ArrayCache(2);

$this->cache->set('foo', '1');
$this->cache->set('bar', '2');
$this->cache->set('foo', '3');
$this->cache->set('baz', '4');

$this->cache->get('foo')->then($this->expectCallableOnceWith('3'));
$this->cache->get('bar')->then($this->expectCallableOnceWith(null));
$this->cache->get('baz')->then($this->expectCallableOnceWith('4'));
}

public function testGetWithLimitedSizeWillUpdateLRUInfo()
{
$this->cache = new ArrayCache(2);

$this->cache->set('foo', '1');
$this->cache->set('bar', '2');
$this->cache->get('foo')->then($this->expectCallableOnceWith('1'));
$this->cache->set('baz', '3');

$this->cache->get('foo')->then($this->expectCallableOnceWith('1'));
$this->cache->get('bar')->then($this->expectCallableOnceWith(null));
$this->cache->get('baz')->then($this->expectCallableOnceWith('3'));
}
}
11 changes: 11 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ protected function expectCallableOnce()
return $mock;
}

protected function expectCallableOnceWith($param)
{
$mock = $this->createCallableMock();
$mock
->expects($this->once())
->method('__invoke')
->with($param);

return $mock;
}

protected function expectCallableNever()
{
$mock = $this->createCallableMock();
Expand Down