FileCache.php 4.3 KB

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