<?php
namespace Frivol\Common\Service;
use App\Event\Content\SingleContentCacheClearedEvent;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class CacheService
{
const VERSION_TAG_NAME = "frivol___versionTag";
/**
* @var TagAwareCacheInterface
*/
protected TagAwareCacheInterface $cache;
/**
* @var EventDispatcherInterface
*/
protected EventDispatcherInterface $eventDispatcher;
/**
* CacheService constructor.
* @param TagAwareCacheInterface $cache
*/
public function __construct(TagAwareCacheInterface $cache, EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
$this->setCache($cache);
}
/**
* @return TagAwareCacheInterface
*/
protected function getCache(): TagAwareCacheInterface
{
return $this->cache;
}
/**
* @param TagAwareCacheInterface $cache
* @return $this
*/
protected function setCache(TagAwareCacheInterface $cache): self
{
$this->cache = $cache;
return $this;
}
public function getEventDispatcher(): EventDispatcherInterface
{
return $this->eventDispatcher;
}
/**
* @param array $tags
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function invalidateTags(array $tags): bool
{
return $this->getCache()->invalidateTags($tags);
}
public function getItem(string $key): CacheItem {
return $this->getCache()->getItem($key);
}
public function save(CacheItem $item): void {
$this->getCache()->save($item);
}
/**
* Updates the redis version tag, invalidating all old cache data by tag
*
* @return string new version tag
*/
public function incrementVersionTag(): string {
$item = $this->getCache()->getItem(self::VERSION_TAG_NAME);
$nb = 0;
if($item->isHit() === true) {
$valueHelper = explode("-", $item->get());
$nb = (int)array_pop($valueHelper);
}
if($nb === 100) {
$nb = 0;
}
$nb++;
$toInvalidate = [];
for($i = 0; $i <= 100; $i++) {
if ($i === $nb) {
continue;
}
$toInvalidate[] = 'version-' . $i;
}
$this->invalidateTags($toInvalidate);
$item->set('version-' . $nb);
$this->getCache()->save($item);
return $item->get();
}
/**
* Returns the current active redis version tag
*
* @return string new version tag
*/
public function getCurrentVersionTag(): string {
$version = $this->getCache()->getItem(self::VERSION_TAG_NAME)->get();
if($version !== null) {
return $version;
}
return $this->incrementVersionTag();
}
/**
* Returns a single item from the cache. If it doesn't exist it gets
* created and the version tag added
*
* @param string $key key of the cache entry
* @param callable|null $cb callback setting the value if not present
* @return mixed cache value
*/
public function get(string $key, callable $cb = null): mixed {
$cacheResult = $this->getCache()->getItem($key);
if($cacheResult->isHit() === true) {
return $cacheResult->get();
}
if($cb === null) {
return '';
}
$call = function(ItemInterface $item) use ($cb) {
$h = $cb($item);
$item->tag([
$this->getCurrentVersionTag()
]);
return $h;
};
return $this->getCache()->get($key, $call);
}
/**
* @param int $member
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearMemberMediaForMember(int $member): bool
{
return $this->getCache()->invalidateTags([
'profilephotos-member-' . $member,
'member-' . $member
]);
}
/**
* @param int $member
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearMemberCoinsCache(int $member): bool
{
return $this->getCache()->delete('membercoins_' . $member);
}
/**
* @param int $member
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearMediaCenterCache(int $member): bool
{
return $this->getCache()->invalidateTags([
'mediacenter-' . $member
]);
}
public function clearMemberPropertyCache(int $memberId): bool
{
return $this->getCache()->invalidateTags([
$this->getMemberPropertyCacheTag($memberId)
]);
}
public function clearWebmasterPropertyCache(int $webmasterId): bool
{
return $this->getCache()->invalidateTags([
$this->getWebmasterPropertyCacheTag($webmasterId)
]);
}
public function clearPublicMemberPropertyCache(int $memberId): bool
{
return $this->getCache()->invalidateTags([
$this->getPublicMemberPropertyCacheTag($memberId)
]);
}
public function getMemberPropertyCacheTag(int $memberId): string
{
return 'memberproperties-' . $memberId;
}
/**
* @param int $webmasterId
* @return string
*/
public function getWebmasterPropertyCacheTag(int $webmasterId): string
{
return 'webmasterproperties-' . $webmasterId;
}
public function getPublicMemberPropertyCacheTag(int $memberId): string
{
return 'publicmemberproperties-' . $memberId;
}
/**
* @param int $group
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearUserGroupCache(int $group): bool
{
$this->getCache()->invalidateTags([
'group-' . $group, # for member_count stat in group overview
]);
return $this->clearUserGroupMembershipCache($group);
}
/**
* @param int $group
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearUserGroupMembershipCache(int $group): bool
{
return $this->getCache()->invalidateTags([
'group-members-' . $group
]);
}
/**
* @param int $group
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearUserGroupThreadCache(int $group): bool
{
return $this->getCache()->invalidateTags([
'group-' . $group, # for post count stat in group overview
'group-threads-' . $group,
]);
}
/**
* @param int $group
* @param int $thread
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearUserGroupThreadPostCache(int $group, int $thread): bool
{
return $this->getCache()->invalidateTags([
'group-' . $group, # for post count stat in group overview
'group-threads-' . $group,
'group-posts-' . $thread
]);
}
/**
* @param int $imagesetId
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearImagesetCache(int $imagesetId): bool
{
$this->getCache()->invalidateTags([
'imageset-' . $imagesetId
]);
return true;
}
/**
* @param int $imagesetId
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearVideoCache(int $videoId): bool
{
$this->getCache()->invalidateTags([
'video-' . $videoId
]);
return true;
}
/**
* @param int $content
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
* @throws \ReflectionException
* @throws NotFoundHttpException
*/
public function clearContentCache(int $content): bool
{
$result = $this->getCache()->invalidateTags([
'content-' . $content
]);
if($result) {
$this->getEventDispatcher()->dispatch(new SingleContentCacheClearedEvent($content));
}
return $result;
}
/**
* @param int $conversation
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearConversationCache(int $conversation): bool
{
return $this->getCache()->invalidateTags([
'conversation-' . $conversation,
'messenger-messages-' . $conversation
]);
}
public function clearImagesetPurchases(int $imagesetId): bool
{
return $this->getCache()->invalidateTags([
'imageset-purchase-' . $imagesetId
]);
}
public function clearAmateurStatistics(int $amateurMemberId): bool
{
return $this->getCache()->invalidateTags([
'statistics-' . $amateurMemberId
]);
}
public function clearGuestbookCache(int $memberId): bool
{
return $this->getCache()->invalidateTags([
'guestbook-' . $memberId
]);
}
/**
* @param int $memberId
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearMassMessageMemberCache(int $memberId): bool
{
return $this->getCache()->invalidateTags([
'massmessage-member-' . $memberId
]);
}
/**
* @param int $massMessageId
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearMassMessageCache(int $massMessageId): bool
{
return $this->getCache()->invalidateTags([
'massmessage',
'massmessage-' . $massMessageId
]);
}
public function clearActorsCacheFor(int $memberId = null, int $contentId = null): bool
{
$tags = [];
if ($memberId > 0) {
$tags[] = 'member-actors-' . $memberId;
}
if ($contentId > 0) {
$tags[] = 'content-' . $contentId;
}
if ($tags === []) {
return true;
}
return $this->getCache()->invalidateTags($tags);
}
public function clearAllActorsCache(): bool
{
return $this->getCache()->invalidateTags(['actors']);
}
/**
* @param int $member
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearPhonenumberCache(int $member): bool
{
return $this->getCache()->invalidateTags([
'phonenumbers-' . $member
]);
}
/**
* @param int $member
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearChatTemplateCache(int $member): bool
{
return $this->getCache()->invalidateTags([
'templates-member-' . $member
]);
}
/**
* @return bool
* @throws \Psr\Cache\InvalidArgumentException
*/
public function clearMemberOnlineCache(): bool
{
return $this->getCache()->invalidateTags([
'memberonline'
]);
}
}