1<?php 2 3/** 4 * Observium 5 * 6 * This file is part of Observium. 7 * 8 * These functions perform rewrites on strings and numbers. 9 * 10 * @package observium 11 * @subpackage cache 12 * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited 13 * 14 */ 15 16// Please see phpFastCache documentation and examples here: 17// https://github.com/PHPSocialNetwork/phpfastcache/wiki 18// https://github.com/PHPSocialNetwork/phpfastcache/tree/final/examples 19// NOTE, static functions used only for compatibility with older php versions (5.4 and less). 20// For more cache functionality needed OOP style for use.. 21 22if (version_compare(PHP_VERSION, '5.5.0', '<')) 23{ 24 // Disable phpFastCache 5.x for PHP less than 5.5, since it unsupported 25 $config['cache']['enable'] = FALSE; 26} 27 28/** 29 * Cache functions. 30 * Leave it before initialize phpFastCache, since here used check for php version. 31 */ 32 33/** 34 * Prepend cache key with username/level 35 * 36 * @param string $key Identificator name 37 * @param boolean $global Set to TRUE for do not prepend keys 38 * @return string Prepended key identificator (ie 'data' -> 'mysql|mike|10|data') 39 */ 40function get_cache_key($key, $global = FALSE) 41{ 42 if ($global || is_cli()) { return $key; } // CLI used global key 43 44 if ($_SESSION['authenticated']) 45 { 46 if ($_SESSION['userlevel'] >= 10) 47 { 48 // Use common cache for Global Administrators 49 $key = '__admin|' . $key; 50 } else { 51 // All other users use unique keys! 52 $key = $_SESSION['auth_mechanism'] . '|' . $_SESSION['username'] . '|' . $_SESSION['userlevel'] . '|' . $key; 53 } 54 } else { 55 // Just "protect" anonymous requests from read/write to global cache 56 $key = '__anonymous|' . $key; 57 } 58 59 return $key; 60} 61 62/** 63 * Call to getItem() method for retrieve an Item for cached object 64 * 65 * @param string $key Item identifier (key), for WUI key auto prepended with current user identificator (by get_cache_key()) 66 * @return object Object with cache Item 67 */ 68function get_cache_item($key) 69{ 70 if ($GLOBALS[OBS_CACHE_LINK] === (object)$GLOBALS[OBS_CACHE_LINK]) 71 { 72 $observium_cache = $GLOBALS[OBS_CACHE_LINK]; 73 } else { 74 // Cache not enabled 75 return; 76 } 77 78 $observium_cache_key = get_cache_key($key); // User specific key 79 80 return $observium_cache->getItem($observium_cache_key); 81} 82 83/** 84 * Call to isHit() method for check if your cache Item exists and is still valid 85 * 86 * @param object $item Cache Item object 87 * @return bool TRUE if cache exist and not expired 88 */ 89function ishit_cache_item($item) 90{ 91 if ($GLOBALS[OBS_CACHE_LINK] !== (object)$GLOBALS[OBS_CACHE_LINK]) 92 { 93 // Cache not enabled 94 return; 95 } 96 97 return $item->isHit(); 98} 99 100/** 101 * Call to get() method for retrieve cache data for used Item 102 * 103 * @param object $item Cache Item object 104 * @return mixed Cached data 105 */ 106function get_cache_data($item) 107{ 108 if ($GLOBALS[OBS_CACHE_LINK] !== (object)$GLOBALS[OBS_CACHE_LINK]) 109 { 110 // Cache not enabled 111 return; 112 } 113 114 $start = microtime(TRUE); 115 $data = $item->get(); 116 $cache_time = microtime(TRUE) - $start; 117 118 if (OBS_DEBUG || OBS_CACHE_DEBUG) 119 { 120 print_warning('<span class="text-success">READ FROM CACHE</span> // TTL: '.$item->getTtl().'s // Expiration: <strong>' . $item->getExpirationDate()->format(Datetime::RFC2822) . '</strong><br />' . 121 'Key: <strong>' . $item->getKey() . '</strong> // Tags: <strong>' . $item->getTagsAsString() . '</strong><br />' . 122 'Driver: <strong>'.str_replace(array('phpFastCache\\Drivers\\', '\\Item'), '', get_class($item)).'</strong> // Read time: ' . sprintf("%.7f", $cache_time) . ' ms'); 123 //print_vars($item->getTags()); 124 } 125 return $data; 126} 127 128/** 129 * Call to set() and save() methods for store data in cache 130 * 131 * @param object $item Cache Item object 132 * @param mixed $data Data for store in cache 133 * @param array $params Additional options for cache entry 134 * @return int Unix timestamp when cache item will expired 135 */ 136function set_cache_item($item, $data, $params = array()) 137{ 138 if ($GLOBALS[OBS_CACHE_LINK] === (object)$GLOBALS[OBS_CACHE_LINK]) 139 { 140 $observium_cache = $GLOBALS[OBS_CACHE_LINK]; 141 } else { 142 // Cache not enabled 143 return; 144 } 145 146 // Item tags (for search/cleanup cache later) 147 if (is_cli()) 148 { 149 $tags = array('__cli'); 150 } else { 151 $tags = array('__wui'); 152 if ($_SESSION['authenticated']) 153 { 154 if ($_SESSION['userlevel'] >= 10) 155 { 156 $tags[] = '__admin'; 157 } else { 158 $tags[] = '__username=' . $_SESSION['username']; 159 } 160 } else { 161 $tags[] = '__anonymous'; 162 } 163 } 164 if (isset($params['tags'])) 165 { 166 $tags = array_merge($tags, (array)$params['tags']); 167 } 168 169 // TTL for cache entry in seconds 170 if (isset($params['ttl']) && is_numeric($params['ttl'])) 171 { 172 $ttl = intval($params['ttl']); 173 } 174 else if (is_numeric($_GLOBALS['config']['cache']['ttl'])) 175 { 176 $ttl = $_GLOBALS['config']['cache']['ttl']; 177 } else { 178 // Default TTL (5 min) 179 $ttl = 300; 180 } 181 182 $start = microtime(TRUE); 183 // Add data to cache 184 $item->set($data); 185 // Set expiration TTL 186 $item->expiresAfter($ttl); 187 // Add tags 188 $item->addTags($tags); 189 // Commit 190 $observium_cache->save($item); 191 $cache_time = microtime(TRUE) - $start; 192 193 if (OBS_DEBUG || OBS_CACHE_DEBUG) 194 { 195 print_warning('<span class="text-info">WROTE TO CACHE</span> // TTL: '.$item->getTtl().'s // Expiration: <strong>' . $item->getExpirationDate()->format(Datetime::RFC2822) . '</strong><br />' . 196 'Key: <strong>' . $item->getKey() . '</strong> // Tags: <strong>' . $item->getTagsAsString() . '</strong><br />' . 197 'Driver: <strong>'.str_replace(array('phpFastCache\\Drivers\\', '\\Item'), '', get_class($item)).'</strong> // Write time: ' . sprintf("%.7f", $cache_time) . ' ms'); 198 //print_vars($item->getTags()); 199 } 200 return $item->getExpirationDate()->getTimestamp(); 201} 202 203/** 204 * Get cache Items by single tag 205 * 206 * @param string $tag Tag name for Get Items 207 */ 208function get_cache_items($tag) 209{ 210 if ($GLOBALS[OBS_CACHE_LINK] === (object)$GLOBALS[OBS_CACHE_LINK]) 211 { 212 $observium_cache = $GLOBALS[OBS_CACHE_LINK]; 213 } else { 214 // Cache not enabled 215 return; 216 } 217 218 return $observium_cache->getItemsByTag($tag); 219} 220 221/** 222 * Delete cache Items by tags. 223 * See session_logout() for example. 224 * 225 * @param array $tags Array of tags for deletion 226 */ 227function del_cache_items($tags) 228{ 229 if ($GLOBALS[OBS_CACHE_LINK] === (object)$GLOBALS[OBS_CACHE_LINK]) 230 { 231 $observium_cache = $GLOBALS[OBS_CACHE_LINK]; 232 } else { 233 // Cache not enabled 234 return; 235 } 236 237 return $observium_cache->deleteItemsByTags($tags); 238} 239 240/** 241 * Delete expired Items. 242 * Used "workaround" as described here: 243 * https://github.com/PHPSocialNetwork/phpfastcache/issues/413#issuecomment-270692658 244 * 245 * @param array $tags Array of tags for clear 246 * @return int Unixtime when last expired cache cleared 247 */ 248function del_cache_expired($tag = '') 249{ 250 if ($GLOBALS[OBS_CACHE_LINK] === (object)$GLOBALS[OBS_CACHE_LINK]) 251 { 252 $observium_cache = $GLOBALS[OBS_CACHE_LINK]; 253 } else { 254 // Cache not enabled 255 return; 256 } 257 258 $item = $observium_cache->getItem('__cache_last_clear_expired'); 259 if (!$item->isHit()) 260 { 261 // Here our default tags, see set_cache_item() 262 if (empty($tag)) 263 { 264 if (is_cli()) 265 { 266 $tag = '__cli'; 267 } else { 268 $tag = '__wui'; 269 } 270 } 271 272 // Touch items for clear expired 273 get_cache_items($tag); 274 275 $clear_expired = time(); 276 277 // Store last clean time for 24 hours 278 $item->set($clear_expired)->expiresAfter(86400); 279 // Commit 280 $observium_cache->save($item); 281 282 if (OBS_DEBUG || OBS_CACHE_DEBUG) 283 { 284 print_warning('<span class="text-success">CLEAR EXPIRED CACHE</span> // Time: <strong>' . format_unixtime($clear_expired) . '</strong>'); 285 } 286 } else { 287 $clear_expired = $item->get(); 288 if (OBS_DEBUG || OBS_CACHE_DEBUG) 289 { 290 print_warning('<span class="text-info">LAST CLEAR CACHE TIME</span> // Time: <strong>' . format_unixtime($clear_expired) . '</strong>'); 291 } 292 } 293 294 return $clear_expired; 295} 296 297/** 298 * Add clear cache attrib, this will request for clering cache in next request. 299 * 300 * @param string $target Clear cache target: wui or cli (default if wui) 301 */ 302/* 303function set_cache_clear($target = 'wui') 304{ 305 if (OBS_DEBUG || OBS_CACHE_DEBUG) 306 { 307 print_error('<span class="text-warning">CACHE CLEAR SET.</span> Cache clear set.'); 308 } 309 if (!$GLOBALS['config']['cache']['enable']) 310 { 311 // Cache not enabled 312 return; 313 } 314 315 switch (strtolower($target)) 316 { 317 case 'cli': 318 // Add clear CLI cache attrib. Currently not used 319 set_obs_attrib('cache_cli_clear', get_request_id()); 320 break; 321 default: 322 // Add clear WUI cache attrib 323 set_obs_attrib('cache_wui_clear', get_request_id()); 324 } 325} 326*/ 327 328/** 329 * Return total cache size in bytes. 330 * Note, this is not user/session specific size, but total for cache system 331 * 332 * @return int Total cache size in bytes 333 */ 334function get_cache_stats() 335{ 336 if ($GLOBALS[OBS_CACHE_LINK] === (object)$GLOBALS[OBS_CACHE_LINK]) 337 { 338 $observium_cache = $GLOBALS[OBS_CACHE_LINK]; 339 } else { 340 // Cache not enabled 341 return array('enabled' => FALSE, 'size' => 0); 342 } 343 344 try 345 { 346 //$_s = $observium_cache->getStats(); 347 $size = $observium_cache->getStats()->getSize(); 348 } 349 catch (Exception $e) 350 { 351 $size = 0; 352 } 353 //r($_s->getInfo()); 354 $stats = array('enabled' => TRUE, 355 'driver' => str_replace(array('phpFastCache\\Drivers\\', '\\Driver'), '', get_class($observium_cache)), 356 'size' => $size, 357 ); 358 359 return $stats; 360} 361 362///////////////////////////////////////////////////////// 363// NO FUNCTION DEFINITIONS FOR CACHE AFTER THIS LINE! // 364///////////////////////////////////////////////////////// 365// YES, THAT MEANS YOU // 366///////////////////////////////////////////////////////// 367 368define('OBS_CACHE_DEBUG', isset($_SERVER['PATH_INFO']) && strpos($_SERVER['PATH_INFO'], 'cache_info') !== FALSE); 369 370// Do not load phpFastCache classes if caching mechanism not enabled or not supported 371if (!$config['cache']['enable']) 372{ 373 if (OBS_DEBUG || OBS_CACHE_DEBUG) 374 { 375 if (version_compare(PHP_VERSION, '5.5.0', '<')) 376 { 377 print_error('<span class="text-danger">CACHE DISABLED.</span> You use too old php version, see <a href="' . OBSERVIUM_URL . '/docs/software_requirements/">minimum software requirements</a>.'); 378 } else { 379 print_error('<span class="text-danger">CACHE DISABLED.</span> Disabled in config.'); 380 } 381 } 382 return; 383} 384 385/** 386 * Temporary hardcoded caching in files, will improved later with other providers 387 */ 388 389define('OBS_CACHE_LINK', 'observium_cache'); // Variable name for call to cache class 390 391// Call the phpFastCache 392use phpFastCache\CacheManager; 393use phpFastCache\Core\phpFastCache; 394 395// Setup File Path and other basic options 396CacheManager::setDefaultConfig(array( 397 'path' => $config['cache_dir'], 398 'securityKey' => is_cli() ? 'cli' : 'wui', // do not use $_SERVER['hostname'] as key 399 'ignoreSymfonyNotice' => TRUE, 400)); 401 402$cache_driver = 'files'; // If other drivers not detected, use files as fallback 403if (str_istarts($config['cache']['driver'], 'auto')) 404{ 405 // Detect avialable drivers, 406 // NOTE order from fastest to slowest! 407 //$detect_driver = array('zendshm', 'apcu', 'xcache', 'sqlite', 'files'); 408 $detect_driver = array('zendshm', 'apcu', 'sqlite', 'files'); 409} else { 410 $detect_driver = array(strtolower($config['cache']['driver'])); 411} 412// Basic detect if extension/driver available 413$cache_driver = 'files'; 414foreach ($detect_driver as $entry) 415{ 416 switch($entry) 417 { 418 case 'zendshm': 419 if (extension_loaded('Zend Data Cache') && function_exists('zend_shm_cache_store')) 420 { 421 $cache_driver = 'zendshm'; 422 break 2; 423 } 424 break; 425 case '!memcached': 426 // Also need connection test 427 try 428 { 429 $mc = new Memcached(); 430 } catch (Exception $e) {} 431 if (class_exists('Memcached')) 432 { 433 $cache_driver = 'memcached'; 434 break 2; 435 } 436 break; 437 case 'apcu': 438 if (extension_loaded('apcu') && ini_get('apc.enabled')) 439 { 440 $cache_driver = 'apcu'; 441 break 2; 442 } 443 break; 444 /* XCache RIP 445 case 'xcache': 446 if (extension_loaded('xcache') && function_exists('xcache_get')) 447 { 448 $cache_driver = 'xcache'; 449 break 2; 450 } 451 break; 452 */ 453 case 'sqlite': 454 if (extension_loaded('pdo_sqlite')) 455 { 456 $cache_driver = 'sqlite'; 457 break 2; 458 } 459 break; 460 case 'files': 461 default: 462 //$cache_driver = 'files'; 463 break; 464 } 465} 466 467switch($cache_driver) 468{ 469 case 'sqlite': 470 // Create base cache dir if not exist 471 if (!is_dir($config['cache_dir'])) 472 { 473 mkdir($config['cache_dir'], 0777, TRUE); 474 chmod($config['cache_dir'], 0777); 475 } 476 // Do not break here! 477 478 case 'zendshm': 479 case 'memcached': 480 case 'apcu': 481 //case 'xcache': 482 try 483 { 484 $GLOBALS[OBS_CACHE_LINK] = CacheManager::getInstance($cache_driver); 485 } 486 catch (Exception $e) 487 { 488 print_debug('Cache driver '.ucfirst($cache_driver).' not functional. Caching disabled!'); 489 $GLOBALS[OBS_CACHE_LINK] = CacheManager::getInstance('Devfalse'); // disable caching 490 } 491 break; 492 493 case 'files': 494 default: 495 // Create base cache dir if not exist 496 if (!is_dir($config['cache_dir'])) 497 { 498 mkdir($config['cache_dir'], 0777, TRUE); 499 chmod($config['cache_dir'], 0777); 500 } 501 502 try 503 { 504 $GLOBALS[OBS_CACHE_LINK] = CacheManager::getInstance('files'); 505 } 506 catch (Exeption $e) 507 { 508 print_debug('Cache driver Files not functional. Caching disabled!'); 509 $GLOBALS[OBS_CACHE_LINK] = CacheManager::getInstance('Devfalse'); // disable caching 510 } 511} 512 513// Clear cache totally by requested attrib 514if (is_cli()) 515{ 516 if (get_obs_attrib('cache_cli_clear')) 517 { 518 $GLOBALS[OBS_CACHE_LINK]->clear(); 519 del_obs_attrib('cache_cli_clear'); 520 if (OBS_DEBUG || OBS_CACHE_DEBUG) 521 { 522 print_error('<span class="text-warning">CACHE CLEARED.</span> Cache clear requested.'); 523 } 524 } 525} 526else if ($request_id = get_obs_attrib('cache_wui_clear')) 527{ 528 if ($request_id !== get_request_id()) // Skip cache clear inside same page request 529 { 530 $GLOBALS[OBS_CACHE_LINK]->clear(); 531 del_obs_attrib('cache_wui_clear'); 532 if (OBS_DEBUG || OBS_CACHE_DEBUG) 533 { 534 print_error('<span class="text-warning">CACHE CLEARED.</span> Cache clear requested.'); 535 } 536 } 537} 538 539// Clean 540unset($detect_driver, $cache_driver, $entry); 541 542// EOF 543