AbstractCache.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <?php
  2. namespace League\Flysystem\Cached\Storage;
  3. use League\Flysystem\Cached\CacheInterface;
  4. use League\Flysystem\Util;
  5. abstract class AbstractCache implements CacheInterface
  6. {
  7. /**
  8. * @var bool
  9. */
  10. protected $autosave = true;
  11. /**
  12. * @var array
  13. */
  14. protected $cache = [];
  15. /**
  16. * @var array
  17. */
  18. protected $complete = [];
  19. /**
  20. * Destructor.
  21. */
  22. public function __destruct()
  23. {
  24. if (! $this->autosave) {
  25. $this->save();
  26. }
  27. }
  28. /**
  29. * Get the autosave setting.
  30. *
  31. * @return bool autosave
  32. */
  33. public function getAutosave()
  34. {
  35. return $this->autosave;
  36. }
  37. /**
  38. * Get the autosave setting.
  39. *
  40. * @param bool $autosave
  41. */
  42. public function setAutosave($autosave)
  43. {
  44. $this->autosave = $autosave;
  45. }
  46. /**
  47. * Store the contents listing.
  48. *
  49. * @param string $directory
  50. * @param array $contents
  51. * @param bool $recursive
  52. *
  53. * @return array contents listing
  54. */
  55. public function storeContents($directory, array $contents, $recursive = false)
  56. {
  57. $directories = [$directory];
  58. foreach ($contents as $object) {
  59. $this->updateObject($object['path'], $object);
  60. $object = $this->cache[$object['path']];
  61. if ($recursive && $this->pathIsInDirectory($directory, $object['path'])) {
  62. $directories[] = $object['dirname'];
  63. }
  64. }
  65. foreach (array_unique($directories) as $directory) {
  66. $this->setComplete($directory, $recursive);
  67. }
  68. $this->autosave();
  69. }
  70. /**
  71. * Update the metadata for an object.
  72. *
  73. * @param string $path object path
  74. * @param array $object object metadata
  75. * @param bool $autosave whether to trigger the autosave routine
  76. */
  77. public function updateObject($path, array $object, $autosave = false)
  78. {
  79. if (! $this->has($path)) {
  80. $this->cache[$path] = Util::pathinfo($path);
  81. }
  82. $this->cache[$path] = array_merge($this->cache[$path], $object);
  83. if ($autosave) {
  84. $this->autosave();
  85. }
  86. $this->ensureParentDirectories($path);
  87. }
  88. /**
  89. * Store object hit miss.
  90. *
  91. * @param string $path
  92. */
  93. public function storeMiss($path)
  94. {
  95. $this->cache[$path] = false;
  96. $this->autosave();
  97. }
  98. /**
  99. * Get the contents listing.
  100. *
  101. * @param string $dirname
  102. * @param bool $recursive
  103. *
  104. * @return array contents listing
  105. */
  106. public function listContents($dirname = '', $recursive = false)
  107. {
  108. $result = [];
  109. foreach ($this->cache as $object) {
  110. if ($object === false) {
  111. continue;
  112. }
  113. if ($object['dirname'] === $dirname) {
  114. $result[] = $object;
  115. } elseif ($recursive && $this->pathIsInDirectory($dirname, $object['path'])) {
  116. $result[] = $object;
  117. }
  118. }
  119. return $result;
  120. }
  121. /**
  122. * {@inheritdoc}
  123. */
  124. public function has($path)
  125. {
  126. if ($path !== false && array_key_exists($path, $this->cache)) {
  127. return $this->cache[$path] !== false;
  128. }
  129. if ($this->isComplete(Util::dirname($path), false)) {
  130. return false;
  131. }
  132. }
  133. /**
  134. * {@inheritdoc}
  135. */
  136. public function read($path)
  137. {
  138. if (isset($this->cache[$path]['contents']) && $this->cache[$path]['contents'] !== false) {
  139. return $this->cache[$path];
  140. }
  141. return false;
  142. }
  143. /**
  144. * {@inheritdoc}
  145. */
  146. public function readStream($path)
  147. {
  148. return false;
  149. }
  150. /**
  151. * {@inheritdoc}
  152. */
  153. public function rename($path, $newpath)
  154. {
  155. if ($this->has($path)) {
  156. $object = $this->cache[$path];
  157. unset($this->cache[$path]);
  158. $object['path'] = $newpath;
  159. $object = array_merge($object, Util::pathinfo($newpath));
  160. $this->cache[$newpath] = $object;
  161. $this->autosave();
  162. }
  163. }
  164. /**
  165. * {@inheritdoc}
  166. */
  167. public function copy($path, $newpath)
  168. {
  169. if ($this->has($path)) {
  170. $object = $this->cache[$path];
  171. $object = array_merge($object, Util::pathinfo($newpath));
  172. $this->updateObject($newpath, $object, true);
  173. }
  174. }
  175. /**
  176. * {@inheritdoc}
  177. */
  178. public function delete($path)
  179. {
  180. $this->storeMiss($path);
  181. }
  182. /**
  183. * {@inheritdoc}
  184. */
  185. public function deleteDir($dirname)
  186. {
  187. foreach ($this->cache as $path => $object) {
  188. if ($this->pathIsInDirectory($dirname, $path) || $path === $dirname) {
  189. unset($this->cache[$path]);
  190. }
  191. }
  192. unset($this->complete[$dirname]);
  193. $this->autosave();
  194. }
  195. /**
  196. * {@inheritdoc}
  197. */
  198. public function getMimetype($path)
  199. {
  200. if (isset($this->cache[$path]['mimetype'])) {
  201. return $this->cache[$path];
  202. }
  203. if (! $result = $this->read($path)) {
  204. return false;
  205. }
  206. $mimetype = Util::guessMimeType($path, $result['contents']);
  207. $this->cache[$path]['mimetype'] = $mimetype;
  208. return $this->cache[$path];
  209. }
  210. /**
  211. * {@inheritdoc}
  212. */
  213. public function getSize($path)
  214. {
  215. if (isset($this->cache[$path]['size'])) {
  216. return $this->cache[$path];
  217. }
  218. return false;
  219. }
  220. /**
  221. * {@inheritdoc}
  222. */
  223. public function getTimestamp($path)
  224. {
  225. if (isset($this->cache[$path]['timestamp'])) {
  226. return $this->cache[$path];
  227. }
  228. return false;
  229. }
  230. /**
  231. * {@inheritdoc}
  232. */
  233. public function getVisibility($path)
  234. {
  235. if (isset($this->cache[$path]['visibility'])) {
  236. return $this->cache[$path];
  237. }
  238. return false;
  239. }
  240. /**
  241. * {@inheritdoc}
  242. */
  243. public function getMetadata($path)
  244. {
  245. if (isset($this->cache[$path]['type'])) {
  246. return $this->cache[$path];
  247. }
  248. return false;
  249. }
  250. /**
  251. * {@inheritdoc}
  252. */
  253. public function isComplete($dirname, $recursive)
  254. {
  255. if (! array_key_exists($dirname, $this->complete)) {
  256. return false;
  257. }
  258. if ($recursive && $this->complete[$dirname] !== 'recursive') {
  259. return false;
  260. }
  261. return true;
  262. }
  263. /**
  264. * {@inheritdoc}
  265. */
  266. public function setComplete($dirname, $recursive)
  267. {
  268. $this->complete[$dirname] = $recursive ? 'recursive' : true;
  269. }
  270. /**
  271. * Filter the contents from a listing.
  272. *
  273. * @param array $contents object listing
  274. *
  275. * @return array filtered contents
  276. */
  277. public function cleanContents(array $contents)
  278. {
  279. $cachedProperties = array_flip([
  280. 'path', 'dirname', 'basename', 'extension', 'filename',
  281. 'size', 'mimetype', 'visibility', 'timestamp', 'type',
  282. ]);
  283. foreach ($contents as $path => $object) {
  284. if (is_array($object)) {
  285. $contents[$path] = array_intersect_key($object, $cachedProperties);
  286. }
  287. }
  288. return $contents;
  289. }
  290. /**
  291. * {@inheritdoc}
  292. */
  293. public function flush()
  294. {
  295. $this->cache = [];
  296. $this->complete = [];
  297. $this->autosave();
  298. }
  299. /**
  300. * {@inheritdoc}
  301. */
  302. public function autosave()
  303. {
  304. if ($this->autosave) {
  305. $this->save();
  306. }
  307. }
  308. /**
  309. * Retrieve serialized cache data.
  310. *
  311. * @return string serialized data
  312. */
  313. public function getForStorage()
  314. {
  315. $cleaned = $this->cleanContents($this->cache);
  316. return json_encode([$cleaned, $this->complete]);
  317. }
  318. /**
  319. * Load from serialized cache data.
  320. *
  321. * @param string $json
  322. */
  323. public function setFromStorage($json)
  324. {
  325. list($cache, $complete) = json_decode($json, true);
  326. if (json_last_error() === JSON_ERROR_NONE && is_array($cache) && is_array($complete)) {
  327. $this->cache = $cache;
  328. $this->complete = $complete;
  329. }
  330. }
  331. /**
  332. * Ensure parent directories of an object.
  333. *
  334. * @param string $path object path
  335. */
  336. public function ensureParentDirectories($path)
  337. {
  338. $object = $this->cache[$path];
  339. while ($object['dirname'] !== '' && ! isset($this->cache[$object['dirname']])) {
  340. $object = Util::pathinfo($object['dirname']);
  341. $object['type'] = 'dir';
  342. $this->cache[$object['path']] = $object;
  343. }
  344. }
  345. /**
  346. * Determines if the path is inside the directory.
  347. *
  348. * @param string $directory
  349. * @param string $path
  350. *
  351. * @return bool
  352. */
  353. protected function pathIsInDirectory($directory, $path)
  354. {
  355. return $directory === '' || strpos($path, $directory . '/') === 0;
  356. }
  357. }