[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Copyright 2005-2014 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file COPYING for license information (LGPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 7 * 8 * @category Horde 9 * @copyright 2005-2014 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Imap_Client 12 */ 13 14 /** 15 * A Horde_Cache implementation for caching IMAP/POP data. 16 * Requires the Horde_Cache package. 17 * 18 * @author Michael Slusarz <slusarz@horde.org> 19 * @category Horde 20 * @copyright 2005-2014 Horde LLC 21 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 22 * @package Imap_Client 23 */ 24 class Horde_Imap_Client_Cache_Backend_Cache extends Horde_Imap_Client_Cache_Backend 25 { 26 /** Cache structure version. */ 27 const VERSION = 3; 28 29 /** 30 * The cache object. 31 * 32 * @var Horde_Cache 33 */ 34 protected $_cache; 35 36 /** 37 * The working data for the current pageload. All changes take place to 38 * this data. 39 * 40 * @var array 41 */ 42 protected $_data = array(); 43 44 /** 45 * The list of cache slices loaded. 46 * 47 * @var array 48 */ 49 protected $_loaded = array(); 50 51 /** 52 * The mapping of UIDs to slices. 53 * 54 * @var array 55 */ 56 protected $_slicemap = array(); 57 58 /** 59 * The list of items to update: 60 * - add: (array) List of IDs that were added. 61 * - slice: (array) List of slices that were modified. 62 * - slicemap: (boolean) Was slicemap info changed? 63 * 64 * @var array 65 */ 66 protected $_update = array(); 67 68 /** 69 * Constructor. 70 * 71 * @param array $params Configuration parameters: 72 * <pre> 73 * - REQUIRED Parameters: 74 * - cacheob: (Horde_Cache) The cache object to use. 75 * 76 * - Optional Parameters: 77 * - lifetime: (integer) The lifetime of the cache data (in seconds). 78 * DEFAULT: 1 week (604800 seconds) 79 * - slicesize: (integer) The slicesize to use. 80 * DEFAULT: 50 81 * </pre> 82 */ 83 public function __construct(array $params = array()) 84 { 85 // Default parameters. 86 $params = array_merge(array( 87 'lifetime' => 604800, 88 'slicesize' => 50 89 ), array_filter($params)); 90 91 if (!isset($params['cacheob'])) { 92 throw new InvalidArgumentException('Missing cacheob parameter.'); 93 } 94 95 foreach (array('lifetime', 'slicesize') as $val) { 96 $params[$val] = intval($params[$val]); 97 } 98 99 parent::__construct($params); 100 } 101 102 /** 103 * Initialization tasks. 104 */ 105 protected function _initOb() 106 { 107 $this->_cache = $this->_params['cacheob']; 108 register_shutdown_function(array($this, 'save')); 109 } 110 111 /** 112 * Updates the cache. 113 */ 114 public function save() 115 { 116 $lifetime = $this->_params['lifetime']; 117 118 foreach ($this->_update as $mbox => $val) { 119 $s = &$this->_slicemap[$mbox]; 120 121 if (!empty($val['add'])) { 122 if ($s['c'] <= $this->_params['slicesize']) { 123 $val['slice'][] = $s['i']; 124 $this->_loadSlice($mbox, $s['i']); 125 } 126 $val['slicemap'] = true; 127 128 foreach (array_keys(array_flip($val['add'])) as $uid) { 129 if ($s['c']++ > $this->_params['slicesize']) { 130 $s['c'] = 0; 131 $val['slice'][] = ++$s['i']; 132 $this->_loadSlice($mbox, $s['i']); 133 } 134 $s['s'][$uid] = $s['i']; 135 } 136 } 137 138 if (!empty($val['slice'])) { 139 $d = &$this->_data[$mbox]; 140 $val['slicemap'] = true; 141 142 foreach (array_keys(array_flip($val['slice'])) as $slice) { 143 $data = array(); 144 foreach (array_keys($s['s'], $slice) as $uid) { 145 $data[$uid] = is_array($d[$uid]) 146 ? serialize($d[$uid]) 147 : $d[$uid]; 148 } 149 $this->_cache->set($this->_getCid($mbox, $slice), serialize($data), $lifetime); 150 } 151 } 152 153 if (!empty($val['slicemap'])) { 154 $this->_cache->set($this->_getCid($mbox, 'slicemap'), serialize($s), $lifetime); 155 } 156 } 157 158 $this->_update = array(); 159 } 160 161 /** 162 */ 163 public function get($mailbox, $uids, $fields, $uidvalid) 164 { 165 $ret = array(); 166 $this->_loadUids($mailbox, $uids, $uidvalid); 167 168 if (empty($this->_data[$mailbox])) { 169 return $ret; 170 } 171 172 if (!empty($fields)) { 173 $fields = array_flip($fields); 174 } 175 $ptr = &$this->_data[$mailbox]; 176 177 foreach (array_intersect($uids, array_keys($ptr)) as $val) { 178 if (is_string($ptr[$val])) { 179 $ptr[$val] = @unserialize($ptr[$val]); 180 } 181 182 $ret[$val] = (empty($fields) || empty($ptr[$val])) 183 ? $ptr[$val] 184 : array_intersect_key($ptr[$val], $fields); 185 } 186 187 return $ret; 188 } 189 190 /** 191 */ 192 public function getCachedUids($mailbox, $uidvalid) 193 { 194 $this->_loadSliceMap($mailbox, $uidvalid); 195 return array_unique(array_merge( 196 array_keys($this->_slicemap[$mailbox]['s']), 197 (isset($this->_update[$mailbox]) ? $this->_update[$mailbox]['add'] : array()) 198 )); 199 } 200 201 /** 202 */ 203 public function set($mailbox, $data, $uidvalid) 204 { 205 $update = array_keys($data); 206 207 try { 208 $this->_loadUids($mailbox, $update, $uidvalid); 209 } catch (Horde_Imap_Client_Exception $e) { 210 // Ignore invalidity - just start building the new cache 211 } 212 213 $d = &$this->_data[$mailbox]; 214 $s = &$this->_slicemap[$mailbox]['s']; 215 $add = $updated = array(); 216 217 foreach ($data as $k => $v) { 218 if (isset($d[$k])) { 219 if (is_string($d[$k])) { 220 $d[$k] = @unserialize($d[$k]); 221 } 222 $d[$k] = is_array($d[$k]) 223 ? array_merge($d[$k], $v) 224 : $v; 225 if (isset($s[$k])) { 226 $updated[$s[$k]] = true; 227 } 228 } else { 229 $d[$k] = $v; 230 $add[] = $k; 231 } 232 } 233 234 $this->_toUpdate($mailbox, 'add', $add); 235 $this->_toUpdate($mailbox, 'slice', array_keys($updated)); 236 } 237 238 /** 239 */ 240 public function getMetaData($mailbox, $uidvalid, $entries) 241 { 242 $this->_loadSliceMap($mailbox, $uidvalid); 243 244 return empty($entries) 245 ? $this->_slicemap[$mailbox]['d'] 246 : array_intersect_key($this->_slicemap[$mailbox]['d'], array_flip($entries)); 247 } 248 249 /** 250 */ 251 public function setMetaData($mailbox, $data) 252 { 253 $this->_loadSliceMap($mailbox, isset($data['uidvalid']) ? $data['uidvalid'] : null); 254 $this->_slicemap[$mailbox]['d'] = array_merge($this->_slicemap[$mailbox]['d'], $data); 255 $this->_toUpdate($mailbox, 'slicemap', true); 256 } 257 258 /** 259 */ 260 public function deleteMsgs($mailbox, $uids) 261 { 262 if (empty($uids)) { 263 return; 264 } 265 266 $this->_loadSliceMap($mailbox); 267 268 $slicemap = &$this->_slicemap[$mailbox]; 269 $deleted = array_intersect_key($slicemap['s'], array_flip($uids)); 270 271 if (isset($this->_update[$mailbox])) { 272 $this->_update[$mailbox]['add'] = array_diff( 273 $this->_update[$mailbox]['add'], 274 $uids 275 ); 276 } 277 278 if (empty($deleted)) { 279 return; 280 } 281 282 $this->_loadUids($mailbox, array_keys($deleted)); 283 $d = &$this->_data[$mailbox]; 284 285 foreach (array_keys($deleted) as $id) { 286 unset($d[$id], $slicemap['s'][$id]); 287 } 288 289 foreach (array_unique($deleted) as $slice) { 290 /* Get rid of slice if less than 10% of capacity. */ 291 if (($slice != $slicemap['i']) && 292 ($slice_uids = array_keys($slicemap['s'], $slice)) && 293 ($this->_params['slicesize'] * 0.1) > count($slice_uids)) { 294 $this->_toUpdate($mailbox, 'add', $slice_uids); 295 $this->_cache->expire($this->_getCid($mailbox, $slice)); 296 foreach ($slice_uids as $val) { 297 unset($slicemap['s'][$val]); 298 } 299 } else { 300 $this->_toUpdate($mailbox, 'slice', array($slice)); 301 } 302 } 303 } 304 305 /** 306 */ 307 public function deleteMailbox($mailbox) 308 { 309 $this->_loadSliceMap($mailbox); 310 $this->_deleteMailbox($mailbox); 311 } 312 313 /** 314 */ 315 public function clear($lifetime) 316 { 317 $this->_cache->clear(); 318 $this->_data = $this->_loaded = $this->_slicemap = $this->_update = array(); 319 } 320 321 /** 322 * Create the unique ID used to store the data in the cache. 323 * 324 * @param string $mailbox The mailbox to cache. 325 * @param string $slice The cache slice. 326 * 327 * @return string The cache ID. 328 */ 329 protected function _getCid($mailbox, $slice) 330 { 331 return implode('|', array( 332 'horde_imap_client', 333 $this->_params['username'], 334 $mailbox, 335 $this->_params['hostspec'], 336 $this->_params['port'], 337 $slice, 338 self::VERSION 339 )); 340 } 341 342 /** 343 * Delete a mailbox from the cache. 344 * 345 * @param string $mbox The mailbox to delete. 346 */ 347 protected function _deleteMailbox($mbox) 348 { 349 foreach (array_merge(array_keys(array_flip($this->_slicemap[$mbox]['s'])), array('slicemap')) as $slice) { 350 $cid = $this->_getCid($mbox, $slice); 351 $this->_cache->expire($cid); 352 unset($this->_loaded[$cid]); 353 } 354 355 unset( 356 $this->_data[$mbox], 357 $this->_slicemap[$mbox], 358 $this->_update[$mbox] 359 ); 360 } 361 362 /** 363 * Load UIDs by regenerating from the cache. 364 * 365 * @param string $mailbox The mailbox to load. 366 * @param array $uids The UIDs to load. 367 * @param integer $uidvalid The IMAP uidvalidity value of the mailbox. 368 */ 369 protected function _loadUids($mailbox, $uids, $uidvalid = null) 370 { 371 if (!isset($this->_data[$mailbox])) { 372 $this->_data[$mailbox] = array(); 373 } 374 375 $this->_loadSliceMap($mailbox, $uidvalid); 376 377 if (!empty($uids)) { 378 foreach (array_unique(array_intersect_key($this->_slicemap[$mailbox]['s'], array_flip($uids))) as $slice) { 379 $this->_loadSlice($mailbox, $slice); 380 } 381 } 382 } 383 384 /** 385 * Load UIDs from a cache slice. 386 * 387 * @param string $mailbox The mailbox to load. 388 * @param integer $slice The slice to load. 389 */ 390 protected function _loadSlice($mailbox, $slice) 391 { 392 $cache_id = $this->_getCid($mailbox, $slice); 393 394 if (!empty($this->_loaded[$cache_id])) { 395 return; 396 } 397 398 if ((($data = $this->_cache->get($cache_id, 0)) !== false) && 399 ($data = @unserialize($data)) && 400 is_array($data)) { 401 $this->_data[$mailbox] += $data; 402 $this->_loaded[$cache_id] = true; 403 } else { 404 $ptr = &$this->_slicemap[$mailbox]; 405 406 // Slice data is corrupt; remove from slicemap. 407 foreach (array_keys($ptr['s'], $slice) as $val) { 408 unset($ptr['s'][$val]); 409 } 410 411 if ($slice == $ptr['i']) { 412 $ptr['c'] = 0; 413 } 414 } 415 } 416 417 /** 418 * Load the slicemap for a given mailbox. The slicemap contains 419 * the uidvalidity information, the UIDs->slice lookup table, and any 420 * metadata that needs to be saved for the mailbox. 421 * 422 * @param string $mailbox The mailbox. 423 * @param integer $uidvalid The IMAP uidvalidity value of the mailbox. 424 */ 425 protected function _loadSliceMap($mailbox, $uidvalid = null) 426 { 427 if (!isset($this->_slicemap[$mailbox]) && 428 (($data = $this->_cache->get($this->_getCid($mailbox, 'slicemap'), 0)) !== false) && 429 ($slice = @unserialize($data)) && 430 is_array($slice)) { 431 $this->_slicemap[$mailbox] = $slice; 432 } 433 434 if (isset($this->_slicemap[$mailbox])) { 435 $ptr = &$this->_slicemap[$mailbox]; 436 if (is_null($ptr['d']['uidvalid'])) { 437 $ptr['d']['uidvalid'] = $uidvalid; 438 return; 439 } elseif (!is_null($uidvalid) && 440 ($ptr['d']['uidvalid'] != $uidvalid)) { 441 $this->_deleteMailbox($mailbox); 442 } else { 443 return; 444 } 445 } 446 447 $this->_slicemap[$mailbox] = array( 448 // Tracking count for purposes of determining slices 449 'c' => 0, 450 // Metadata storage 451 // By default includes UIDVALIDITY of mailbox. 452 'd' => array('uidvalid' => $uidvalid), 453 // The ID of the last slice. 454 'i' => 0, 455 // The slice list. 456 's' => array() 457 ); 458 } 459 460 /** 461 * Add update entry for a mailbox. 462 * 463 * @param string $mailbox The mailbox. 464 * @param string $type 'add', 'slice', or 'slicemap'. 465 * @param mixed $data The data to update. 466 */ 467 protected function _toUpdate($mailbox, $type, $data) 468 { 469 if (!isset($this->_update[$mailbox])) { 470 $this->_update[$mailbox] = array( 471 'add' => array(), 472 'slice' => array() 473 ); 474 } 475 476 $this->_update[$mailbox][$type] = ($type == 'slicemap') 477 ? $data 478 : array_merge($this->_update[$mailbox][$type], $data); 479 } 480 481 /* Serializable methods. */ 482 483 /** 484 */ 485 public function serialize() 486 { 487 $this->save(); 488 return parent::serialize(); 489 } 490 491 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |