Leaseweb / LswMemcacheBundle

Symfony bundle for Memcache Doctrine caching and session storage in the Web Debug Toolbar.
MIT License
202 stars 57 forks source link

Feature: assign tags to memcache items #84

Open d3f3kt opened 8 years ago

d3f3kt commented 8 years ago

I've written some month ago a little system to assign memcache items tags. So it's possible to invalidate all items which owns a tag by invalidating this special tag.

I think it will be very helpful to migrate this code directly into the memache bundle.

<?php

namespace Geistert\Utility\Memcache;

use Lsw\MemcacheBundle\Cache\MemcacheInterface;

/**
 * Memcache Wrapper to use custom tags for items.
 */
class MemcacheManager
{
    /**
     * @var MemacheInterface
     */
    private $memache;

    /**
     * Cache of Tag values.
     *
     * @var array
     */
    private $tagCache;

    const TAGPREFIX = 'MEMCACHEMANAGERTAG__';

    /**
     * Inititialize Memcache Manager with custom memcache instance.
     *
     * @param MemcacheInterface $memcache Memache instance
     */
    public function __construct(MemcacheInterface $memcache)
    {
        $this->memcache = $memcache;
        $this->tagCache = array();
    }

    /**
     * Native memcache get method.
     *
     * @param string $name name of item
     *
     * @return mixed item value
     */
    public function get($name)
    {
        return $this->memcache->get(sha1($name));
    }

    /**
     * Get item from memcache and validate custom tags.
     *
     * @param string $name name of item
     *
     * @return mixed item value
     */
    public function load($name)
    {
        if ($item = $this->get($name)) {
            if ($item instanceof MemcacheData) {
                if ($this->isValid($item)) {
                    return $item->getData();
                }
            } else {
                return $item;
            }
        }

        return false;
    }

    /**
     * Save item with custom tags.
     *
     * @param string $key   Name of item
     * @param mixed  $value Value of item
     * @param array  $tags
     * @param int    $time  Time to live in secounds
     */
    public function save($key, $value, $tags = array(), $time = 0)
    {
        $tagList = array();
        foreach ($tags as $tag) {
            if ($tagValue = $this->getTagValue($tag)) {
                $tagList[$tag] = $tagValue;
            } else {
                $tagList[$tag] = $this->flushTag($tag);
            }
        }

        $memObject = new MemcacheData($value, $tagList);
        $this->memcache->set(sha1($key), $memObject, null, $time);
    }

    /**
     * Flush a single tag.
     *
     * @param string $tagName Name of Tag
     *
     * @return int new tag id
     */
    public function flushTag($tagName)
    {
        $rand = rand(1, 300000);

        $this->memcache->set(self::TAGPREFIX.$tagName, $rand);
        $this->tagCache[$tagName] = $rand;

        return $rand;
    }

    /**
     * Check if Item is valid.
     *
     * @param MemcacheData $item MemcacheData Object with tags
     *
     * @return bool Is item valid?
     */
    private function isValid(MemcacheData $item)
    {
        foreach ($item->getTags() as $tag => $value) {
            if ($this->getTagValue($tag) != $value) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get id of a tag. Use internal caching.
     *
     * @param string $tag Tag name
     *
     * @return int tag id
     */
    private function getTagValue($tag)
    {
        if (array_key_exists($tag, $this->tagCache)) {
            return $this->tagCache[$tag];
        } else {
            $tagValue = $this->memcache->get(self::TAGPREFIX.$tag);
            $this->tagCache[$tag] = $tagValue;

            return $tagValue;
        }
    }
}
<?php

namespace Geistert\Utility\Memcache;

/**
 * Memache data wrapper object.
 */
class MemcacheData
{
    /**
     * Real data of item.
     *
     * @var mixed
     */
    private $data;

    /**
     * Array of tags.
     *
     * @var array
     */
    private $tags;

    /**
     * Init Data wrapper.
     *
     * @param mixed $data item data
     * @param array $tags array of tags
     */
    public function __construct($data, $tags = array())
    {
        $this->data = $data;
        $this->tags = $tags;
    }

    /**
     * Get item value.
     *
     * @return mixed item value
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Set item value.
     *
     * @param mixed $data item value
     *
     * @return self
     */
    public function setData($data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Get item tags.
     *
     * @return array tags
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * Set tags of item.
     *
     * @param array $tags tags of item
     *
     * @return self
     */
    public function setTags($tags)
    {
        $this->tags = $tags;

        return $this;
    }
}