Last active
December 2, 2016 18:57
-
-
Save davidlukac/1f7fa09ac1649b9b2b892e3df3e2176f to your computer and use it in GitHub Desktop.
FallBackCachItem and Spec
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace NMH\Helper; | |
use Stash\Invalidation; | |
use Tedivm\StashBundle\Service\CacheService; | |
/** | |
* Class FallbackCacheItemRepository | |
* | |
* @package NMH\Helper | |
*/ | |
class FallbackCacheItemRepository | |
{ | |
/** | |
* Helper to retrieve given items from cache, underlying service, or from the fallback. | |
* | |
* @param string $cacheKey Cache key for the item to retrieve. | |
* @param string $fallbackKey Fallback cache key, in case underlying service is unavailable. | |
* @param CacheService $cacheService Cache service to get data from. | |
* @param callable $valueCallback Actual data service to get data from. | |
* @param int $cacheTtl Cache TTL - will be set if greater than 0 (optional). | |
* @param int $invalidationMethod Cache invalidation strategy. | |
* | |
* @return mixed Item retrieved from the cache. | |
*/ | |
public function getValueFromCacheWithFallback( | |
string $cacheKey, | |
string $fallbackKey, | |
CacheService $cacheService, | |
callable $valueCallback, | |
int $cacheTtl = -1, | |
int $invalidationMethod = Invalidation::OLD | |
) { | |
$cacheItem = $cacheService->getItem($cacheKey); | |
$fallback = $cacheService->getItem($fallbackKey); | |
$cacheItem->setInvalidationMethod($invalidationMethod); | |
if ($cacheItem->isHit()) { | |
$retrievedItem = $cacheItem->get(); | |
} else { | |
$retrievedItem = call_user_func($valueCallback); | |
if (empty($retrievedItem)) { | |
if ($fallback->isHit()) { | |
$retrievedItem = $fallback->get(); | |
} | |
} else { | |
$cacheItem->set($retrievedItem); | |
if ($cacheTtl > 0) { | |
$cacheItem->expiresAfter($cacheTtl); | |
} | |
$cacheService->save($cacheItem); | |
$cacheService->save($fallback); | |
} | |
} | |
return $retrievedItem; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace spec\NMH\Helper; | |
use NMH\Helper\FallbackCacheItemRepository; | |
use NMH\SocialIntegrations\DisqusIntegration; | |
use PhpSpec\ObjectBehavior; | |
use Prophecy\Argument; | |
use Prophecy\Prophet; | |
use Stash\Invalidation; | |
use Stash\Item; | |
use Tedivm\StashBundle\Service\CacheService; | |
/** | |
* Class FallbackCacheItemRepositorySpec | |
* | |
* @package spec\NMH\Helper | |
* | |
* @codingStandardsIgnoreStart | |
* @SuppressWarnings(PHPMD) | |
*/ | |
class FallbackCacheItemRepositorySpec extends ObjectBehavior | |
{ | |
// Constants. | |
private $cacheKey = 'disqus/most_discussed_articles'; | |
private $fallbackKey = 'disqus/most_discussed_articles/fallback'; | |
private $cacheTtl = -1; | |
function it_is_initializable() | |
{ | |
$this->shouldHaveType(FallbackCacheItemRepository::class); | |
} | |
function it_retrieves_empty_data_from_the_service(CacheService $cacheService, DisqusIntegration $disqusIntegration) | |
{ | |
// Mocks. | |
$prophet = new Prophet(); | |
// Generic setup. | |
$cacheItem = $prophet->prophesize(Item::class); | |
$cacheItem->save()->willReturn(true); | |
$cacheItem->setInvalidationMethod(Argument::any())->willReturn(); | |
$fallbackCacheItem = $prophet->prophesize(Item::class); | |
$fallbackCacheItem->save()->willReturn(true); | |
$fallbackCacheItem->setInvalidationMethod(Argument::any())->willReturn(); | |
$cacheService->getItem($this->cacheKey)->willReturn($cacheItem); | |
$cacheService->getItem($this->fallbackKey)->willReturn($fallbackCacheItem); | |
// First case is, that the service returns an empty array - the was was either not successful, or the result is | |
// really empty. | |
$disqusIntegration->getMostDiscussed()->willReturn([]); | |
// Skipping any cache for now. | |
$cacheItem->isHit()->willReturn(false); | |
$fallbackCacheItem->isHit()->willReturn(false); | |
$this->getValueFromCacheWithFallback( | |
$this->cacheKey, | |
$this->fallbackKey, | |
$cacheService, | |
function () use ($disqusIntegration) { | |
return $disqusIntegration->getWrappedObject()->getMostDiscussed(); | |
}, | |
$this->cacheTtl, | |
Invalidation::OLD | |
)->shouldBe([]); | |
} | |
function it_retrieves_actual_data_from_the_service_and_stores_them_in_cache( | |
CacheService $cacheService, | |
DisqusIntegration $disqusIntegration | |
) { | |
// Mocks. | |
$prophet = new Prophet(); | |
// Generic setup. | |
$cacheItem = $prophet->prophesize(Item::class); | |
$cacheItem->save()->willReturn(true); | |
$cacheItem->setInvalidationMethod(Argument::any())->willReturn(); | |
$fallbackCacheItem = $prophet->prophesize(Item::class); | |
$fallbackCacheItem->save()->willReturn(true); | |
$fallbackCacheItem->setInvalidationMethod(Argument::any())->willReturn(); | |
$cacheService->getItem($this->cacheKey)->willReturn($cacheItem); | |
$cacheService->getItem($this->fallbackKey)->willReturn($fallbackCacheItem); | |
// Skipping any cache for now. | |
$cacheItem->isHit()->willReturn(false); | |
$fallbackCacheItem->isHit()->willReturn(false); | |
// Now the underlying service really returns some data. | |
$actualValue = ['a', 'b', 'c']; | |
$cacheItem->set($actualValue)->shouldBeCalled(); | |
$fallbackCacheItem->set($actualValue)->shouldBeCalled(); | |
$cacheService->save($cacheItem)->shouldBeCalled(); | |
$cacheService->save($fallbackCacheItem)->shouldBeCalled(); | |
$disqusIntegration->getMostDiscussed()->willReturn($actualValue); | |
$this->getValueFromCacheWithFallback( | |
$this->cacheKey, | |
$this->fallbackKey, | |
$cacheService, | |
function () use ($disqusIntegration) { | |
return $disqusIntegration->getWrappedObject()->getMostDiscussed(); | |
}, | |
$this->cacheTtl, | |
Invalidation::OLD | |
)->shouldBe($actualValue); | |
} | |
function it_retrieves_data_from_fallback_cache(CacheService $cacheService, DisqusIntegration $disqusIntegration) | |
{ | |
// Mocks. | |
$prophet = new Prophet(); | |
// Generic setup. | |
$cacheItem = $prophet->prophesize(Item::class); | |
$cacheItem->save()->willReturn(true); | |
$cacheItem->setInvalidationMethod(Argument::any())->willReturn(); | |
$fallbackCacheItem = $prophet->prophesize(Item::class); | |
$fallbackCacheItem->save()->willReturn(true); | |
$fallbackCacheItem->setInvalidationMethod(Argument::any())->willReturn(); | |
$cacheService->getItem($this->cacheKey)->willReturn($cacheItem); | |
$cacheService->getItem($this->fallbackKey)->willReturn($fallbackCacheItem); | |
// Fallback now has some data. | |
$actualValue = ['a', 'b', 'c']; | |
$cacheItem->isHit()->willReturn(false); | |
$cacheItem->set($actualValue)->shouldBeCalled(); | |
$fallbackCacheItem->isHit()->willReturn(true); | |
$fallbackCacheItem->get()->willReturn($actualValue); | |
$fallbackCacheItem->set($actualValue)->shouldBeCalled(); | |
$cacheService->save($cacheItem)->willReturn(); | |
$cacheService->save($fallbackCacheItem)->willReturn(); | |
$disqusIntegration->getMostDiscussed()->willReturn([]); | |
$this->getValueFromCacheWithFallback( | |
$this->cacheKey, | |
$this->fallbackKey, | |
$cacheService, | |
function () use ($disqusIntegration) { | |
return $disqusIntegration->getWrappedObject()->getMostDiscussed(); | |
}, | |
$this->cacheTtl, | |
Invalidation::OLD | |
)->shouldBe($actualValue); | |
} | |
function it_retrieves_data_from_the_cache( | |
CacheService $cacheService, | |
DisqusIntegration $disqusIntegration, | |
Item $cacheItem, | |
Item $fallbackCacheItem | |
) { | |
$cacheItem->save()->willReturn(true); | |
$cacheItem->setInvalidationMethod(Invalidation::OLD)->shouldBeCalled(); | |
$cacheService->getItem($this->cacheKey)->willReturn($cacheItem); | |
$cacheService->getItem($this->fallbackKey)->willReturn($fallbackCacheItem); | |
// Cache now has some data. | |
$cacheItem->isHit()->willReturn(true); | |
$actualValue = ['a', 'b', 'c']; | |
$cacheItem->get()->willReturn($actualValue); | |
$this->getValueFromCacheWithFallback( | |
$this->cacheKey, | |
$this->fallbackKey, | |
$cacheService, | |
function () use ($disqusIntegration) { | |
return $disqusIntegration->getWrappedObject()->getMostDiscussed(); | |
}, | |
$this->cacheTtl, | |
Invalidation::OLD | |
)->shouldBe($actualValue); | |
} | |
function it_sets_cache_life_span( | |
CacheService $cacheService, | |
DisqusIntegration $disqusIntegration, | |
Item $cacheItem, | |
Item $fallbackCacheItem | |
) { | |
$cacheItem->save()->willReturn(true); | |
$cacheItem->setInvalidationMethod(Invalidation::OLD)->shouldBeCalled(); | |
$cacheItem->expiresAfter(Argument::type('int'))->willReturn(); | |
$cacheService->getItem($this->cacheKey)->willReturn($cacheItem); | |
$cacheService->getItem($this->fallbackKey)->willReturn($fallbackCacheItem); | |
$cacheService->save($cacheItem)->willReturn(); | |
$cacheService->save($fallbackCacheItem)->willReturn(); | |
// Current cache is stale, new one needs to be set with live span. | |
$cacheItem->isHit()->willReturn(false); | |
$actualValue = ['a', 'b', 'c']; | |
$cacheItem->set($actualValue)->shouldBeCalled(); | |
$disqusIntegration->getMostDiscussed()->willReturn($actualValue); | |
$this->getValueFromCacheWithFallback( | |
$this->cacheKey, | |
$this->fallbackKey, | |
$cacheService, | |
function () use ($disqusIntegration) { | |
return $disqusIntegration->getWrappedObject()->getMostDiscussed(); | |
}, | |
10, | |
Invalidation::OLD | |
)->shouldBe($actualValue); | |
$cacheItem->expiresAfter(10)->shouldHaveBeenCalled(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment