You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
6.2 KiB

2 years ago
  1. <?php
  2. /*
  3. * This file is part of php-cache organization.
  4. *
  5. * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Cache\Adapter\Common;
  11. use Cache\Adapter\Common\Exception\InvalidArgumentException;
  12. use Cache\TagInterop\TaggableCacheItemInterface;
  13. /**
  14. * @author Aaron Scherer <aequasi@gmail.com>
  15. * @author Tobias Nyholm <tobias.nyholm@gmail.com>
  16. */
  17. class CacheItem implements PhpCacheItem
  18. {
  19. /**
  20. * @type array
  21. */
  22. private $prevTags = [];
  23. /**
  24. * @type array
  25. */
  26. private $tags = [];
  27. /**
  28. * @type \Closure
  29. */
  30. private $callable;
  31. /**
  32. * @type string
  33. */
  34. private $key;
  35. /**
  36. * @type mixed
  37. */
  38. private $value;
  39. /**
  40. * The expiration timestamp is the source of truth. This is the UTC timestamp
  41. * when the cache item expire. A value of zero means it never expires. A nullvalue
  42. * means that no expiration is set.
  43. *
  44. * @type int|null
  45. */
  46. private $expirationTimestamp = null;
  47. /**
  48. * @type bool
  49. */
  50. private $hasValue = false;
  51. /**
  52. * @param string $key
  53. * @param \Closure|bool $callable or boolean hasValue
  54. */
  55. public function __construct($key, $callable = null, $value = null)
  56. {
  57. $this->key = $key;
  58. if ($callable === true) {
  59. $this->hasValue = true;
  60. $this->value = $value;
  61. } elseif ($callable !== false) {
  62. // This must be a callable or null
  63. $this->callable = $callable;
  64. }
  65. }
  66. /**
  67. * {@inheritdoc}
  68. */
  69. public function getKey()
  70. {
  71. return $this->key;
  72. }
  73. /**
  74. * {@inheritdoc}
  75. */
  76. public function set($value)
  77. {
  78. $this->value = $value;
  79. $this->hasValue = true;
  80. $this->callable = null;
  81. return $this;
  82. }
  83. /**
  84. * {@inheritdoc}
  85. */
  86. public function get()
  87. {
  88. if (!$this->isHit()) {
  89. return;
  90. }
  91. return $this->value;
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public function isHit()
  97. {
  98. $this->initialize();
  99. if (!$this->hasValue) {
  100. return false;
  101. }
  102. if ($this->expirationTimestamp !== null) {
  103. return $this->expirationTimestamp > time();
  104. }
  105. return true;
  106. }
  107. /**
  108. * {@inheritdoc}
  109. */
  110. public function getExpirationTimestamp()
  111. {
  112. return $this->expirationTimestamp;
  113. }
  114. /**
  115. * {@inheritdoc}
  116. */
  117. public function expiresAt($expiration)
  118. {
  119. if ($expiration instanceof \DateTimeInterface) {
  120. $this->expirationTimestamp = $expiration->getTimestamp();
  121. } elseif (is_int($expiration) || null === $expiration) {
  122. $this->expirationTimestamp = $expiration;
  123. } else {
  124. throw new InvalidArgumentException('Cache item ttl/expiresAt must be of type integer or \DateTimeInterface.');
  125. }
  126. return $this;
  127. }
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function expiresAfter($time)
  132. {
  133. if ($time === null) {
  134. $this->expirationTimestamp = null;
  135. } elseif ($time instanceof \DateInterval) {
  136. $date = new \DateTime();
  137. $date->add($time);
  138. $this->expirationTimestamp = $date->getTimestamp();
  139. } elseif (is_int($time)) {
  140. $this->expirationTimestamp = time() + $time;
  141. } else {
  142. throw new InvalidArgumentException('Cache item ttl/expiresAfter must be of type integer or \DateInterval.');
  143. }
  144. return $this;
  145. }
  146. /**
  147. * {@inheritdoc}
  148. */
  149. public function getPreviousTags()
  150. {
  151. $this->initialize();
  152. return $this->prevTags;
  153. }
  154. /**
  155. * {@inheritdoc}
  156. */
  157. public function getTags()
  158. {
  159. return $this->tags;
  160. }
  161. /**
  162. * {@inheritdoc}
  163. */
  164. public function setTags(array $tags)
  165. {
  166. $this->tags = [];
  167. $this->tag($tags);
  168. return $this;
  169. }
  170. /**
  171. * Adds a tag to a cache item.
  172. *
  173. * @param string|string[] $tags A tag or array of tags
  174. *
  175. * @throws InvalidArgumentException When $tag is not valid.
  176. *
  177. * @return TaggableCacheItemInterface
  178. */
  179. private function tag($tags)
  180. {
  181. $this->initialize();
  182. if (!is_array($tags)) {
  183. $tags = [$tags];
  184. }
  185. foreach ($tags as $tag) {
  186. if (!is_string($tag)) {
  187. throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given', is_object($tag) ? get_class($tag) : gettype($tag)));
  188. }
  189. if (isset($this->tags[$tag])) {
  190. continue;
  191. }
  192. if (!isset($tag[0])) {
  193. throw new InvalidArgumentException('Cache tag length must be greater than zero');
  194. }
  195. if (isset($tag[strcspn($tag, '{}()/\@:')])) {
  196. throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:', $tag));
  197. }
  198. $this->tags[$tag] = $tag;
  199. }
  200. return $this;
  201. }
  202. /**
  203. * If callable is not null, execute it an populate this object with values.
  204. */
  205. private function initialize()
  206. {
  207. if ($this->callable !== null) {
  208. // $func will be $adapter->fetchObjectFromCache();
  209. $func = $this->callable;
  210. $result = $func();
  211. $this->hasValue = $result[0];
  212. $this->value = $result[1];
  213. $this->prevTags = isset($result[2]) ? $result[2] : [];
  214. $this->expirationTimestamp = null;
  215. if (isset($result[3]) && is_int($result[3])) {
  216. $this->expirationTimestamp = $result[3];
  217. }
  218. $this->callable = null;
  219. }
  220. }
  221. /**
  222. * @internal This function should never be used and considered private.
  223. *
  224. * Move tags from $tags to $prevTags
  225. */
  226. public function moveTagsToPrevious()
  227. {
  228. $this->prevTags = $this->tags;
  229. $this->tags = [];
  230. }
  231. }