PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/main/lib/data/cache.php

https://gitlab.com/alexprowars/bitrix
PHP | 541 lines | 430 code | 76 blank | 35 comment | 85 complexity | 8ef062c287abd0a205f2971d70409466 MD5 | raw file
  1. <?php
  2. /**
  3. * Bitrix Framework
  4. * @package bitrix
  5. * @subpackage main
  6. * @copyright 2001-2014 Bitrix
  7. */
  8. namespace Bitrix\Main\Data;
  9. use Bitrix\Main;
  10. use Bitrix\Main\Config;
  11. use Bitrix\Main\Diag;
  12. interface ICacheEngine
  13. {
  14. public function isAvailable();
  15. public function clean($baseDir, $initDir = false, $filename = false);
  16. public function read(&$allVars, $baseDir, $initDir, $filename, $TTL);
  17. public function write($allVars, $baseDir, $initDir, $filename, $TTL);
  18. public function isCacheExpired($path);
  19. }
  20. interface ICacheEngineStat
  21. {
  22. public function getReadBytes();
  23. public function getWrittenBytes();
  24. public function getCachePath();
  25. }
  26. class Cache
  27. {
  28. /**
  29. * @var ICacheEngine | \ICacheBackend
  30. */
  31. protected $cacheEngine;
  32. protected $content;
  33. protected $vars;
  34. protected $TTL;
  35. protected $uniqueString;
  36. protected $baseDir;
  37. protected $initDir;
  38. protected $filename;
  39. protected $isStarted = false;
  40. protected static $showCacheStat = false;
  41. protected static $clearCache = null;
  42. protected static $clearCacheSession = null;
  43. protected $forceRewriting = false;
  44. public static function createCacheEngine($params = [])
  45. {
  46. static $cacheEngine = null;
  47. if ($cacheEngine)
  48. {
  49. return clone $cacheEngine;
  50. }
  51. // Events can't be used here because events use cache
  52. $cacheType = "files";
  53. $v = Config\Configuration::getValue("cache");
  54. if ($v != null && isset($v["type"]) && !empty($v["type"]))
  55. {
  56. $cacheType = $v["type"];
  57. }
  58. if (is_array($cacheType))
  59. {
  60. if (isset($cacheType["class_name"]))
  61. {
  62. if (!isset($cacheType["extension"]) || extension_loaded($cacheType["extension"]))
  63. {
  64. if (isset($cacheType["required_file"]) && ($requiredFile = Main\Loader::getLocal($cacheType["required_file"])) !== false)
  65. {
  66. require_once($requiredFile);
  67. }
  68. if (isset($cacheType["required_remote_file"]))
  69. {
  70. require_once($cacheType["required_remote_file"]);
  71. }
  72. $className = $cacheType["class_name"];
  73. if (class_exists($className))
  74. {
  75. $cacheEngine = new $className($params);
  76. }
  77. }
  78. }
  79. }
  80. else
  81. {
  82. if ($cacheType == 'memcache' && extension_loaded('memcache'))
  83. {
  84. $cacheEngine = new CacheEngineMemcache($params);
  85. }
  86. elseif ($cacheType == 'redis' && extension_loaded('redis'))
  87. {
  88. $cacheEngine = new CacheEngineRedis($params);
  89. }
  90. elseif ($cacheType == 'apc' && extension_loaded('apc'))
  91. {
  92. $cacheEngine = new CacheEngineApc();
  93. }
  94. elseif ($cacheType == 'xcache' && extension_loaded('xcache'))
  95. {
  96. $cacheEngine = new CacheEngineXCache($params);
  97. }
  98. elseif ($cacheType == 'files')
  99. {
  100. $cacheEngine = new CacheEngineFiles($params);
  101. }
  102. elseif ($cacheType == 'none')
  103. {
  104. $cacheEngine = new CacheEngineNone($params);
  105. }
  106. }
  107. if ($cacheEngine == null)
  108. {
  109. $cacheEngine = new CacheEngineNone();
  110. trigger_error("Cache engine is not found", E_USER_WARNING);
  111. }
  112. if (!$cacheEngine->isAvailable())
  113. {
  114. $cacheEngine = new CacheEngineNone();
  115. trigger_error("Cache engine is not available", E_USER_WARNING);
  116. }
  117. return clone $cacheEngine;
  118. }
  119. public static function getCacheEngineType()
  120. {
  121. $obj = static::createCacheEngine();
  122. $class = get_class($obj);
  123. if (($pos = mb_strrpos($class, "\\")) !== false)
  124. {
  125. $class = mb_substr($class, $pos + 1);
  126. }
  127. return mb_strtolower($class);
  128. }
  129. /**
  130. * @param array $params
  131. * @return static Cache
  132. */
  133. public static function createInstance($params = [])
  134. {
  135. $cacheEngine = static::createCacheEngine($params);
  136. return new static($cacheEngine);
  137. }
  138. public function __construct($cacheEngine)
  139. {
  140. $this->cacheEngine = $cacheEngine;
  141. }
  142. public static function setShowCacheStat($showCacheStat)
  143. {
  144. static::$showCacheStat = $showCacheStat;
  145. }
  146. public static function getShowCacheStat()
  147. {
  148. return static::$showCacheStat;
  149. }
  150. /**
  151. * A privileged user wants to skip cache on this hit.
  152. * @param bool $clearCache
  153. */
  154. public static function setClearCache($clearCache)
  155. {
  156. static::$clearCache = $clearCache;
  157. }
  158. /**
  159. * A privileged user wants to skip cache on this session.
  160. * @param bool $clearCacheSession
  161. */
  162. public static function setClearCacheSession($clearCacheSession)
  163. {
  164. static::$clearCacheSession = $clearCacheSession;
  165. }
  166. public static function getSalt()
  167. {
  168. $context = Main\Application::getInstance()->getContext();
  169. $server = $context->getServer();
  170. $scriptName = $server->get("SCRIPT_NAME");
  171. if ($scriptName == "/bitrix/urlrewrite.php" && (($v = $server->get("REAL_FILE_PATH")) != null))
  172. {
  173. $scriptName = $v;
  174. }
  175. elseif ($scriptName == "/404.php" && (($v = $server->get("REAL_FILE_PATH")) != null))
  176. {
  177. $scriptName = $v;
  178. }
  179. return "/".mb_substr(md5($scriptName), 0, 3);
  180. }
  181. /**
  182. * Returns true if a privileged user wants to skip reading from cache (on this hit or session).
  183. * @return bool
  184. */
  185. public static function shouldClearCache()
  186. {
  187. global $USER;
  188. $kernelSession = null;
  189. $application = \Bitrix\Main\Application::getInstance();
  190. if ($application->isExtendedKernelInitialized())
  191. {
  192. $kernelSession = $application->getKernelSession();
  193. }
  194. if (isset(static::$clearCacheSession) || isset(static::$clearCache))
  195. {
  196. if (is_object($USER) && $USER->CanDoOperation('cache_control'))
  197. {
  198. if (isset(static::$clearCacheSession))
  199. {
  200. if (static::$clearCacheSession === true)
  201. {
  202. $kernelSession["SESS_CLEAR_CACHE"] = "Y";
  203. }
  204. else
  205. {
  206. unset($kernelSession["SESS_CLEAR_CACHE"]);
  207. }
  208. }
  209. if (isset(static::$clearCache) && (static::$clearCache === true))
  210. {
  211. return true;
  212. }
  213. }
  214. }
  215. if (isset($kernelSession["SESS_CLEAR_CACHE"]) && $kernelSession["SESS_CLEAR_CACHE"] === "Y")
  216. {
  217. return true;
  218. }
  219. return false;
  220. }
  221. public static function getPath($uniqueString)
  222. {
  223. $un = md5($uniqueString);
  224. return mb_substr($un, 0, 2)."/".$un.".php";
  225. }
  226. public function clean($uniqueString, $initDir = false, $baseDir = "cache")
  227. {
  228. $personalRoot = Main\Application::getPersonalRoot();
  229. $baseDir = $personalRoot."/".$baseDir."/";
  230. $filename = $this->getPath($uniqueString);
  231. if (static::$showCacheStat)
  232. {
  233. Diag\CacheTracker::add(0, "", $baseDir, $initDir, "/" . $filename, "C");
  234. }
  235. return $this->cacheEngine->clean($baseDir, $initDir, "/".$filename);
  236. }
  237. public function cleanDir($initDir = false, $baseDir = "cache")
  238. {
  239. $personalRoot = Main\Application::getPersonalRoot();
  240. $baseDir = $personalRoot."/".$baseDir."/";
  241. if (static::$showCacheStat)
  242. {
  243. Diag\CacheTracker::add(0, "", $baseDir, $initDir, "", "C");
  244. }
  245. return $this->cacheEngine->clean($baseDir, $initDir);
  246. }
  247. public function initCache($TTL, $uniqueString, $initDir = false, $baseDir = "cache")
  248. {
  249. if ($initDir === false)
  250. {
  251. $initDir = 'default';
  252. }
  253. $personalRoot = Main\Application::getPersonalRoot();
  254. $this->baseDir = $personalRoot."/".$baseDir."/";
  255. $this->initDir = $initDir;
  256. $this->filename = "/".$this->getPath($uniqueString);
  257. $this->TTL = $TTL;
  258. $this->uniqueString = $uniqueString;
  259. $this->vars = false;
  260. if ($TTL <= 0 || $this->forceRewriting || static::shouldClearCache())
  261. {
  262. return false;
  263. }
  264. $data = ['CONTENT' => '', 'VARS' => ''];
  265. if (!$this->cacheEngine->read($data, $this->baseDir, $this->initDir, $this->filename, $this->TTL))
  266. {
  267. return false;
  268. }
  269. if (!is_array($data) || empty($data) || !isset($data['CONTENT']) || !isset($data['VARS']))
  270. {
  271. return false;
  272. }
  273. if (static::$showCacheStat)
  274. {
  275. $read = 0;
  276. $path = '';
  277. if ($this->cacheEngine instanceof ICacheEngineStat)
  278. {
  279. $read = $this->cacheEngine->getReadBytes();
  280. $path = $this->cacheEngine->getCachePath();
  281. }
  282. elseif ($this->cacheEngine instanceof \ICacheBackend)
  283. {
  284. $read = $this->cacheEngine->read;
  285. $path = $this->cacheEngine->path;
  286. }
  287. Diag\CacheTracker::addCacheStatBytes($read);
  288. Diag\CacheTracker::add($read, $path, $this->baseDir, $this->initDir, $this->filename, "R");
  289. }
  290. $this->content = $data['CONTENT'];
  291. $this->vars = $data['VARS'];
  292. return true;
  293. }
  294. public function output()
  295. {
  296. echo $this->content;
  297. }
  298. public function getVars()
  299. {
  300. return $this->vars;
  301. }
  302. public function startDataCache($TTL = false, $uniqueString = false, $initDir = false, $vars = array(), $baseDir = "cache")
  303. {
  304. $narg = func_num_args();
  305. if ($narg <= 0)
  306. {
  307. $TTL = $this->TTL;
  308. }
  309. if ($narg <= 1)
  310. {
  311. $uniqueString = $this->uniqueString;
  312. }
  313. if ($narg <= 2)
  314. {
  315. $initDir = $this->initDir;
  316. }
  317. if ($narg <= 3)
  318. {
  319. $vars = $this->vars;
  320. }
  321. if ($this->initCache($TTL, $uniqueString, $initDir, $baseDir))
  322. {
  323. $this->output();
  324. return false;
  325. }
  326. if ($TTL <= 0)
  327. {
  328. return true;
  329. }
  330. ob_start();
  331. $this->vars = $vars;
  332. $this->isStarted = true;
  333. return true;
  334. }
  335. public function abortDataCache()
  336. {
  337. if (!$this->isStarted)
  338. {
  339. return;
  340. }
  341. $this->isStarted = false;
  342. ob_end_flush();
  343. }
  344. public function endDataCache($vars=false)
  345. {
  346. if (!$this->isStarted)
  347. {
  348. return;
  349. }
  350. $this->isStarted = false;
  351. $allVars = array(
  352. "CONTENT" => ob_get_contents(),
  353. "VARS" => ($vars!==false ? $vars : $this->vars),
  354. );
  355. $this->cacheEngine->write($allVars, $this->baseDir, $this->initDir, $this->filename, $this->TTL);
  356. if (static::$showCacheStat)
  357. {
  358. $written = 0;
  359. $path = '';
  360. if ($this->cacheEngine instanceof ICacheEngineStat)
  361. {
  362. $written = $this->cacheEngine->getWrittenBytes();
  363. $path = $this->cacheEngine->getCachePath();
  364. }
  365. elseif ($this->cacheEngine instanceof \ICacheBackend)
  366. {
  367. /** @noinspection PhpUndefinedFieldInspection */
  368. $written = $this->cacheEngine->written;
  369. /** @noinspection PhpUndefinedFieldInspection */
  370. $path = $this->cacheEngine->path;
  371. }
  372. Diag\CacheTracker::addCacheStatBytes($written);
  373. Diag\CacheTracker::add($written, $path, $this->baseDir, $this->initDir, $this->filename, "W");
  374. }
  375. if (ob_get_contents() <> '')
  376. {
  377. ob_end_flush();
  378. }
  379. else
  380. {
  381. ob_end_clean();
  382. }
  383. }
  384. public function isCacheExpired($path)
  385. {
  386. return $this->cacheEngine->isCacheExpired($path);
  387. }
  388. public function isStarted()
  389. {
  390. return $this->isStarted;
  391. }
  392. public static function clearCache($full = false, $initDir = "")
  393. {
  394. if (($full !== true) && ($full !== false) && ($initDir === "") && is_string($full))
  395. {
  396. $initDir = $full;
  397. $full = true;
  398. }
  399. $res = true;
  400. if ($full === true)
  401. {
  402. $obCache = static::createInstance();
  403. $obCache->cleanDir($initDir, "cache");
  404. }
  405. $path = Main\Loader::getPersonal("cache".$initDir);
  406. if (is_dir($path) && ($handle = opendir($path)))
  407. {
  408. while (($file = readdir($handle)) !== false)
  409. {
  410. if ($file === "." || $file === "..")
  411. {
  412. continue;
  413. }
  414. if (is_dir($path."/".$file))
  415. {
  416. if (!static::clearCache($full, $initDir."/".$file))
  417. {
  418. $res = false;
  419. }
  420. else
  421. {
  422. @chmod($path."/".$file, BX_DIR_PERMISSIONS);
  423. //We suppress error handle here because there may be valid cache files in this dir
  424. @rmdir($path."/".$file);
  425. }
  426. }
  427. elseif ($full)
  428. {
  429. @chmod($path."/".$file, BX_FILE_PERMISSIONS);
  430. if (!unlink($path."/".$file))
  431. {
  432. $res = false;
  433. }
  434. }
  435. elseif (mb_substr($file, -4) === ".php")
  436. {
  437. $c = static::createInstance();
  438. if ($c->isCacheExpired($path."/".$file))
  439. {
  440. @chmod($path."/".$file, BX_FILE_PERMISSIONS);
  441. if (!unlink($path."/".$file))
  442. {
  443. $res = false;
  444. }
  445. }
  446. }
  447. else
  448. {
  449. //We should skip unknown file
  450. //it will be deleted with full cache cleanup
  451. }
  452. }
  453. closedir($handle);
  454. }
  455. return $res;
  456. }
  457. /**
  458. * Sets the forced mode to ignore TTL and rewrite the cache.
  459. * @param bool $mode
  460. */
  461. public function forceRewriting($mode)
  462. {
  463. $this->forceRewriting = (bool) $mode;
  464. }
  465. }