FileCache.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <?php
  2. namespace Drupal\Component\FileCache;
  3. /**
  4. * Allows to cache data based on file modification dates.
  5. */
  6. class FileCache implements FileCacheInterface {
  7. /**
  8. * Prefix that is used for cache entries.
  9. *
  10. * @var string
  11. */
  12. protected $prefix;
  13. /**
  14. * Static cache that contains already loaded cache entries.
  15. *
  16. * @var array
  17. */
  18. protected static $cached = [];
  19. /**
  20. * The collection identifier of this cache.
  21. *
  22. * @var string
  23. */
  24. protected $collection;
  25. /**
  26. * The cache backend backing this FileCache object.
  27. *
  28. * @var \Drupal\Component\FileCache\FileCacheBackendInterface
  29. */
  30. protected $cache;
  31. /**
  32. * Constructs a FileCache object.
  33. *
  34. * @param string $prefix
  35. * The cache prefix.
  36. * @param string $collection
  37. * A collection identifier to ensure that the same files could be cached for
  38. * different purposes without clashing.
  39. * @param string|null $cache_backend_class
  40. * (optional) The class that should be used as cache backend.
  41. * @param array $cache_backend_configuration
  42. * (optional) The configuration for the backend class.
  43. */
  44. public function __construct($prefix, $collection, $cache_backend_class = NULL, array $cache_backend_configuration = []) {
  45. if (empty($prefix)) {
  46. throw new \InvalidArgumentException('Required prefix configuration is missing');
  47. }
  48. $this->prefix = $prefix;
  49. $this->collection = $collection;
  50. if (isset($cache_backend_class)) {
  51. $this->cache = new $cache_backend_class($cache_backend_configuration);
  52. }
  53. }
  54. /**
  55. * {@inheritdoc}
  56. */
  57. public function get($filepath) {
  58. $filepaths = [$filepath];
  59. $cached = $this->getMultiple($filepaths);
  60. return isset($cached[$filepath]) ? $cached[$filepath] : NULL;
  61. }
  62. /**
  63. * {@inheritdoc}
  64. */
  65. public function getMultiple(array $filepaths) {
  66. $file_data = [];
  67. $remaining_cids = [];
  68. // First load from the static cache what we can.
  69. foreach ($filepaths as $filepath) {
  70. if (!file_exists($filepath)) {
  71. continue;
  72. }
  73. $realpath = realpath($filepath);
  74. // If the file exists but realpath returns nothing, it is using a stream
  75. // wrapper, those are not supported.
  76. if (empty($realpath)) {
  77. continue;
  78. }
  79. $cid = $this->prefix . ':' . $this->collection . ':' . $realpath;
  80. if (isset(static::$cached[$cid]) && static::$cached[$cid]['mtime'] == filemtime($filepath)) {
  81. $file_data[$filepath] = static::$cached[$cid]['data'];
  82. }
  83. else {
  84. // Collect a list of cache IDs that we still need to fetch from cache
  85. // backend.
  86. $remaining_cids[$cid] = $filepath;
  87. }
  88. }
  89. // If there are any cache IDs left to fetch from the cache backend.
  90. if ($remaining_cids && $this->cache) {
  91. $cache_results = $this->cache->fetch(array_keys($remaining_cids)) ?: [];
  92. foreach ($cache_results as $cid => $cached) {
  93. $filepath = $remaining_cids[$cid];
  94. if ($cached['mtime'] == filemtime($filepath)) {
  95. $file_data[$cached['filepath']] = $cached['data'];
  96. static::$cached[$cid] = $cached;
  97. }
  98. }
  99. }
  100. return $file_data;
  101. }
  102. /**
  103. * {@inheritdoc}
  104. */
  105. public function set($filepath, $data) {
  106. $realpath = realpath($filepath);
  107. $cached = [
  108. 'mtime' => filemtime($filepath),
  109. 'filepath' => $filepath,
  110. 'data' => $data,
  111. ];
  112. $cid = $this->prefix . ':' . $this->collection . ':' . $realpath;
  113. static::$cached[$cid] = $cached;
  114. if ($this->cache) {
  115. $this->cache->store($cid, $cached);
  116. }
  117. }
  118. /**
  119. * {@inheritdoc}
  120. */
  121. public function delete($filepath) {
  122. $realpath = realpath($filepath);
  123. $cid = $this->prefix . ':' . $this->collection . ':' . $realpath;
  124. unset(static::$cached[$cid]);
  125. if ($this->cache) {
  126. $this->cache->delete($cid);
  127. }
  128. }
  129. /**
  130. * Resets the static cache.
  131. *
  132. * @todo Replace this once https://www.drupal.org/node/2260187 is in.
  133. */
  134. public static function reset() {
  135. static::$cached = [];
  136. }
  137. }