DiskKeyCache.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * A KeyCache which streams to and from disk.
  11. *
  12. * @author Chris Corbyn
  13. */
  14. class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache
  15. {
  16. /** Signal to place pointer at start of file */
  17. const POSITION_START = 0;
  18. /** Signal to place pointer at end of file */
  19. const POSITION_END = 1;
  20. /** Signal to leave pointer in whatever position it currently is */
  21. const POSITION_CURRENT = 2;
  22. /**
  23. * An InputStream for cloning.
  24. *
  25. * @var Swift_KeyCache_KeyCacheInputStream
  26. */
  27. private $stream;
  28. /**
  29. * A path to write to.
  30. *
  31. * @var string
  32. */
  33. private $path;
  34. /**
  35. * Stored keys.
  36. *
  37. * @var array
  38. */
  39. private $keys = [];
  40. /**
  41. * Create a new DiskKeyCache with the given $stream for cloning to make
  42. * InputByteStreams, and the given $path to save to.
  43. *
  44. * @param string $path to save to
  45. */
  46. public function __construct(Swift_KeyCache_KeyCacheInputStream $stream, $path)
  47. {
  48. $this->stream = $stream;
  49. $this->path = $path;
  50. }
  51. /**
  52. * Set a string into the cache under $itemKey for the namespace $nsKey.
  53. *
  54. * @see MODE_WRITE, MODE_APPEND
  55. *
  56. * @param string $nsKey
  57. * @param string $itemKey
  58. * @param string $string
  59. * @param int $mode
  60. *
  61. * @throws Swift_IoException
  62. */
  63. public function setString($nsKey, $itemKey, $string, $mode)
  64. {
  65. $this->prepareCache($nsKey);
  66. switch ($mode) {
  67. case self::MODE_WRITE:
  68. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
  69. break;
  70. case self::MODE_APPEND:
  71. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_END);
  72. break;
  73. default:
  74. throw new Swift_SwiftException('Invalid mode ['.$mode.'] used to set nsKey='.$nsKey.', itemKey='.$itemKey);
  75. break;
  76. }
  77. fwrite($fp, $string);
  78. $this->freeHandle($nsKey, $itemKey);
  79. }
  80. /**
  81. * Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
  82. *
  83. * @see MODE_WRITE, MODE_APPEND
  84. *
  85. * @param string $nsKey
  86. * @param string $itemKey
  87. * @param int $mode
  88. *
  89. * @throws Swift_IoException
  90. */
  91. public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
  92. {
  93. $this->prepareCache($nsKey);
  94. switch ($mode) {
  95. case self::MODE_WRITE:
  96. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
  97. break;
  98. case self::MODE_APPEND:
  99. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_END);
  100. break;
  101. default:
  102. throw new Swift_SwiftException('Invalid mode ['.$mode.'] used to set nsKey='.$nsKey.', itemKey='.$itemKey);
  103. break;
  104. }
  105. while (false !== $bytes = $os->read(8192)) {
  106. fwrite($fp, $bytes);
  107. }
  108. $this->freeHandle($nsKey, $itemKey);
  109. }
  110. /**
  111. * Provides a ByteStream which when written to, writes data to $itemKey.
  112. *
  113. * NOTE: The stream will always write in append mode.
  114. *
  115. * @param string $nsKey
  116. * @param string $itemKey
  117. *
  118. * @return Swift_InputByteStream
  119. */
  120. public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
  121. {
  122. $is = clone $this->stream;
  123. $is->setKeyCache($this);
  124. $is->setNsKey($nsKey);
  125. $is->setItemKey($itemKey);
  126. if (isset($writeThrough)) {
  127. $is->setWriteThroughStream($writeThrough);
  128. }
  129. return $is;
  130. }
  131. /**
  132. * Get data back out of the cache as a string.
  133. *
  134. * @param string $nsKey
  135. * @param string $itemKey
  136. *
  137. * @throws Swift_IoException
  138. *
  139. * @return string
  140. */
  141. public function getString($nsKey, $itemKey)
  142. {
  143. $this->prepareCache($nsKey);
  144. if ($this->hasKey($nsKey, $itemKey)) {
  145. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
  146. $str = '';
  147. while (!feof($fp) && false !== $bytes = fread($fp, 8192)) {
  148. $str .= $bytes;
  149. }
  150. $this->freeHandle($nsKey, $itemKey);
  151. return $str;
  152. }
  153. }
  154. /**
  155. * Get data back out of the cache as a ByteStream.
  156. *
  157. * @param string $nsKey
  158. * @param string $itemKey
  159. * @param Swift_InputByteStream $is to write the data to
  160. */
  161. public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
  162. {
  163. if ($this->hasKey($nsKey, $itemKey)) {
  164. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
  165. while (!feof($fp) && false !== $bytes = fread($fp, 8192)) {
  166. $is->write($bytes);
  167. }
  168. $this->freeHandle($nsKey, $itemKey);
  169. }
  170. }
  171. /**
  172. * Check if the given $itemKey exists in the namespace $nsKey.
  173. *
  174. * @param string $nsKey
  175. * @param string $itemKey
  176. *
  177. * @return bool
  178. */
  179. public function hasKey($nsKey, $itemKey)
  180. {
  181. return is_file($this->path.'/'.$nsKey.'/'.$itemKey);
  182. }
  183. /**
  184. * Clear data for $itemKey in the namespace $nsKey if it exists.
  185. *
  186. * @param string $nsKey
  187. * @param string $itemKey
  188. */
  189. public function clearKey($nsKey, $itemKey)
  190. {
  191. if ($this->hasKey($nsKey, $itemKey)) {
  192. $this->freeHandle($nsKey, $itemKey);
  193. unlink($this->path.'/'.$nsKey.'/'.$itemKey);
  194. }
  195. }
  196. /**
  197. * Clear all data in the namespace $nsKey if it exists.
  198. *
  199. * @param string $nsKey
  200. */
  201. public function clearAll($nsKey)
  202. {
  203. if (\array_key_exists($nsKey, $this->keys)) {
  204. foreach ($this->keys[$nsKey] as $itemKey => $null) {
  205. $this->clearKey($nsKey, $itemKey);
  206. }
  207. if (is_dir($this->path.'/'.$nsKey)) {
  208. rmdir($this->path.'/'.$nsKey);
  209. }
  210. unset($this->keys[$nsKey]);
  211. }
  212. }
  213. /**
  214. * Initialize the namespace of $nsKey if needed.
  215. *
  216. * @param string $nsKey
  217. */
  218. private function prepareCache($nsKey)
  219. {
  220. $cacheDir = $this->path.'/'.$nsKey;
  221. if (!is_dir($cacheDir)) {
  222. if (!mkdir($cacheDir)) {
  223. throw new Swift_IoException('Failed to create cache directory '.$cacheDir);
  224. }
  225. $this->keys[$nsKey] = [];
  226. }
  227. }
  228. /**
  229. * Get a file handle on the cache item.
  230. *
  231. * @param string $nsKey
  232. * @param string $itemKey
  233. * @param int $position
  234. *
  235. * @return resource
  236. */
  237. private function getHandle($nsKey, $itemKey, $position)
  238. {
  239. if (!isset($this->keys[$nsKey][$itemKey])) {
  240. $openMode = $this->hasKey($nsKey, $itemKey) ? 'r+b' : 'w+b';
  241. $fp = fopen($this->path.'/'.$nsKey.'/'.$itemKey, $openMode);
  242. $this->keys[$nsKey][$itemKey] = $fp;
  243. }
  244. if (self::POSITION_START == $position) {
  245. fseek($this->keys[$nsKey][$itemKey], 0, SEEK_SET);
  246. } elseif (self::POSITION_END == $position) {
  247. fseek($this->keys[$nsKey][$itemKey], 0, SEEK_END);
  248. }
  249. return $this->keys[$nsKey][$itemKey];
  250. }
  251. private function freeHandle($nsKey, $itemKey)
  252. {
  253. $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_CURRENT);
  254. fclose($fp);
  255. $this->keys[$nsKey][$itemKey] = null;
  256. }
  257. /**
  258. * Destructor.
  259. */
  260. public function __destruct()
  261. {
  262. foreach ($this->keys as $nsKey => $null) {
  263. $this->clearAll($nsKey);
  264. }
  265. }
  266. public function __wakeup()
  267. {
  268. $this->keys = [];
  269. }
  270. }