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