123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- <?php
- /**
- * System-wide caching abstraction engine
- */
- class UbillingCache {
- /**
- * System alter.ini config content
- *
- * @var array
- */
- protected $altCfg = array();
- /**
- * Cache storage type: files, memcached, fake
- * via UBCACHE_STORAGE option
- *
- * @var string
- */
- protected $storage = '';
- /**
- * Memcached server IP/Hostname
- * via MEMCACHED_SERVER option
- *
- * @var string
- */
- protected $memcachedServer = '';
- /**
- * Memcached server port
- * via MEMCACHED_PORT option
- *
- * @var int
- */
- protected $memcachedPort = '';
- /**
- * Redis server IP/Hostname
- * via REDIS_SERVER option
- *
- * @var string
- */
- protected $redisServer = '';
- /**
- * Redis server port
- * via REDIS_PORT option
- *
- * @var int
- */
- protected $redisPort = '';
- /**
- * File storage path: "exports/" by default
- *
- * @var string
- */
- protected $storagePath = '';
- /**
- * Just debugging flag
- *
- * @var bool
- */
- protected $debug = false;
- /**
- * Single instance of memcached object
- *
- * @var object
- */
- protected $memcached = ''; // single memcached object
- /**
- * Cache keys prefix
- */
- const CACHE_PREFIX = 'UBCACHE_';
- /**
- * Default cache log path for debug mode
- */
- const LOG_PATH = 'exports/cache.log';
- /**
- * Creates new UbillingCache instance
- *
- * @param string $storageType overrides cache storage set in config files.
- *
- * @return void
- */
- public function __construct($storageType = '') {
- $this->loadAlter();
- $this->setOptions($storageType);
- $this->initStorageServerCache();
- }
- /**
- * Loads global alter config into protected property
- *
- * @global object $ubillingConfig
- *
- * @return void
- */
- protected function loadAlter() {
- global $ubillingConfig;
- $this->altCfg = $ubillingConfig->getAlter();
- }
- /**
- * Sets object storage mode
- *
- * @param string $storageType overrides cache storage set in config files.
- *
- * @return void
- */
- protected function setOptions($storageType = '') {
- if (isset($this->altCfg['UBCACHE_STORAGE'])) {
- $this->storage = $this->altCfg['UBCACHE_STORAGE'];
- } else {
- $this->storage = 'fake';
- }
- //override storage type from constructor
- if (!empty($storageType)) {
- $this->storage = $storageType;
- }
- if (@$this->altCfg['UBCACHE_DEBUG']) {
- $this->debug = true;
- }
- if ($this->storage == 'memcached') {
- if (isset($this->altCfg['MEMCACHED_SERVER'])) {
- $this->memcachedServer = $this->altCfg['MEMCACHED_SERVER'];
- } else {
- $this->memcachedServer = 'localhost';
- }
- if (isset($this->altCfg['MEMCACHED_PORT'])) {
- $this->memcachedPort = $this->altCfg['MEMCACHED_PORT'];
- } else {
- $this->memcachedPort = 11211;
- }
- }
- if ($this->storage == 'redis') {
- if (isset($this->altCfg['REDIS_SERVER'])) {
- $this->redisServer = $this->altCfg['REDIS_SERVER'];
- } else {
- $this->redisServer = 'localhost';
- }
- if (isset($this->altCfg['REDIS_PORT'])) {
- $this->redisPort = $this->altCfg['REDIS_PORT'];
- } else {
- $this->redisPort = 6379;
- }
- }
- if ($this->storage == 'files') {
- $this->storagePath = 'exports/';
- }
- }
- /**
- * Inits storage server cache if it needed
- *
- * @return void
- */
- protected function initStorageServerCache() {
- // Init Memcached
- if ($this->storage == 'memcached') {
- $this->memcached = new Memcached();
- $this->memcached->addServer($this->memcachedServer, $this->memcachedPort);
- }
- // Init Redis
- if ($this->storage == 'redis') {
- $this->redis = new Redis();
- $this->redis->connect($this->redisServer, $this->redisPort);
- $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
- }
- }
- /**
- * Generates key storable internal name
- *
- * @param string $key
- *
- * @return string
- */
- protected function genKey($key) {
- $result = self::CACHE_PREFIX . vf($key);
- return ($result);
- }
- /**
- * Logs data if logging is enabled
- *
- * @param string $event
- * @param string $dataSample
- *
- * @return void
- */
- protected function logEvent($event, $dataSample = '') {
- if ($this->debug) {
- $curDate = curdatetime();
- $dataSize = '';
- if (!empty($dataSample)) {
- $dataSize = strlen(serialize($dataSample)) . ' bytes';
- if (ispos($event, 'GET KEY')) {
- $dataSize .= ' HIT';
- }
- } else {
- $dataSize = 'EMPTY_DATA';
- if (ispos($event, 'GET KEY')) {
- $dataSize .= ' MISS YA';
- }
- }
- $logData = $curDate . ' ' . $this->storage . ' ' . $event . ' ' . $dataSize . "\n";
- file_put_contents(self::LOG_PATH, $logData, FILE_APPEND);
- }
- }
- /**
- * Puts data into cache storage
- *
- * @param string $key
- * @param string $data
- * @param int $expiration
- *
- * @return void
- */
- public function set($key, $data, $expiration = 2592000) {
- $key = $this->genKey($key);
- // Set expiration time not more 1 month
- $expiration = ($expiration > 2592000) ? '2592000' : $expiration;
- //files storage
- if ($this->storage == 'files') {
- file_put_contents($this->storagePath . $key, serialize($data));
- }
- //memcached storage
- if ($this->storage == 'memcached') {
- $this->memcached->set($key, $data, $expiration);
- }
- //redis storage
- if ($this->storage == 'redis') {
- $this->redis->set($key, $data);
- // setTimeout method deprecated: https://github.com/phpredis/phpredis/pull/1572
- // that check required for paleolithic legacy setups with PHP 5.x etc.
- if (method_exists($this->redis,'expire')) {
- $this->redis->expire($key, $expiration);
- } else {
- @$this->redis->setTimeout($key, $expiration);
- }
- }
- $this->logEvent('SET KEY: ' . $key, $data);
- }
- /**
- * Returns data by key name. Empty if no data exists or cache expired.
- *
- * @param string $key Storage key name
- * @param int $expiration Expiration time in seconds
- *
- * @return mixed
- */
- public function get($key, $expiration = 2592000) {
- $result = '';
- $keyRaw = $key;
- $key = $this->genKey($key);
- //files storage
- if ($this->storage == 'files') {
- $cacheName = $this->storagePath . $key;
- $cacheTime = time() - $expiration;
- $updateCache = false;
- if (file_exists($cacheName)) {
- $updateCache = false;
- if ((filemtime($cacheName) > $cacheTime)) {
- $updateCache = false;
- } else {
- $updateCache = true;
- }
- } else {
- $updateCache = true;
- }
- if (!$updateCache) {
- //read data directly from cache
- $data = file_get_contents($cacheName);
- $result = unserialize($data);
- } else {
- //cache expired, return empty result
- $result = '';
- $this->delete($keyRaw);
- }
- $this->logEvent('GET KEY: ' . $key, $result);
- return ($result);
- }
- //memcached storage
- if ($this->storage == 'memcached') {
- $result = $this->memcached->get($key);
- if ($result===false) {
- $result = '';
- }
- $this->logEvent('GET KEY: ' . $key, $result);
- return ($result);
- }
- //redis storage
- if ($this->storage == 'redis') {
- $result = $this->redis->get($key);
- if ($result===false) {
- $result = '';
- }
- $this->logEvent('GET KEY: ' . $key, $result);
- return ($result);
- }
- //fake storage
- if ($this->storage == 'fake') {
- $result = '';
- $this->logEvent('GET KEY: ' . $key, $result);
- return ($result);
- }
- return ($result);
- }
- /**
- * Returns data from cache by key or runs callback and fills new cache data
- *
- * @param string $key Storage key
- * @param Closure $callback Callback function
- * @param int $expiration Expiration time in seconds
- *
- * @return string
- */
- public function getCallback($key, Closure $callback, $expiration = 2592000) {
- // Use this class get function
- $result = $this->get($key, $expiration);
- if (!$result) {
- // If not have result from class get function
- // return $callback data function and set new cache
- $result = $callback();
- /**
- * Intensity - I feel the lava rushing through my veins
- * Stars are reforming - to enter the fourth dimension
- */
- $this->set($key, $result, $expiration);
- }
- return ($result);
- }
- /**
- * Deletes data from cache by key name
- *
- * @param string $key
- *
- * @return void
- */
- public function delete($key) {
- $key = $this->genKey($key);
- //files storage
- if ($this->storage == 'files') {
- if (file_exists($this->storagePath . $key)) {
- @unlink($this->storagePath . $key);
- }
- }
- //memcached storage
- if ($this->storage == 'memcached') {
- $this->memcached->delete($key);
- }
- //redis storage
- if ($this->storage == 'redis') {
- $this->redis->delete($key);
- }
- $this->logEvent('DELETE KEY: ' . $key);
- }
- /**
- * Show all data from cache
- *
- * @param bool $show_data
- *
- * @return array
- */
- public function getAllcache($show_data = false) {
- $result = array();
- //files storage
- if ($this->storage == 'files') {
- $cache = scandir($this->storagePath);
- $keys = array_diff($cache, array('..', '.', '.gitignore', '.htaccess'));
- $keys = preg_grep("/^" . self::CACHE_PREFIX . "/", $keys);
- if ($show_data) {
- $result = array();
- foreach ($keys as $key => $file) {
- $result[$key]['key'] = $file;
- $result[$key]['value'] = unserialize(file_get_contents($this->storagePath . $file));
- }
- } else {
- $result = $keys;
- }
- return($result);
- }
- //memcached storage
- if ($this->storage == 'memcached') {
- $keys = $this->memcached->getAllKeys();
- if ($keys == false) {
- $keys = array(); //preventing issues on PHP 8.2
- }
- $keys = preg_grep("/^" . self::CACHE_PREFIX . "/", $keys);
- if ($show_data) {
- $this->memcached->getDelayed($keys);
- $result = $this->memcached->fetchAll();
- } else {
- $result = $keys;
- }
- return($result);
- }
- //redis storage
- if ($this->storage == 'redis') {
- $keys = $this->redis->keys(self::CACHE_PREFIX . '*');
- if ($show_data) {
- $value = $this->redis->mGet($keys);
- foreach ($keys as $id => $key) {
- $result[$id]['key'] = $key;
- $result[$id]['value'] = $value[$id];
- }
- } else {
- $result = $keys;
- }
- return($result);
- }
- }
- /**
- * Delete all data from cache
- *
- * @return void
- */
- public function deleteAllcache() {
- $cache_data = $this->getAllcache();
- $this->logEvent('TOTAL CLEANUP');
- //files storage
- if ($this->storage == 'files' and !empty($cache_data)) {
- foreach ($cache_data as $cache) {
- unlink($this->storagePath . $cache);
- }
- }
- //memcached storage
- if ($this->storage == 'memcached' and !empty($cache_data)) {
- $result = $this->memcached->deleteMulti($cache_data);
- return($result);
- }
- //redis storage
- if ($this->storage == 'redis' and !empty($cache_data)) {
- $result = $this->redis->delete($cache_data);
- return($result);
- }
- }
- }
- ?>
|