PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/public/infocaptor/dash/social/include/google_api/Google/Cache/File.php

https://bitbucket.org/mad3linux/bigbox
PHP | 247 lines | 186 code | 26 blank | 35 comment | 24 complexity | fe49299eae404034cce024c1b934ed24 MD5 | raw file
Possible License(s): GPL-2.0, MIT, Apache-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /*
  3. * Copyright 2008 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. use Google\Auth\CacheInterface;
  18. use Psr\Log\LoggerInterface;
  19. /*
  20. * This class implements a basic on disk storage. While that does
  21. * work quite well it's not the most elegant and scalable solution.
  22. * It will also get you into a heap of trouble when you try to run
  23. * this in a clustered environment.
  24. *
  25. * @author Chris Chabot <chabotc@google.com>
  26. */
  27. class Google_Cache_File implements CacheInterface
  28. {
  29. const MAX_LOCK_RETRIES = 10;
  30. private $path;
  31. private $fh;
  32. /**
  33. * @var use Psr\Log\LoggerInterface logger
  34. */
  35. private $logger;
  36. public function __construct($path, LoggerInterface $logger = null)
  37. {
  38. $this->path = $path;
  39. $this->logger = $logger;
  40. }
  41. public function get($key, $expiration = false)
  42. {
  43. $storageFile = $this->getCacheFile($key);
  44. $data = false;
  45. if (!file_exists($storageFile)) {
  46. $this->log(
  47. 'debug',
  48. 'File cache miss',
  49. array('key' => $key, 'file' => $storageFile)
  50. );
  51. return false;
  52. }
  53. if ($expiration) {
  54. $mtime = filemtime($storageFile);
  55. if ((time() - $mtime) >= $expiration) {
  56. $this->log(
  57. 'debug',
  58. 'File cache miss (expired)',
  59. array('key' => $key, 'file' => $storageFile)
  60. );
  61. $this->delete($key);
  62. return false;
  63. }
  64. }
  65. if ($this->acquireReadLock($storageFile)) {
  66. if (filesize($storageFile) > 0) {
  67. $data = fread($this->fh, filesize($storageFile));
  68. $data = unserialize($data);
  69. } else {
  70. $this->log(
  71. 'debug',
  72. 'Cache file was empty',
  73. array('file' => $storageFile)
  74. );
  75. }
  76. $this->unlock();
  77. }
  78. $this->log(
  79. 'debug',
  80. 'File cache hit',
  81. array('key' => $key, 'file' => $storageFile, 'var' => $data)
  82. );
  83. return $data;
  84. }
  85. public function set($key, $value)
  86. {
  87. $storageFile = $this->getWriteableCacheFile($key);
  88. if ($this->acquireWriteLock($storageFile)) {
  89. // We serialize the whole request object, since we don't only want the
  90. // responseContent but also the postBody used, headers, size, etc.
  91. $data = serialize($value);
  92. fwrite($this->fh, $data);
  93. $this->unlock();
  94. $this->log(
  95. 'debug',
  96. 'File cache set',
  97. array('key' => $key, 'file' => $storageFile, 'var' => $value)
  98. );
  99. } else {
  100. $this->log(
  101. 'notice',
  102. 'File cache set failed',
  103. array('key' => $key, 'file' => $storageFile)
  104. );
  105. }
  106. }
  107. public function delete($key)
  108. {
  109. $file = $this->getCacheFile($key);
  110. if (file_exists($file) && !unlink($file)) {
  111. $this->log(
  112. 'error',
  113. 'File cache delete failed',
  114. array('key' => $key, 'file' => $file)
  115. );
  116. throw new Google_Cache_Exception("Cache file could not be deleted");
  117. }
  118. $this->log(
  119. 'debug',
  120. 'File cache delete',
  121. array('key' => $key, 'file' => $file)
  122. );
  123. }
  124. private function getWriteableCacheFile($file)
  125. {
  126. return $this->getCacheFile($file, true);
  127. }
  128. private function getCacheFile($file, $forWrite = false)
  129. {
  130. return $this->getCacheDir($file, $forWrite) . '/' . md5($file);
  131. }
  132. private function getCacheDir($file, $forWrite)
  133. {
  134. // use the first 2 characters of the hash as a directory prefix
  135. // this should prevent slowdowns due to huge directory listings
  136. // and thus give some basic amount of scalability
  137. $fileHash = substr(md5($file), 0, 2);
  138. $userHash = md5(get_current_user());
  139. $dirHash = $userHash . DIRECTORY_SEPARATOR . $fileHash;
  140. // trim the directory separator from the path to prevent double separators
  141. $rootCacheDir = rtrim($this->path, DIRECTORY_SEPARATOR);
  142. $storageDir = $rootCacheDir . DIRECTORY_SEPARATOR . $dirHash;
  143. if ($forWrite && !is_dir($storageDir)) {
  144. // create root dir
  145. if (!is_dir($rootCacheDir)) {
  146. if (!mkdir($rootCacheDir, 0777, true)) {
  147. $this->log(
  148. 'error',
  149. 'File cache creation failed',
  150. array('dir' => $rootCacheDir)
  151. );
  152. throw new Google_Cache_Exception("Could not create cache directory: $rootCacheDir");
  153. }
  154. }
  155. // create dir for file
  156. if (!mkdir($storageDir, 0700, true)) {
  157. $this->log(
  158. 'error',
  159. 'File cache creation failed',
  160. array('dir' => $storageDir)
  161. );
  162. throw new Google_Cache_Exception("Could not create cache directory: $storageDir");
  163. }
  164. }
  165. return $storageDir;
  166. }
  167. private function acquireReadLock($storageFile)
  168. {
  169. return $this->acquireLock(LOCK_SH, $storageFile);
  170. }
  171. private function acquireWriteLock($storageFile)
  172. {
  173. $rc = $this->acquireLock(LOCK_EX, $storageFile);
  174. if (!$rc) {
  175. $this->log(
  176. 'notice',
  177. 'File cache write lock failed',
  178. array('file' => $storageFile)
  179. );
  180. $this->delete($storageFile);
  181. }
  182. return $rc;
  183. }
  184. private function acquireLock($type, $storageFile)
  185. {
  186. $mode = $type == LOCK_EX ? "w" : "r";
  187. $this->fh = fopen($storageFile, $mode);
  188. if (!$this->fh) {
  189. $this->log(
  190. 'error',
  191. 'Failed to open file during lock acquisition',
  192. array('file' => $storageFile)
  193. );
  194. return false;
  195. }
  196. if ($type == LOCK_EX) {
  197. chmod($storageFile, 0600);
  198. }
  199. $count = 0;
  200. while (!flock($this->fh, $type | LOCK_NB)) {
  201. // Sleep for 10ms.
  202. usleep(10000);
  203. if (++$count < self::MAX_LOCK_RETRIES) {
  204. return false;
  205. }
  206. }
  207. return true;
  208. }
  209. public function unlock()
  210. {
  211. if ($this->fh) {
  212. flock($this->fh, LOCK_UN);
  213. }
  214. }
  215. private function log($level, $message, $context = array())
  216. {
  217. if ($this->logger) {
  218. $this->logger->log($level, $message, $context);
  219. }
  220. }
  221. }