Source of file SimpleCache.php
Size: 8,521 Bytes - Last Modified: 2021-12-23T10:26:52+00:00
/var/www/docs.ssmods.com/process/src/code/services/SimpleCache.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 | <?php include_once(dirname(__FILE__).'/SimpleCacheStores.php'); /** * A simple cache service that uses a configurable CacheStore * for persisting items for a given length of time. * * Usage * * SimpleCache::get_cache('name')->get('mykey'); * SimpleCache::get_cache('name')->store('mykey', {someobject}, 3600seconds); * * */ class SimpleCache { const HIT_KEY = '__SC_HIT'; const MISS_KEY = '__SC_MISS'; private static $default_caches = array('PublisherCache', 'DynamicPublisherCache', 'FragmentCache'); /** * The cache store to use for actually putting and retrieving items from * * @var CacheStore */ protected $store = null; /** * The type of store we're using for the cache * * @var string */ public static $store_type = 'SimpleFileBasedCacheStore'; /** * * @var Define some config for named caches */ public static $cache_configs = array( 'default' => array( 'store_type' => 'SimpleFileBasedCacheStore', 'store_options' => array( 'silverstripe-cache/cache_store', ), 'cache_options' => array( 'expiry' => 600 ) ), ); /** * @var int */ public $expiry = 0; /** * In memory object store * * @var array */ private $items = array(); /** * Track cache instances * * @var array */ private static $instances = array(); /** * Track hits and misses * * @var int */ private $hits = 0; private $misses = 0; public $recordStats = false; /** * Get the instance * @return CacheService */ public function inst() { return self::get_cache('default'); } /** * Registers caches in the injector */ public static function register_caches($cacheNames = null) { if (!$cacheNames) { $cacheNames = self::$default_caches; } foreach ($cacheNames as $cacheName) { $cache = self::get_cache($cacheName); if ($cache) { Injector::inst()->registerService($cache, $cacheName); } } } /** * Get a named cache * * @param type $name * @param type $store * * @return SimpleCache */ public static function get_cache($name='default', $store=null, $config = null) { if (!isset(self::$instances[$name])) { if (!isset(self::$cache_configs[$name])) { $name = 'default'; } if (isset(self::$cache_configs[$name]) && $conf = self::$cache_configs[$name]) { $type = $conf['store_type']; $storeOpts = $conf['store_options']; $config = isset($conf['cache_options']) ? $conf['cache_options'] : $config; $reflector = new ReflectionClass($type); // note that this bit is to be backwards $args = array( $name, $storeOpts ); $store = $reflector->newInstanceArgs($args); } self::$instances[$name] = new SimpleCache($store, $config); } return self::$instances[$name]; } public function __construct($store = null, $config = null) { if (!$store) { $store = self::$store_type; } if (is_string($store)) { $store = new $store; } $this->store = $store; if ($config) { $this->configure($config); } register_shutdown_function(array($this, 'shutdown')); } /** * Set config for this cache */ public function configure($config) { $this->expiry = isset($config['expiry']) ? $config['expiry'] : $this->expiry; } /** * On shutdown, store hits and misses */ public function shutdown() { if ($this->recordStats) { if ($this->hits) { $curr = $this->get(self::HIT_KEY); $curr += $this->hits; // don't count the hit on hit_key! $this->store(self::HIT_KEY, $curr); } if ($this->misses) { $curr = $this->get(self::MISS_KEY); $curr += $this->misses; $this->store(self::MISS_KEY, $curr); } } } /** * Gets statistics about this cache */ public function stats() { $stats = new stdClass(); $stats->hits = $this->get(self::HIT_KEY); $stats->misses = $this->get(self::MISS_KEY); $stats->count = $this->store->count(); return $stats; } protected function tagKey($tag) { return "__TAG__$tag"; } /** * Cache an item * * @param string $key * @param mixed $value * @param int $expiry * How many seconds to cache this object for (no value uses the configured default) * @param array $tags * Any tags to flag this item has */ public function store($key, $value, $expiry = -1, $tags = null) { if ($expiry == -1) { $expiry = $this->expiry; } $entry = new SimpleCacheItem(); $entry->value = serialize($value); $entry->stored = time(); if ($expiry) { $entry->expireAt = time() + $expiry; } else { $entry->expireAt = 0; } $data = serialize($entry); $this->store->store($key, $data, $expiry); // if we've got tags, add this element to the list of keys stored for // a given tag if ($tags) { foreach ($tags as $tag) { $tagKey = $this->tagKey($tag); $tagStore = $this->get($tagKey); if (!$tagStore) { $tagStore = array(); } // we store the key in the map to prevent duplicated keys $tagStore[$key] = true; $this->store($tagKey, $tagStore, 0); } } $this->items[$key] = $entry; } /** * Gets a cached value for a given key * @param String $key * The key to retrieve data for */ public function get($key) { $entry = null; if (isset($this->items[$key])) { $entry = $this->items[$key]; } else { $data = $this->store->get($key); if ($data) { $entry = unserialize($data); } } if (!$entry) { if ($key != self::HIT_KEY && $key != self::MISS_KEY) { ++$this->misses; } return $entry; } // if the expire time is in the future if ($entry->expireAt > time() || $entry->expireAt == 0) { if ($key != self::HIT_KEY && $key != self::MISS_KEY) { ++$this->hits; } return unserialize($entry->value); } if ($key != self::MISS_KEY) { ++$this->misses; } // if we got to here, we need to expire the value $this->expire($key); return null; } /** * Get a list of items by tag * * @param string $tag * @return array */ public function getByTag($tag) { $tagKey = $this->tagKey($tag); $keys = $this->get($tagKey); $elems = array(); if ($keys) { foreach ($keys as $key => $keyval) { $elem = $this->get($key); if (!$elem) { unset($keys[$key]); } else { $elems[] = $elem; } } // re-save all the relevant keys $this->store($tagKey, $keys, 0); } return $elems; } /** * Gets the raw item underneath a given key, so we can see things about expiry etc * @param type $key */ public function getCacheEntry($key) { $entry = null; if (isset($this->items[$key])) { $entry = $this->items[$key]; } else { $data = $this->store->get($key); if ($data) { $entry = unserialize($data); } } return $entry; } /** * Delete from the cache * * @param mixed $key */ public function delete($key) { unset($this->items[$key]); $this->store->delete($key); } /** * Clear out all elements with a particular tag * * @param string $tag */ public function deleteByTag($tag) { $tagKey = $this->tagKey($tag); $keys = $this->get($tagKey); $num = count($keys); if ($keys) { foreach ($keys as $key => $dummy) { $this->delete($key); } } $this->delete($tagKey); return $num; } /** * Explicitly expire the given key * * @param $key */ public function expire($key) { $this->delete($key); } /** * Flush the whole cache clean */ public function clear() { $this->items = array(); $this->store->clear(); } /** * If underlying store access is needed; avoid using if possible! * * @return SimpleCacheStore */ public function getStore() { return $this->store; } } /** * Basic wrapper around items that need to be stored in the cache * * @author Marcus Nyeholt <marcus@silverstripe.com.au> * */ class SimpleCacheItem { public $value; public $expireAt; public $stored; } if (!function_exists('rrmdir')) { function rrmdir($dir) { if (is_dir($dir)) { $objects = scandir($dir); foreach ($objects as $object) { if ($object != "." && $object != "..") { if (filetype($dir . "/" . $object) == "dir") rrmdir($dir . "/" . $object); else unlink($dir . "/" . $object); } } reset($objects); rmdir($dir); } } } |