PageRenderTime 42ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/core/CacheFile.php

https://github.com/CodeYellowBV/piwik
PHP | 206 lines | 121 code | 28 blank | 57 comment | 20 complexity | 14e1f49b11c6a8fcd3f03b02df0fa506 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik;
  10. use Exception;
  11. /**
  12. * This class is used to cache data on the filesystem.
  13. *
  14. * It is for example used by the Tracker process to cache various settings and websites attributes in tmp/cache/tracker/*
  15. *
  16. */
  17. class CacheFile
  18. {
  19. // for testing purposes since tests run on both CLI/FPM (changes in CLI can't invalidate
  20. // opcache in FPM, so we have to invalidate before reading)
  21. public static $invalidateOpCacheBeforeRead = false;
  22. /**
  23. * @var string
  24. */
  25. protected $cachePath;
  26. /**
  27. * @var
  28. */
  29. protected $cachePrefix;
  30. /**
  31. * Minimum enforced TTL in seconds
  32. */
  33. const MINIMUM_TTL = 60;
  34. /**
  35. * @param string $directory directory to use
  36. * @param int $timeToLiveInSeconds TTL
  37. */
  38. public function __construct($directory, $timeToLiveInSeconds = 300)
  39. {
  40. $cachePath = PIWIK_USER_PATH . '/tmp/cache/' . $directory . '/';
  41. $this->cachePath = SettingsPiwik::rewriteTmpPathWithInstanceId($cachePath);
  42. if ($timeToLiveInSeconds < self::MINIMUM_TTL) {
  43. $timeToLiveInSeconds = self::MINIMUM_TTL;
  44. }
  45. $this->ttl = $timeToLiveInSeconds;
  46. }
  47. /**
  48. * Function to fetch a cache entry
  49. *
  50. * @param string $id The cache entry ID
  51. * @return array|bool False on error, or array the cache content
  52. */
  53. public function get($id)
  54. {
  55. if (empty($id)) {
  56. return false;
  57. }
  58. $id = $this->cleanupId($id);
  59. $cache_complete = false;
  60. $content = '';
  61. $expires_on = false;
  62. // We are assuming that most of the time cache will exists
  63. $cacheFilePath = $this->cachePath . $id . '.php';
  64. if (self::$invalidateOpCacheBeforeRead) {
  65. $this->opCacheInvalidate($cacheFilePath);
  66. }
  67. $ok = @include($cacheFilePath);
  68. if ($ok && $cache_complete == true) {
  69. if (empty($expires_on)
  70. || $expires_on < time()
  71. ) {
  72. return false;
  73. }
  74. return $content;
  75. }
  76. return false;
  77. }
  78. private function getExpiresTime()
  79. {
  80. return time() + $this->ttl;
  81. }
  82. protected function cleanupId($id)
  83. {
  84. if (!Filesystem::isValidFilename($id)) {
  85. throw new Exception("Invalid cache ID request $id");
  86. }
  87. return $id;
  88. }
  89. /**
  90. * A function to store content a cache entry.
  91. *
  92. * @param string $id The cache entry ID
  93. * @param array $content The cache content
  94. * @throws \Exception
  95. * @return bool True if the entry was succesfully stored
  96. */
  97. public function set($id, $content)
  98. {
  99. if (empty($id)) {
  100. return false;
  101. }
  102. if (!is_dir($this->cachePath)) {
  103. Filesystem::mkdir($this->cachePath);
  104. }
  105. if (!is_writable($this->cachePath)) {
  106. return false;
  107. }
  108. $id = $this->cleanupId($id);
  109. $id = $this->cachePath . $id . '.php';
  110. if (is_object($content)) {
  111. throw new \Exception('You cannot use the CacheFile to cache an object, only arrays, strings and numbers.');
  112. }
  113. $cache_literal = "<" . "?php\n";
  114. $cache_literal .= "$" . "content = " . var_export($content, true) . ";\n";
  115. $cache_literal .= "$" . "expires_on = " . $this->getExpiresTime() . ";\n";
  116. $cache_literal .= "$" . "cache_complete = true;\n";
  117. $cache_literal .= "?" . ">";
  118. // Write cache to a temp file, then rename it, overwriting the old cache
  119. // On *nix systems this should guarantee atomicity
  120. $tmp_filename = tempnam($this->cachePath, 'tmp_');
  121. @chmod($tmp_filename, 0640);
  122. if ($fp = @fopen($tmp_filename, 'wb')) {
  123. @fwrite($fp, $cache_literal, strlen($cache_literal));
  124. @fclose($fp);
  125. if (!@rename($tmp_filename, $id)) {
  126. // On some systems rename() doesn't overwrite destination
  127. @unlink($id);
  128. if (!@rename($tmp_filename, $id)) {
  129. // Make sure that no temporary file is left over
  130. // if the destination is not writable
  131. @unlink($tmp_filename);
  132. }
  133. }
  134. $this->opCacheInvalidate($id);
  135. return true;
  136. }
  137. return false;
  138. }
  139. /**
  140. * A function to delete a single cache entry
  141. *
  142. * @param string $id The cache entry ID
  143. * @return bool True if the entry was succesfully deleted
  144. */
  145. public function delete($id)
  146. {
  147. if (empty($id)) {
  148. return false;
  149. }
  150. $id = $this->cleanupId($id);
  151. $filename = $this->cachePath . $id . '.php';
  152. if (file_exists($filename)) {
  153. $this->opCacheInvalidate($filename);
  154. @unlink($filename);
  155. return true;
  156. }
  157. return false;
  158. }
  159. /**
  160. * A function to delete all cache entries in the directory
  161. */
  162. public function deleteAll()
  163. {
  164. $self = $this;
  165. $beforeUnlink = function ($path) use ($self) {
  166. $self->opCacheInvalidate($path);
  167. };
  168. Filesystem::unlinkRecursive($this->cachePath, $deleteRootToo = false, $beforeUnlink);
  169. }
  170. public function opCacheInvalidate($filepath)
  171. {
  172. if (function_exists('opcache_invalidate')
  173. && is_file($filepath)
  174. ) {
  175. opcache_invalidate($filepath, $force = true);
  176. }
  177. }
  178. }