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.

213 lines
5.1 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\Filesystem;
  11. use Cache\Adapter\Common\AbstractCachePool;
  12. use Cache\Adapter\Common\Exception\InvalidArgumentException;
  13. use Cache\Adapter\Common\PhpCacheItem;
  14. use League\Flysystem\FileExistsException;
  15. use League\Flysystem\FileNotFoundException;
  16. use League\Flysystem\FilesystemInterface;
  17. /**
  18. * @author Tobias Nyholm <tobias.nyholm@gmail.com>
  19. */
  20. class FilesystemCachePool extends AbstractCachePool
  21. {
  22. /**
  23. * @type FilesystemInterface
  24. */
  25. private $filesystem;
  26. /**
  27. * The folder should not begin nor end with a slash. Example: path/to/cache.
  28. *
  29. * @type string
  30. */
  31. private $folder;
  32. /**
  33. * @param FilesystemInterface $filesystem
  34. * @param string $folder
  35. */
  36. public function __construct(FilesystemInterface $filesystem, $folder = 'cache')
  37. {
  38. $this->folder = $folder;
  39. $this->filesystem = $filesystem;
  40. $this->filesystem->createDir($this->folder);
  41. }
  42. /**
  43. * @param string $folder
  44. */
  45. public function setFolder($folder)
  46. {
  47. $this->folder = $folder;
  48. }
  49. /**
  50. * {@inheritdoc}
  51. */
  52. protected function fetchObjectFromCache($key)
  53. {
  54. $empty = [false, null, [], null];
  55. $file = $this->getFilePath($key);
  56. try {
  57. $data = @unserialize($this->filesystem->read($file));
  58. if ($data === false) {
  59. return $empty;
  60. }
  61. } catch (FileNotFoundException $e) {
  62. return $empty;
  63. }
  64. // Determine expirationTimestamp from data, remove items if expired
  65. $expirationTimestamp = $data[2] ?: null;
  66. if ($expirationTimestamp !== null && time() > $expirationTimestamp) {
  67. foreach ($data[1] as $tag) {
  68. $this->removeListItem($this->getTagKey($tag), $key);
  69. }
  70. $this->forceClear($key);
  71. return $empty;
  72. }
  73. return [true, $data[0], $data[1], $expirationTimestamp];
  74. }
  75. /**
  76. * {@inheritdoc}
  77. */
  78. protected function clearAllObjectsFromCache()
  79. {
  80. $this->filesystem->deleteDir($this->folder);
  81. $this->filesystem->createDir($this->folder);
  82. return true;
  83. }
  84. /**
  85. * {@inheritdoc}
  86. */
  87. protected function clearOneObjectFromCache($key)
  88. {
  89. return $this->forceClear($key);
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. protected function storeItemInCache(PhpCacheItem $item, $ttl)
  95. {
  96. $data = serialize(
  97. [
  98. $item->get(),
  99. $item->getTags(),
  100. $item->getExpirationTimestamp(),
  101. ]
  102. );
  103. $file = $this->getFilePath($item->getKey());
  104. if ($this->filesystem->has($file)) {
  105. // Update file if it exists
  106. return $this->filesystem->update($file, $data);
  107. }
  108. try {
  109. return $this->filesystem->write($file, $data);
  110. } catch (FileExistsException $e) {
  111. // To handle issues when/if race conditions occurs, we try to update here.
  112. return $this->filesystem->update($file, $data);
  113. }
  114. }
  115. /**
  116. * @param string $key
  117. *
  118. * @throws InvalidArgumentException
  119. *
  120. * @return string
  121. */
  122. private function getFilePath($key)
  123. {
  124. if (!preg_match('|^[a-zA-Z0-9_\.! ]+$|', $key)) {
  125. throw new InvalidArgumentException(sprintf('Invalid key "%s". Valid filenames must match [a-zA-Z0-9_\.! ].', $key));
  126. }
  127. return sprintf('%s/%s', $this->folder, $key);
  128. }
  129. /**
  130. * {@inheritdoc}
  131. */
  132. protected function getList($name)
  133. {
  134. $file = $this->getFilePath($name);
  135. if (!$this->filesystem->has($file)) {
  136. $this->filesystem->write($file, serialize([]));
  137. }
  138. return unserialize($this->filesystem->read($file));
  139. }
  140. /**
  141. * {@inheritdoc}
  142. */
  143. protected function removeList($name)
  144. {
  145. $file = $this->getFilePath($name);
  146. $this->filesystem->delete($file);
  147. }
  148. /**
  149. * {@inheritdoc}
  150. */
  151. protected function appendListItem($name, $key)
  152. {
  153. $list = $this->getList($name);
  154. $list[] = $key;
  155. return $this->filesystem->update($this->getFilePath($name), serialize($list));
  156. }
  157. /**
  158. * {@inheritdoc}
  159. */
  160. protected function removeListItem($name, $key)
  161. {
  162. $list = $this->getList($name);
  163. foreach ($list as $i => $item) {
  164. if ($item === $key) {
  165. unset($list[$i]);
  166. }
  167. }
  168. return $this->filesystem->update($this->getFilePath($name), serialize($list));
  169. }
  170. /**
  171. * @param $key
  172. *
  173. * @return bool
  174. */
  175. private function forceClear($key)
  176. {
  177. try {
  178. return $this->filesystem->delete($this->getFilePath($key));
  179. } catch (FileNotFoundException $e) {
  180. return true;
  181. }
  182. }
  183. }