FileCache.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. namespace ForkBB\Core\Cache;
  3. use RuntimeException;
  4. use InvalidArgumentException;
  5. class FileCache implements ProviderCacheInterface
  6. {
  7. /**
  8. * Директория кэша
  9. * @var string
  10. */
  11. protected $cacheDir;
  12. /**
  13. * Конструктор
  14. *
  15. * @param string $dir
  16. *
  17. * @throws InvalidArgumentException
  18. * @throws RuntimeException
  19. */
  20. public function __construct($dir)
  21. {
  22. if (empty($dir) || ! \is_string($dir)) {
  23. throw new InvalidArgumentException('Cache directory must be set to a string');
  24. } elseif (! \is_dir($dir)) {
  25. throw new RuntimeException("`$dir`: Not a directory");
  26. } elseif (! \is_writable($dir)) {
  27. throw new RuntimeException("No write access to `$dir` directory");
  28. }
  29. $this->cacheDir = $dir;
  30. }
  31. /**
  32. * Получение данных из кэша по ключу
  33. *
  34. * @param string $key
  35. * @param mixed $default
  36. *
  37. * @return mixed
  38. */
  39. public function get($key, $default = null)
  40. {
  41. $file = $this->file($key);
  42. if (\is_file($file)) {
  43. require $file;
  44. if (isset($expire) && isset($data)
  45. && ($expire < 1 || $expire > \time())
  46. ) {
  47. return $data;
  48. }
  49. }
  50. return $default;
  51. }
  52. /**
  53. * Установка данных в кэш по ключу
  54. *
  55. * @param string $key
  56. * @param mixed $value
  57. * @param int $ttl
  58. *
  59. * @throws RuntimeException
  60. *
  61. * @return bool
  62. */
  63. public function set($key, $value, $ttl = null)
  64. {
  65. $file = $this->file($key);
  66. $expire = null === $ttl || $ttl < 1 ? 0 : \time() + $ttl;
  67. $content = "<?php\n\n" . '$expire = ' . $expire . ";\n\n" . '$data = ' . \var_export($value, true) . ";\n";
  68. if (false === \file_put_contents($file, $content, \LOCK_EX)) {
  69. throw new RuntimeException("The key '$key' can not be saved");
  70. } else {
  71. $this->invalidate($file);
  72. return true;
  73. }
  74. }
  75. /**
  76. * Удаление данных по ключу
  77. *
  78. * @param string $key
  79. *
  80. * @throws RuntimeException
  81. *
  82. * @return bool
  83. */
  84. public function delete($key)
  85. {
  86. $file = $this->file($key);
  87. if (\is_file($file)) {
  88. if (\unlink($file)) {
  89. $this->invalidate($file);
  90. return true;
  91. } else {
  92. throw new RuntimeException("The key `$key` could not be removed");
  93. }
  94. } else {
  95. return true;
  96. }
  97. }
  98. /**
  99. * Очистка кэша
  100. *
  101. * @return bool
  102. */
  103. public function clear()
  104. {
  105. $d = \dir($this->cacheDir);
  106. if (! $d) {
  107. return false;
  108. }
  109. $result = true;
  110. while (($entry = $d->read()) !== false) {
  111. if (\substr($entry, -4) == '.php') {
  112. $f = \unlink($this->cacheDir . '/' . $entry);
  113. $result = $result && $f;
  114. }
  115. }
  116. $d->close();
  117. return $result;
  118. }
  119. /**
  120. * Проверка наличия ключа
  121. *
  122. * @param string $key
  123. *
  124. * @return bool
  125. */
  126. public function has($key)
  127. {
  128. return null !== $this->get($key);
  129. }
  130. /**
  131. * Генерация имени файла по ключу
  132. *
  133. * @param string $key
  134. *
  135. * @throws InvalidArgumentException
  136. *
  137. * @return string
  138. */
  139. protected function file($key)
  140. {
  141. if (\is_string($key) && \preg_match('%^[a-z0-9_-]+$%Di', $key)) {
  142. return $this->cacheDir . '/cache_' . $key . '.php';
  143. }
  144. throw new InvalidArgumentException("Key '$key' contains invalid characters.");
  145. }
  146. /**
  147. * Очистка opcache и apc от закэшированного файла
  148. *
  149. * @param string $file
  150. */
  151. protected function invalidate($file)
  152. {
  153. if (\function_exists('opcache_invalidate')) {
  154. \opcache_invalidate($file, true);
  155. } elseif (\function_exists('apc_delete_file')) {
  156. \apc_delete_file($file);
  157. }
  158. }
  159. }