Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6fee3ea
Add memcache memoize support.
s-sathish May 12, 2019
1cb275f
Use star for ext-memcache in composer.json
s-sathish May 12, 2019
ae8a491
Add test case for memcache memoize, address review comments.
s-sathish May 14, 2019
0541038
Added back ext-json requirement
s-sathish May 14, 2019
56380ff
Update travis ci file to enable support for memcache
s-sathish May 14, 2019
06d0eb2
Update travis ci file to enable support for memcache
s-sathish May 14, 2019
c119d44
Remove the config-add command.
s-sathish May 14, 2019
c914269
Try to install php-memcache extension before_script
s-sathish May 14, 2019
8b50ff3
Try to install php-memcache extension before_script
s-sathish May 14, 2019
0224353
Try different method to make it work.
s-sathish May 14, 2019
338c3bc
Travis file changes.
s-sathish May 15, 2019
44ebc38
Merge branch 'traderinteractive:master' into master
s-sathish Feb 23, 2022
ecc8795
Changes for build environment fixes
s-sathish Feb 23, 2022
f6444e0
Merge branch 'master' into master-memcache
chrisryan Apr 21, 2025
01b9c9d
Update src/Memcache.php
chrisryan Apr 21, 2025
a583758
Update tests/MemcacheTest.php
chrisryan Apr 21, 2025
eb60f59
Update src/Memcache.php
chrisryan Apr 21, 2025
d64748b
Handle Memcache tests across php versions.
chrisryan Apr 21, 2025
28038a5
Add newer versions of PHP to test.
chrisryan Apr 21, 2025
7b2ce7f
Simplify the tests to avoid using require
chrisryan Apr 21, 2025
226ecb3
Remove older build related configurations no longer used.
chrisryan Apr 21, 2025
6d11f67
Update all tests to be in the TraderInteractiveTest\Memoize namespace.
chrisryan Apr 21, 2025
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
3 changes: 0 additions & 3 deletions .coveralls.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/.github export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis export-ignore
/.*.yml export-ignore
/phpcs.xml export-ignore
/phpunit.xml.dist export-ignore
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
22 changes: 0 additions & 22 deletions .scrutinizer.yml

This file was deleted.

18 changes: 0 additions & 18 deletions .travis.yml

This file was deleted.

22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,28 @@ $compute = function() {
$result = $memoize->memoizeCallable('myLongOperation', $compute, 3600);
```

### Memcache
The memcache provider uses the [memcache](https://www.php.net/manual/en/book.memcache.php) library to
cache the results in Memcache. It supports the `$cacheTime` parameter so that
results can be recomputed after the time expires.

This memoizer can be used in a way that makes it persistent between processes
rather than only caching computation for the current process.

#### Example
```php
$memcache = new Memcache;
$memcacheInstance = $memcache->connect('127.0.0.1', 11211);
$memoize = new \TraderInteractive\Memoize\Memcache($memcacheInstance);

$compute = function() {
// Perform some long operation that you want to memoize
};

// Cache he results of $compute for 1 hour.
$result = $memoize->memoizeCallable('myLongOperation', $compute, 3600);
```

### Memory
This is a standard in-memory memoizer. It does not support `$cacheTime` at the
moment and only keeps the results around as long as the memoizer is in memory.
Expand Down
15 changes: 12 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,29 @@
"sort-packages": true
},
"require": {
"php": "^7.0 || ^8.0"
"php": "^7.0 || ^8.0",
"ext-json": "*"
},
"require-dev": {
"php-coveralls/php-coveralls": "^1.0",
"phpunit/phpunit": "^6.0 || ^7.0 || ^8.0 || ^9.0",
"predis/predis": "^1.0",
"squizlabs/php_codesniffer": "^3.2"
"squizlabs/php_codesniffer": "^3.2",
"ext-memcache": "*",
"ext-memcached": "*"
},
"suggest": {
"predis/predis": "Allows for Redis-based memoization."
"predis/predis": "Allows for Redis-based memoization.",
"ext-memcache": "Allows for Memcache-based memoization.",
"ext-memcached": "Allows for Memcache-based memoization."
},
"autoload": {
"psr-4": { "TraderInteractive\\Memoize\\": "src/" }
},
"autoload-dev": {
"psr-4": { "TraderInteractiveTest\\Memoize\\": "tests/" }
},

"scripts": {
"lint": "vendor/bin/phpcs",
"test": "vendor/bin/phpunit"
Expand Down
95 changes: 95 additions & 0 deletions src/Memcache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace TraderInteractive\Memoize;

/**
* A memoizer that caches the results in memcache.
*/
class Memcache implements Memoize
{
/**
* The memcache client
*
* @var \Memcache
*/
private $client;

/**
* Cache refresh
*
* @var boolean
*/
private $refresh;

/**
* Sets the memcache client.
*
* @param \Memcache $client The memcache client to use
* @param boolean $refresh If true we will always overwrite cache even if it is already set
*/
public function __construct(\Memcache $client, bool $refresh = false)
{
$this->client = $client;
$this->refresh = $refresh;
}

/**
* The value is stored in memcache as a json_encoded string,
* so make sure that the value you return from $compute is json-encode-able.
*
* @see Memoize::memoizeCallable
*
* @param string $key
* @param callable $compute
* @param int|null $cacheTime
* @param bool $refresh
*
* @return mixed
*/
public function memoizeCallable(string $key, callable $compute, int $cacheTime = null, bool $refresh = false)
{
if (!$this->refresh && !$refresh) {
try {
$cached = $this->client->get($key, $flags, $flags);
if ($cached !== false && $cached != null) {
$data = json_decode($cached, true);
return $data['result'];
}
} catch (\Exception $e) {
return call_user_func($compute);
}
}

$result = call_user_func($compute);

// If the result is false/null/empty, then there is no point in storing it in cache.
if ($result === false || $result == null || empty($result)) {
return $result;
}

$this->cache($key, json_encode(['result' => $result]), $cacheTime);

return $result;
}

/**
* Caches the value into memcache with errors suppressed.
*
* @param string $key The key.
* @param string $value The value.
* @param int $cacheTime The optional cache time
*
* @return void
*/
private function cache(string $key, string $value, int $cacheTime = null)
{
try {
$this->client->set($key, $value, 0, $cacheTime);
} catch (\Exception $e) {
// We don't want exceptions in accessing the cache to break functionality.
// The cache should be as transparent as possible.
// If insight is needed into these exceptions,
// a better way would be by notifying an observer with the errors.
}
}
}
10 changes: 10 additions & 0 deletions tests/MemcacheMockable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace TraderInteractiveTest\Memoize;

class MemcacheMockable extends \Memcache
{
public function get($name, &$flags, &$cas)
{
}
}
137 changes: 137 additions & 0 deletions tests/MemcacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

namespace TraderInteractiveTest\Memoize;

use PHPUnit\Framework\TestCase;
use TraderInteractive\Memoize\Memcache;

/**
* @coversDefaultClass \TraderInteractive\Memoize\Memcache
* @covers ::<private>
*/
class MemcacheTest extends TestCase
{
/**
* @test
* @covers ::__construct
* @covers ::memoizeCallable
*/
public function memoizeCallableWithCachedValue()
{
$count = 0;
$key = 'foo';
$value = 'bar';
$cachedValue = json_encode(['result' => $value]);
$compute = function () use (&$count, $value) {
$count++;

return $value;
};


$client = $this->getMemcacheMock();
$client->expects(
$this->once()
)->method('get')->with($this->equalTo($key))->will($this->returnValue($cachedValue));

$memoizer = new Memcache($client);

$this->assertSame($value, $memoizer->memoizeCallable($key, $compute));
$this->assertSame(0, $count);
}

/**
* @test
* @covers ::__construct
* @covers ::memoizeCallable
*/
public function memoizeCallableWithExceptionOnGet()
{
$count = 0;
$key = 'foo';
$value = 'bar';
$compute = function () use (&$count, $value) {
$count++;

return $value;
};

$client = $this->getMemcacheMock();
$client->expects(
$this->once()
)->method('get')->with($this->equalTo($key))->will($this->throwException(new \Exception()));

$memoizer = new Memcache($client);

$this->assertSame($value, $memoizer->memoizeCallable($key, $compute));
$this->assertSame(1, $count);
}

/**
* @test
* @covers ::__construct
* @covers ::memoizeCallable
*/
public function memoizeCallableWithUncachedKey()
{
$count = 0;
$key = 'foo';
$value = 'bar';
$cachedValue = json_encode(['result' => $value]);
$cacheTime = 1234;
$compute = function () use (&$count, $value) {
$count++;

return $value;
};

$client = $this->getMemcacheMock();
$client->expects($this->once())->method('get')->with($this->equalTo($key))->will($this->returnValue(null));
$client->expects($this->once())->method('set')
->with($this->equalTo($key), $this->equalTo($cachedValue), $this->equalTo(0), $this->equalTo($cacheTime));

$memoizer = new Memcache($client);

$this->assertSame($value, $memoizer->memoizeCallable($key, $compute, $cacheTime));
$this->assertSame(1, $count);
}

/**
* @test
* @covers ::__construct
* @covers ::memoizeCallable
*/
public function memoizeCallableWithUncachedKeyWithExceptionOnSet()
{
$count = 0;
$key = 'foo';
$value = 'bar';
$cachedValue = json_encode(['result' => $value]);
$compute = function () use (&$count, $value) {
$count++;

return $value;
};

$client = $this->getMemcacheMock();
$client->expects(
$this->once()
)->method('get')->with($this->equalTo($key))->will($this->returnValue(null));
$setExpectation = $client->expects(
$this->once()
)->method('set')->with($this->equalTo($key), $this->equalTo($cachedValue));
$setExpectation->will($this->throwException(new \Exception()));

$memoizer = new Memcache($client);

$this->assertSame($value, $memoizer->memoizeCallable($key, $compute));
$this->assertSame(1, $count);
}

public function getMemcacheMock() : \Memcache
{
$isOlderPHPVersion = PHP_VERSION_ID < 80000;
$memcacheClass = $isOlderPHPVersion ? MemcacheMockable::class : \Memcache::class;
return $this->getMockBuilder($memcacheClass)->setMethods(['get', 'set'])->getMock();
}
}
3 changes: 2 additions & 1 deletion tests/MemoryTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

namespace TraderInteractive\Memoize;
namespace TraderInteractiveTest\Memoize;

use PHPUnit\Framework\TestCase;
use TraderInteractive\Memoize\Memory;

/**
* @coversDefaultClass \TraderInteractive\Memoize\Memory
Expand Down
3 changes: 2 additions & 1 deletion tests/NoneTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

namespace TraderInteractive\Memoize;
namespace TraderInteractiveTest\Memoize;

use PHPUnit\Framework\TestCase;
use TraderInteractive\Memoize\None;

/**
* @coversDefaultClass \TraderInteractive\Memoize\None
Expand Down
Loading