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