PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/nette/caching/src/Caching/Cache.php

https://gitlab.com/kubinos/writeoff
PHP | 376 lines | 191 code | 63 blank | 122 comment | 21 complexity | 0fd99008c85ff1530bcc3f2504c1960c MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (https://nette.org)
  4. * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  5. */
  6. namespace Nette\Caching;
  7. use Nette;
  8. use Nette\Utils\Callback;
  9. /**
  10. * Implements the cache for a application.
  11. */
  12. class Cache extends Nette\Object implements \ArrayAccess
  13. {
  14. /** dependency */
  15. const PRIORITY = 'priority',
  16. EXPIRATION = 'expire',
  17. EXPIRE = 'expire',
  18. SLIDING = 'sliding',
  19. TAGS = 'tags',
  20. FILES = 'files',
  21. ITEMS = 'items',
  22. CONSTS = 'consts',
  23. CALLBACKS = 'callbacks',
  24. ALL = 'all';
  25. /** @internal */
  26. const NAMESPACE_SEPARATOR = "\x00";
  27. /** @var IStorage */
  28. private $storage;
  29. /** @var string */
  30. private $namespace;
  31. /** @var string last query cache used by offsetGet() */
  32. private $key;
  33. /** @var mixed last query cache used by offsetGet() */
  34. private $data;
  35. public function __construct(IStorage $storage, $namespace = NULL)
  36. {
  37. $this->storage = $storage;
  38. $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
  39. }
  40. /**
  41. * Returns cache storage.
  42. * @return IStorage
  43. */
  44. public function getStorage()
  45. {
  46. return $this->storage;
  47. }
  48. /**
  49. * Returns cache namespace.
  50. * @return string
  51. */
  52. public function getNamespace()
  53. {
  54. return (string) substr($this->namespace, 0, -1);
  55. }
  56. /**
  57. * Returns new nested cache object.
  58. * @param string
  59. * @return self
  60. */
  61. public function derive($namespace)
  62. {
  63. $derived = new static($this->storage, $this->namespace . $namespace);
  64. return $derived;
  65. }
  66. /**
  67. * Reads the specified item from the cache or generate it.
  68. * @param mixed key
  69. * @param callable
  70. * @return mixed|NULL
  71. */
  72. public function load($key, $fallback = NULL)
  73. {
  74. $data = $this->storage->read($this->generateKey($key));
  75. if ($data === NULL && $fallback) {
  76. return $this->save($key, function (& $dependencies) use ($fallback) {
  77. return call_user_func_array($fallback, [& $dependencies]);
  78. });
  79. }
  80. return $data;
  81. }
  82. /**
  83. * Writes item into the cache.
  84. * Dependencies are:
  85. * - Cache::PRIORITY => (int) priority
  86. * - Cache::EXPIRATION => (timestamp) expiration
  87. * - Cache::SLIDING => (bool) use sliding expiration?
  88. * - Cache::TAGS => (array) tags
  89. * - Cache::FILES => (array|string) file names
  90. * - Cache::ITEMS => (array|string) cache items
  91. * - Cache::CONSTS => (array|string) cache items
  92. *
  93. * @param mixed key
  94. * @param mixed value
  95. * @param array dependencies
  96. * @return mixed value itself
  97. * @throws Nette\InvalidArgumentException
  98. */
  99. public function save($key, $data, array $dependencies = NULL)
  100. {
  101. $this->key = $this->data = NULL;
  102. $key = $this->generateKey($key);
  103. if ($data instanceof Nette\Callback || $data instanceof \Closure) {
  104. $this->storage->lock($key);
  105. try {
  106. $data = call_user_func_array($data, [& $dependencies]);
  107. } catch (\Throwable $e) {
  108. $this->storage->remove($key);
  109. throw $e;
  110. } catch (\Exception $e) {
  111. $this->storage->remove($key);
  112. throw $e;
  113. }
  114. }
  115. if ($data === NULL) {
  116. $this->storage->remove($key);
  117. } else {
  118. $this->storage->write($key, $data, $this->completeDependencies($dependencies, $data));
  119. return $data;
  120. }
  121. }
  122. private function completeDependencies($dp, $data)
  123. {
  124. // convert expire into relative amount of seconds
  125. if (isset($dp[self::EXPIRATION])) {
  126. $dp[self::EXPIRATION] = Nette\Utils\DateTime::from($dp[self::EXPIRATION])->format('U') - time();
  127. }
  128. // convert FILES into CALLBACKS
  129. if (isset($dp[self::FILES])) {
  130. foreach (array_unique((array) $dp[self::FILES]) as $item) {
  131. $dp[self::CALLBACKS][] = [[__CLASS__, 'checkFile'], $item, @filemtime($item)]; // @ - stat may fail
  132. }
  133. unset($dp[self::FILES]);
  134. }
  135. // add namespaces to items
  136. if (isset($dp[self::ITEMS])) {
  137. $dp[self::ITEMS] = array_unique(array_map([$this, 'generateKey'], (array) $dp[self::ITEMS]));
  138. }
  139. // convert CONSTS into CALLBACKS
  140. if (isset($dp[self::CONSTS])) {
  141. foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
  142. $dp[self::CALLBACKS][] = [[__CLASS__, 'checkConst'], $item, constant($item)];
  143. }
  144. unset($dp[self::CONSTS]);
  145. }
  146. if (!is_array($dp)) {
  147. $dp = [];
  148. }
  149. return $dp;
  150. }
  151. /**
  152. * Removes item from the cache.
  153. * @param mixed key
  154. * @return void
  155. */
  156. public function remove($key)
  157. {
  158. $this->save($key, NULL);
  159. }
  160. /**
  161. * Removes items from the cache by conditions.
  162. * Conditions are:
  163. * - Cache::PRIORITY => (int) priority
  164. * - Cache::TAGS => (array) tags
  165. * - Cache::ALL => TRUE
  166. * @return void
  167. */
  168. public function clean(array $conditions = NULL)
  169. {
  170. $this->key = $this->data = NULL;
  171. $this->storage->clean((array) $conditions);
  172. }
  173. /**
  174. * Caches results of function/method calls.
  175. * @param mixed
  176. * @return mixed
  177. */
  178. public function call($function)
  179. {
  180. $key = func_get_args();
  181. if (is_array($function) && is_object($function[0])) {
  182. $key[0][0] = get_class($function[0]);
  183. }
  184. return $this->load($key, function () use ($function, $key) {
  185. return Callback::invokeArgs($function, array_slice($key, 1));
  186. });
  187. }
  188. /**
  189. * Caches results of function/method calls.
  190. * @param mixed
  191. * @param array dependencies
  192. * @return \Closure
  193. */
  194. public function wrap($function, array $dependencies = NULL)
  195. {
  196. return function () use ($function, $dependencies) {
  197. $key = [$function, func_get_args()];
  198. if (is_array($function) && is_object($function[0])) {
  199. $key[0][0] = get_class($function[0]);
  200. }
  201. $data = $this->load($key);
  202. if ($data === NULL) {
  203. $data = $this->save($key, Callback::invokeArgs($function, $key[1]), $dependencies);
  204. }
  205. return $data;
  206. };
  207. }
  208. /**
  209. * Starts the output cache.
  210. * @param mixed key
  211. * @return OutputHelper|NULL
  212. */
  213. public function start($key)
  214. {
  215. $data = $this->load($key);
  216. if ($data === NULL) {
  217. return new OutputHelper($this, $key);
  218. }
  219. echo $data;
  220. }
  221. /**
  222. * Generates internal cache key.
  223. *
  224. * @param string
  225. * @return string
  226. */
  227. protected function generateKey($key)
  228. {
  229. return $this->namespace . md5(is_scalar($key) ? $key : serialize($key));
  230. }
  231. /********************* interface ArrayAccess ****************d*g**/
  232. /**
  233. * @deprecated
  234. */
  235. public function offsetSet($key, $data)
  236. {
  237. trigger_error('Using [] is deprecated; use Cache::save(key, data) instead.', E_USER_DEPRECATED);
  238. $this->save($key, $data);
  239. }
  240. /**
  241. * @deprecated
  242. */
  243. public function offsetGet($key)
  244. {
  245. trigger_error('Using [] is deprecated; use Cache::load(key) instead.', E_USER_DEPRECATED);
  246. $key = is_scalar($key) ? (string) $key : serialize($key);
  247. if ($this->key !== $key) {
  248. $this->key = $key;
  249. $this->data = $this->load($key);
  250. }
  251. return $this->data;
  252. }
  253. /**
  254. * @deprecated
  255. */
  256. public function offsetExists($key)
  257. {
  258. trigger_error('Using [] is deprecated; use Cache::load(key) !== NULL instead.', E_USER_DEPRECATED);
  259. $this->key = $this->data = NULL;
  260. return $this->offsetGet($key) !== NULL;
  261. }
  262. /**
  263. * @deprecated
  264. */
  265. public function offsetUnset($key)
  266. {
  267. trigger_error('Using [] is deprecated; use Cache::remove(key) instead.', E_USER_DEPRECATED);
  268. $this->save($key, NULL);
  269. }
  270. /**
  271. * @deprecated
  272. */
  273. public function release()
  274. {
  275. trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
  276. $this->key = $this->data = NULL;
  277. }
  278. /********************* dependency checkers ****************d*g**/
  279. /**
  280. * Checks CALLBACKS dependencies.
  281. * @param array
  282. * @return bool
  283. */
  284. public static function checkCallbacks($callbacks)
  285. {
  286. foreach ($callbacks as $callback) {
  287. if (!call_user_func_array(array_shift($callback), $callback)) {
  288. return FALSE;
  289. }
  290. }
  291. return TRUE;
  292. }
  293. /**
  294. * Checks CONSTS dependency.
  295. * @param string
  296. * @param mixed
  297. * @return bool
  298. */
  299. private static function checkConst($const, $value)
  300. {
  301. return defined($const) && constant($const) === $value;
  302. }
  303. /**
  304. * Checks FILES dependency.
  305. * @param string
  306. * @param int
  307. * @return bool
  308. */
  309. private static function checkFile($file, $time)
  310. {
  311. return @filemtime($file) == $time; // @ - stat may fail
  312. }
  313. }