Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402]
1 <?php 2 /** 3 * Copyright 2012-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file LICENSE 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 2012-2017 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Imap_Client 12 */ 13 14 /** 15 * An object implementing lookups between UIDs and message sequence numbers. 16 * 17 * @author Michael Slusarz <slusarz@horde.org> 18 * @category Horde 19 * @copyright 2012-2017 Horde LLC 20 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 21 * @package Imap_Client 22 * @since 2.1.0 23 * 24 * @property-read array $map The raw ID mapping data. 25 * @property-read Horde_Imap_Client_Ids $seq The sorted sequence values. 26 * @property-read Horde_Imap_Client_Ids $uids The sorted UIDs. 27 */ 28 class Horde_Imap_Client_Ids_Map implements Countable, IteratorAggregate, Serializable 29 { 30 /** 31 * Sequence -> UID mapping. 32 * 33 * @var array 34 */ 35 protected $_ids = array(); 36 37 /** 38 * Is the array sorted? 39 * 40 * @var boolean 41 */ 42 protected $_sorted = true; 43 44 /** 45 * Constructor. 46 * 47 * @param array $ids Array of sequence -> UID mapping. 48 */ 49 public function __construct(array $ids = array()) 50 { 51 $this->update($ids); 52 } 53 54 /** 55 */ 56 public function __get($name) 57 { 58 switch ($name) { 59 case 'map': 60 return $this->_ids; 61 62 case 'seq': 63 $this->sort(); 64 return new Horde_Imap_Client_Ids(array_keys($this->_ids), true); 65 66 case 'uids': 67 $this->sort(); 68 return new Horde_Imap_Client_Ids($this->_ids); 69 } 70 } 71 72 /** 73 * Updates the mapping. 74 * 75 * @param array $ids Array of sequence -> UID mapping. 76 * 77 * @return boolean True if the mapping changed. 78 */ 79 public function update($ids) 80 { 81 if (empty($ids)) { 82 return false; 83 } elseif (empty($this->_ids)) { 84 $this->_ids = $ids; 85 $change = true; 86 } else { 87 $change = false; 88 foreach ($ids as $k => $v) { 89 if (!isset($this->_ids[$k]) || ($this->_ids[$k] != $v)) { 90 $this->_ids[$k] = $v; 91 $change = true; 92 } 93 } 94 } 95 96 if ($change) { 97 $this->_sorted = false; 98 } 99 100 return $change; 101 } 102 103 /** 104 * Create a Sequence <-> UID lookup table. 105 * 106 * @param Horde_Imap_Client_Ids $ids IDs to lookup. 107 * 108 * @return array Keys are sequence numbers, values are UIDs. 109 */ 110 public function lookup(Horde_Imap_Client_Ids $ids) 111 { 112 if ($ids->all) { 113 return $this->_ids; 114 } elseif ($ids->sequence) { 115 return array_intersect_key($this->_ids, array_flip($ids->ids)); 116 } 117 118 return array_intersect($this->_ids, $ids->ids); 119 } 120 121 /** 122 * Removes messages from the ID mapping. 123 * 124 * @param Horde_Imap_Client_Ids $ids IDs to remove. 125 */ 126 public function remove(Horde_Imap_Client_Ids $ids) 127 { 128 /* For sequence numbers, we need to reindex anytime we have an index 129 * that appears equal to or after a previously seen index. If an IMAP 130 * server is smart, it will expunge in reverse order instead. */ 131 if ($ids->sequence) { 132 $remove = $ids->ids; 133 } else { 134 $ids->sort(); 135 $remove = array_reverse(array_keys($this->lookup($ids))); 136 } 137 138 if (empty($remove)) { 139 return; 140 } 141 142 $this->sort(); 143 144 if (count($remove) == count($this->_ids) && 145 !array_diff($remove, array_keys($this->_ids))) { 146 $this->_ids = array(); 147 return; 148 } 149 150 /* Find the minimum sequence number to remove. We know entries before 151 * this are untouched so no need to process them multiple times. */ 152 $first = min($remove); 153 $edit = $newids = array(); 154 foreach (array_keys($this->_ids) as $i => $seq) { 155 if ($seq >= $first) { 156 $i += (($seq == $first) ? 0 : 1); 157 $newids = array_slice($this->_ids, 0, $i, true); 158 $edit = array_slice($this->_ids, $i + (($seq == $first) ? 0 : 1), null, true); 159 break; 160 } 161 } 162 163 if (!empty($edit)) { 164 foreach ($remove as $val) { 165 $found = false; 166 $tmp = array(); 167 168 foreach (array_keys($edit) as $i => $seq) { 169 if ($found) { 170 $tmp[$seq - 1] = $edit[$seq]; 171 } elseif ($seq >= $val) { 172 $tmp = array_slice($edit, 0, ($seq == $val) ? $i : $i + 1, true); 173 $found = true; 174 } 175 } 176 177 $edit = $tmp; 178 } 179 } 180 181 $this->_ids = $newids + $edit; 182 } 183 184 /** 185 * Sort the map. 186 */ 187 public function sort() 188 { 189 if (!$this->_sorted) { 190 ksort($this->_ids, SORT_NUMERIC); 191 $this->_sorted = true; 192 } 193 } 194 195 /* Countable methods. */ 196 197 /** 198 */ 199 #[ReturnTypeWillChange] 200 public function count() 201 { 202 return count($this->_ids); 203 } 204 205 /* IteratorAggregate method. */ 206 207 /** 208 */ 209 #[ReturnTypeWillChange] 210 public function getIterator() 211 { 212 return new ArrayIterator($this->_ids); 213 } 214 215 /* Serializable methods. */ 216 217 public function serialize() 218 { 219 return serialize($this->__serialize()); 220 } 221 222 public function unserialize($data) 223 { 224 $data = @unserialize($data); 225 if (!is_array($data)) { 226 throw new Exception('Cache version change.'); 227 } 228 $this->__unserialize($data); 229 } 230 231 /** 232 */ 233 public function __serialize() 234 { 235 /* Sort before storing; provides more compressible representation. */ 236 $this->sort(); 237 238 return [ 239 strval(new Horde_Imap_Client_Ids(array_keys($this->_ids))), 240 strval(new Horde_Imap_Client_Ids(array_values($this->_ids))) 241 ]; 242 } 243 244 /** 245 */ 246 public function __unserialize($data) 247 { 248 $keys = new Horde_Imap_Client_Ids($data[0]); 249 $vals = new Horde_Imap_Client_Ids($data[1]); 250 $this->_ids = array_combine($keys->ids, $vals->ids); 251 252 /* Guaranteed to be sorted if unserializing. */ 253 $this->_sorted = true; 254 } 255 256 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body