From 6fee3eafc6a72832a0b728a61495df2c14bfdf10 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Sun, 12 May 2019 12:32:52 +0530 Subject: [PATCH 01/15] Add memcache memoize support. --- README.md | 22 ++++++++++++ composer.json | 4 ++- src/Memcache.php | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/Memcache.php diff --git a/README.md b/README.md index e59577d..377e798 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/composer.json b/composer.json index ad8ca81..78490e1 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,9 @@ "sort-packages": true }, "require": { - "php": "^7.0" + "php": "^7.0", + "ext-memcache": "^3.0.8", + "ext-json": "*" }, "require-dev": { "php-coveralls/php-coveralls": "^1.0", diff --git a/src/Memcache.php b/src/Memcache.php new file mode 100644 index 0000000..a660e22 --- /dev/null +++ b/src/Memcache.php @@ -0,0 +1,93 @@ +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 + * + * @return mixed + */ + public function memoizeCallable(string $key, callable $compute, int $cacheTime = null) + { + if (!$this->refresh) { + try { + $cached = $this->client->get($key); + if ($cached !== false) { + $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. + } + } +} From 1cb275fa630e1b7201c6258e22362aa8d69f1768 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Sun, 12 May 2019 12:44:00 +0530 Subject: [PATCH 02/15] Use star for ext-memcache in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 78490e1..4545124 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ }, "require": { "php": "^7.0", - "ext-memcache": "^3.0.8", + "ext-memcache": "*", "ext-json": "*" }, "require-dev": { From ae8a491300885b345f600d0c2130622575319ca3 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 01:25:27 +0530 Subject: [PATCH 03/15] Add test case for memcache memoize, address review comments. --- composer.json | 10 ++-- src/Memcache.php | 4 +- tests/MemcacheTest.php | 133 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 tests/MemcacheTest.php diff --git a/composer.json b/composer.json index 4545124..2e5aa32 100644 --- a/composer.json +++ b/composer.json @@ -14,18 +14,18 @@ "sort-packages": true }, "require": { - "php": "^7.0", - "ext-memcache": "*", - "ext-json": "*" + "php": "^7.0" }, "require-dev": { "php-coveralls/php-coveralls": "^1.0", "phpunit/phpunit": "^6.0", "predis/predis": "~1.0", - "squizlabs/php_codesniffer": "^3.2" + "squizlabs/php_codesniffer": "^3.2", + "ext-memcache": "*" }, "suggest": { - "predis/predis": "Allows for Redis-based memoization." + "predis/predis": "Allows for Redis-based memoization.", + "ext-memcache": "Allows for Memcache-based memoization." }, "autoload": { "psr-4": { "TraderInteractive\\Memoize\\": "src/" } diff --git a/src/Memcache.php b/src/Memcache.php index a660e22..dfd6868 100644 --- a/src/Memcache.php +++ b/src/Memcache.php @@ -49,8 +49,8 @@ public function memoizeCallable(string $key, callable $compute, int $cacheTime = { if (!$this->refresh) { try { - $cached = $this->client->get($key); - if ($cached !== false) { + $cached = $this->client->get($key, $flags, $flags); + if ($cached !== false && $cached != null) { $data = json_decode($cached, true); return $data['result']; } diff --git a/tests/MemcacheTest.php b/tests/MemcacheTest.php new file mode 100644 index 0000000..c4f13c7 --- /dev/null +++ b/tests/MemcacheTest.php @@ -0,0 +1,133 @@ + + */ +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 + { + return $this->getMockBuilder('\Memcache')->setMethods(['get', 'set'])->getMock(); + } +} From 0541038ecd029585cd41ab8aa93c8153e28f4f70 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 01:37:35 +0530 Subject: [PATCH 04/15] Added back ext-json requirement --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2e5aa32..b35ac14 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "sort-packages": true }, "require": { - "php": "^7.0" + "php": "^7.0", + "ext-json": "*" }, "require-dev": { "php-coveralls/php-coveralls": "^1.0", From 56380ffc7b0ab36791c677cd964ff62d1a52d60c Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 01:58:16 +0530 Subject: [PATCH 05/15] Update travis ci file to enable support for memcache --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 141629e..b97aed8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,11 +7,15 @@ php: env: - PREFER_LOWEST="--prefer-lowest --prefer-stable" - PREFER_LOWEST="" +services: + - memcached matrix: fast_finish: true allow_failures: - php: nightly before_script: + - phpenv config-add memcache.ini + - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: - ./vendor/bin/phpunit --coverage-clover clover.xml From 06d0eb280f761f429186b2e845544bc82048c972 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 02:00:19 +0530 Subject: [PATCH 06/15] Update travis ci file to enable support for memcache --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b97aed8..51a6080 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ matrix: allow_failures: - php: nightly before_script: - - phpenv config-add memcache.ini + - phpenv config-add ./build/php/memcache.ini - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: From c119d44c8bb152016392115c29df55726e4b3dd0 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 02:03:53 +0530 Subject: [PATCH 07/15] Remove the config-add command. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 51a6080..426d662 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ matrix: allow_failures: - php: nightly before_script: - - phpenv config-add ./build/php/memcache.ini - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: From c914269539127c8eb9b8d0e1fdafdde73883f94a Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 02:14:04 +0530 Subject: [PATCH 08/15] Try to install php-memcache extension before_script --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 426d662..c5711f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ matrix: allow_failures: - php: nightly before_script: + - pecl install memcache - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: From 8b50ff3d7812f08eeec150a7d222ed68286deb07 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 02:17:25 +0530 Subject: [PATCH 09/15] Try to install php-memcache extension before_script --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c5711f9..68df1ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ matrix: allow_failures: - php: nightly before_script: - - pecl install memcache + - printf "\n" | pecl install memcache - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: From 022435391e06df99e0ef51a6ea6988e0f019a7a2 Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 02:34:04 +0530 Subject: [PATCH 10/15] Try different method to make it work. --- .travis.yml | 2 +- memcache.ini | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 memcache.ini diff --git a/.travis.yml b/.travis.yml index 68df1ae..b97aed8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ matrix: allow_failures: - php: nightly before_script: - - printf "\n" | pecl install memcache + - phpenv config-add memcache.ini - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: diff --git a/memcache.ini b/memcache.ini new file mode 100644 index 0000000..1043096 --- /dev/null +++ b/memcache.ini @@ -0,0 +1 @@ +extension = "memcache.so" \ No newline at end of file From 338c3bc3c617502113da229df174f8a94f6103fa Mon Sep 17 00:00:00 2001 From: Sathish S Date: Wed, 15 May 2019 21:10:13 +0530 Subject: [PATCH 11/15] Travis file changes. --- .travis.yml | 1 - memcache.ini | 1 - 2 files changed, 2 deletions(-) delete mode 100644 memcache.ini diff --git a/.travis.yml b/.travis.yml index b97aed8..426d662 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ matrix: allow_failures: - php: nightly before_script: - - phpenv config-add memcache.ini - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer update $PREFER_LOWEST script: diff --git a/memcache.ini b/memcache.ini deleted file mode 100644 index 1043096..0000000 --- a/memcache.ini +++ /dev/null @@ -1 +0,0 @@ -extension = "memcache.so" \ No newline at end of file From ecc8795baa0640f91a4709219c45273dda010ab7 Mon Sep 17 00:00:00 2001 From: s-sathish Date: Wed, 23 Feb 2022 23:59:09 +0530 Subject: [PATCH 12/15] Changes for build environment fixes --- .gitattributes | 1 + .travis.yml | 3 ++- .travis/memcache.ini | 1 + .travis/memcached.ini | 1 + composer.json | 6 ++++-- 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 .travis/memcache.ini create mode 100644 .travis/memcached.ini diff --git a/.gitattributes b/.gitattributes index 6f4c549..c20634c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/.travis.yml b/.travis.yml index 426d662..d15db3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,8 @@ matrix: allow_failures: - php: nightly before_script: - - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - phpenv config-add .travis/memcached.ini + - phpenv config-add .travis/memcache.ini - composer update $PREFER_LOWEST script: - ./vendor/bin/phpunit --coverage-clover clover.xml diff --git a/.travis/memcache.ini b/.travis/memcache.ini new file mode 100644 index 0000000..587455f --- /dev/null +++ b/.travis/memcache.ini @@ -0,0 +1 @@ +extension=memcache.so diff --git a/.travis/memcached.ini b/.travis/memcached.ini new file mode 100644 index 0000000..7d09664 --- /dev/null +++ b/.travis/memcached.ini @@ -0,0 +1 @@ +extension=memcached.so diff --git a/composer.json b/composer.json index b35ac14..8e3e7f0 100644 --- a/composer.json +++ b/composer.json @@ -22,11 +22,13 @@ "phpunit/phpunit": "^6.0", "predis/predis": "~1.0", "squizlabs/php_codesniffer": "^3.2", - "ext-memcache": "*" + "ext-memcache": "*", + "ext-memcached": "*" }, "suggest": { "predis/predis": "Allows for Redis-based memoization.", - "ext-memcache": "Allows for Memcache-based memoization." + "ext-memcache": "Allows for Memcache-based memoization.", + "ext-memcached": "Allows for Memcache-based memoization." }, "autoload": { "psr-4": { "TraderInteractive\\Memoize\\": "src/" } From 01b9c9d3d16b6c329e092392e61f2ee929992d71 Mon Sep 17 00:00:00 2001 From: Chris Ryan <704326+chrisryan@users.noreply.github.com> Date: Mon, 21 Apr 2025 09:10:36 -0400 Subject: [PATCH 13/15] Update src/Memcache.php --- src/Memcache.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Memcache.php b/src/Memcache.php index dfd6868..f9c73fb 100644 --- a/src/Memcache.php +++ b/src/Memcache.php @@ -62,8 +62,9 @@ public function memoizeCallable(string $key, callable $compute, int $cacheTime = $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)) + if ($result === false || $result == null || empty($result)) { return $result; + } $this->cache($key, json_encode(['result' => $result]), $cacheTime); From a5837586546b05a47a5c589c322323aaf91ad4f7 Mon Sep 17 00:00:00 2001 From: Chris Ryan <704326+chrisryan@users.noreply.github.com> Date: Mon, 21 Apr 2025 09:16:23 -0400 Subject: [PATCH 14/15] Update tests/MemcacheTest.php --- tests/MemcacheTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/MemcacheTest.php b/tests/MemcacheTest.php index c4f13c7..c599a23 100644 --- a/tests/MemcacheTest.php +++ b/tests/MemcacheTest.php @@ -86,7 +86,8 @@ public function memoizeCallableWithUncachedKey() $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)); + $client->expects($this->once())->method('set') + ->with($this->equalTo($key), $this->equalTo($cachedValue), $this->equalTo(0), $this->equalTo($cacheTime)); $memoizer = new Memcache($client); From eb60f592a45abbc7143fc79572c983603f84754a Mon Sep 17 00:00:00 2001 From: Chris Ryan <704326+chrisryan@users.noreply.github.com> Date: Mon, 21 Apr 2025 09:22:13 -0400 Subject: [PATCH 15/15] Update src/Memcache.php --- src/Memcache.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Memcache.php b/src/Memcache.php index f9c73fb..cd809d4 100644 --- a/src/Memcache.php +++ b/src/Memcache.php @@ -42,12 +42,13 @@ public function __construct(\Memcache $client, bool $refresh = false) * @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) + public function memoizeCallable(string $key, callable $compute, int $cacheTime = null, bool $refresh = false) { - if (!$this->refresh) { + if (!$this->refresh && !$refresh) { try { $cached = $this->client->get($key, $flags, $flags); if ($cached !== false && $cached != null) {