[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/horde/framework/Horde/Imap/Client/Cache/Backend/ -> Cache.php (source)

   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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1