/wp-content/plugins/out-of-the-box/includes/Cache.php

https://github.com/livinglab/openlab · PHP · 378 lines · 237 code · 73 blank · 68 comment · 26 complexity · c0ef76f7268140fa1d269b94d4857078 MD5 · raw file

  1. <?php
  2. namespace TheLion\OutoftheBox;
  3. class Cache
  4. {
  5. /**
  6. * @var \TheLion\OutoftheBox\Processor
  7. */
  8. public $processor;
  9. /**
  10. * Contains the location to the cache file.
  11. *
  12. * @var string
  13. */
  14. private $_cache_location;
  15. /**
  16. * Contains the file handle in case the plugin has to work
  17. * with a file for unlocking/locking.
  18. *
  19. * @var type
  20. */
  21. private $_cache_file_handle;
  22. /**
  23. * $_nodes contains all the cached entries that are present
  24. * in the Cache File or Database.
  25. *
  26. * @var CacheNode[]
  27. */
  28. private $_nodes = [];
  29. /**
  30. * Is set to true when a change has been made in the cache.
  31. * Forcing the plugin to save the cache when needed.
  32. *
  33. * @var bool
  34. */
  35. private $_updated = false;
  36. public function __construct(Processor $processor)
  37. {
  38. $this->processor = $processor;
  39. $cache_id = get_current_blog_id();
  40. if (null !== $processor->get_current_account()) {
  41. $cache_id = $processor->get_current_account()->get_id();
  42. }
  43. $this->_cache_name = Helpers::filter_filename($cache_id, false).'.index';
  44. $this->_cache_location = OUTOFTHEBOX_CACHEDIR.$this->_cache_name;
  45. // Load Cache
  46. $this->load_cache();
  47. }
  48. public function __destruct()
  49. {
  50. $this->update_cache();
  51. }
  52. public function load_cache()
  53. {
  54. $cache = false;
  55. // 1: Try to load the Local cache when needed
  56. $cache = $this->_read_local_cache('close');
  57. // 2: Uncompress if needed
  58. if (function_exists('gzdecode') && function_exists('gzencode') && !empty($cache)) {
  59. $cache = @gzdecode($cache);
  60. }
  61. // 3: Unserialize the Cache, and reset if it became somehow corrupt
  62. if (!empty($cache) && !is_array($cache)) {
  63. $this->_unserialize_cache($cache);
  64. }
  65. }
  66. public function reset_cache()
  67. {
  68. $this->_nodes = [];
  69. $this->update_cache();
  70. }
  71. public function update_cache()
  72. {
  73. if ($this->is_updated()) {
  74. $saved = $this->_save_local_cache();
  75. }
  76. return true;
  77. }
  78. /**
  79. * @param string $value
  80. * @param string $findby
  81. *
  82. * @return CacheNode|false
  83. */
  84. public function is_cached($value, $findby = 'id')
  85. {
  86. // Find the node by ID/NAME
  87. $node = null;
  88. if ('id' === $findby) {
  89. $node = $this->get_node_by_id($value);
  90. }
  91. // Return if nothing can be found in the cache
  92. if (empty($node)) {
  93. return false;
  94. }
  95. return $node;
  96. }
  97. /**
  98. * @param \TheLion\OutoftheBox\Client\Entry\DropboxEntry $entry
  99. *
  100. * @return CacheNode
  101. */
  102. public function add_to_cache(Entry $entry)
  103. {
  104. // Check if entry is present in cache
  105. $cached_node = $this->get_node_by_id($entry->get_id());
  106. /* If entry is not yet present in the cache,
  107. * create a new CacheNode
  108. */
  109. if ((false === $cached_node)) {
  110. $cached_node = $this->add_node($entry);
  111. $this->set_updated();
  112. }
  113. // Check if the added file has another rev
  114. if ($cached_node->get_rev() !== $entry->get_rev()) {
  115. $cached_node->set_rev($entry->get_rev());
  116. // Remove the thumbnails if there is a new version available
  117. //$cached_node->remove_thumbnails();
  118. }
  119. // Return the cached CacheNode
  120. return $cached_node;
  121. }
  122. public function remove_from_cache($entry_id)
  123. {
  124. $node = $this->get_node_by_id($entry_id);
  125. $this->set_updated();
  126. return true;
  127. }
  128. public function get_node_by_id($id)
  129. {
  130. if (!isset($this->_nodes[$id])) {
  131. return false;
  132. }
  133. return $this->_nodes[$id];
  134. }
  135. public function has_nodes()
  136. {
  137. return count($this->_nodes) > 0;
  138. }
  139. /**
  140. * @return CacheNode[]
  141. */
  142. public function get_nodes()
  143. {
  144. return $this->_nodes;
  145. }
  146. public function add_node(Entry $entry)
  147. {
  148. $cached_node = new CacheNode(
  149. [
  150. '_id' => $entry->get_id(),
  151. '_account_id' => $this->get_processor()->get_current_account()->get_id(),
  152. '_path' => $entry->get_path(),
  153. '_rev' => $entry->get_rev(),
  154. ]
  155. );
  156. return $this->set_node($cached_node);
  157. }
  158. public function set_node(CacheNode $node)
  159. {
  160. $id = $node->get_id();
  161. $this->_nodes[$id] = $node;
  162. return $this->_nodes[$id];
  163. }
  164. public function is_updated()
  165. {
  166. return $this->_updated;
  167. }
  168. public function set_updated($value = true)
  169. {
  170. $this->_updated = (bool) $value;
  171. return $this->_updated;
  172. }
  173. public function get_cache_name()
  174. {
  175. return $this->_cache_name;
  176. }
  177. public function get_cache_type()
  178. {
  179. return $this->_cache_type;
  180. }
  181. public function get_cache_location()
  182. {
  183. return $this->_cache_location;
  184. }
  185. /**
  186. * @return \TheLion\OutoftheBox\Processor
  187. */
  188. public function get_processor()
  189. {
  190. return $this->processor;
  191. }
  192. protected function _read_local_cache($close = false)
  193. {
  194. $handle = $this->_get_cache_file_handle();
  195. if (empty($handle)) {
  196. $this->_create_local_lock(LOCK_SH);
  197. }
  198. clearstatcache();
  199. rewind($this->_get_cache_file_handle());
  200. $data = null;
  201. if (filesize($this->get_cache_location()) > 0) {
  202. $data = fread($this->_get_cache_file_handle(), filesize($this->get_cache_location()));
  203. }
  204. if (false !== $close) {
  205. $this->_unlock_local_cache();
  206. }
  207. return $data;
  208. }
  209. protected function _create_local_lock($type)
  210. {
  211. // Check if file exists
  212. $file = $this->get_cache_location();
  213. if (!file_exists($file)) {
  214. @file_put_contents($file, $this->_serialize_cache());
  215. if (!is_writable($file)) {
  216. // TODO log error
  217. die(sprintf('Cache file (%s) is not writable', $file));
  218. }
  219. }
  220. // Check if the file is more than 1 minute old.
  221. $requires_unlock = ((filemtime($file) + 60) < (time()));
  222. // Temporarily workaround when flock is disabled. Can cause problems when plugin is used in multiple processes
  223. if (false !== strpos(ini_get('disable_functions'), 'flock')) {
  224. $requires_unlock = false;
  225. }
  226. // Check if file is already opened and locked in this process
  227. $handle = $this->_get_cache_file_handle();
  228. if (empty($handle)) {
  229. $handle = fopen($file, 'c+');
  230. if (!is_resource($handle)) {
  231. die(sprintf('Cache file (%s) is not writable', $file));
  232. }
  233. $this->_set_cache_file_handle($handle);
  234. }
  235. @set_time_limit(60);
  236. if (!flock($this->_get_cache_file_handle(), $type)) {
  237. /*
  238. * If the file cannot be unlocked and the last time
  239. * it was modified was 1 minute, assume that
  240. * the previous process died and unlock the file manually
  241. */
  242. if ($requires_unlock) {
  243. $this->_unlock_local_cache();
  244. $handle = fopen($file, 'c+');
  245. $this->_set_cache_file_handle($handle);
  246. }
  247. // Try to lock the file again
  248. flock($this->_get_cache_file_handle(), LOCK_EX);
  249. }
  250. @set_time_limit(60);
  251. return true;
  252. }
  253. protected function _save_local_cache()
  254. {
  255. if (!$this->_create_local_lock(LOCK_EX)) {
  256. return false;
  257. }
  258. $data = $this->_serialize_cache($this);
  259. ftruncate($this->_get_cache_file_handle(), 0);
  260. rewind($this->_get_cache_file_handle());
  261. $result = fwrite($this->_get_cache_file_handle(), $data);
  262. $this->_unlock_local_cache();
  263. $this->set_updated(false);
  264. return true;
  265. }
  266. protected function _unlock_local_cache()
  267. {
  268. $handle = $this->_get_cache_file_handle();
  269. if (!empty($handle)) {
  270. flock($this->_get_cache_file_handle(), LOCK_UN);
  271. fclose($this->_get_cache_file_handle());
  272. $this->_set_cache_file_handle(null);
  273. }
  274. clearstatcache();
  275. return true;
  276. }
  277. protected function _set_cache_file_handle($handle)
  278. {
  279. return $this->_cache_file_handle = $handle;
  280. }
  281. protected function _get_cache_file_handle()
  282. {
  283. return $this->_cache_file_handle;
  284. }
  285. private function _serialize_cache()
  286. {
  287. $data = [
  288. '_nodes' => $this->_nodes,
  289. ];
  290. $data_str = serialize($data);
  291. if (function_exists('gzencode')) {
  292. $data_str = gzencode($data_str);
  293. }
  294. return $data_str;
  295. }
  296. private function _unserialize_cache($data)
  297. {
  298. $values = unserialize($data);
  299. if (false !== $values) {
  300. foreach ($values as $key => $value) {
  301. $this->{$key} = $value;
  302. }
  303. }
  304. }
  305. }