See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 /** 3 * Copyright 2011-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 2011-2017 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Imap_Client 12 */ 13 14 /** 15 * An object that provides a way to identify a list of IMAP indices. 16 * 17 * @author Michael Slusarz <slusarz@horde.org> 18 * @category Horde 19 * @copyright 2011-2017 Horde LLC 20 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 21 * @package Imap_Client 22 * 23 * @property-read boolean $all Does this represent an ALL message set? 24 * @property-read array $ids The list of IDs. 25 * @property-read boolean $largest Does this represent the largest ID in use? 26 * @property-read string $max The largest ID (@since 2.20.0). 27 * @property-read string $min The smallest ID (@since 2.20.0). 28 * @property-read string $range_string Generates a range string consisting of 29 * all messages between begin and end of 30 * ID list. 31 * @property-read boolean $search_res Does this represent a search result? 32 * @property-read boolean $sequence Are these sequence IDs? If false, these 33 * are UIDs. 34 * @property-read boolean $special True if this is a "special" ID 35 * representation. 36 * @property-read string $tostring Return the non-sorted string 37 * representation. 38 * @property-read string $tostring_sort Return the sorted string 39 * representation. 40 */ 41 class Horde_Imap_Client_Ids implements Countable, Iterator, Serializable 42 { 43 /** 44 * "Special" representation constants. 45 */ 46 const ALL = "\01"; 47 const SEARCH_RES = "\02"; 48 const LARGEST = "\03"; 49 50 /** 51 * Allow duplicate IDs? 52 * 53 * @var boolean 54 */ 55 public $duplicates = false; 56 57 /** 58 * List of IDs. 59 * 60 * @var mixed 61 */ 62 protected $_ids = array(); 63 64 /** 65 * Are IDs message sequence numbers? 66 * 67 * @var boolean 68 */ 69 protected $_sequence = false; 70 71 /** 72 * Are IDs sorted? 73 * 74 * @var boolean 75 */ 76 protected $_sorted = false; 77 78 /** 79 * Constructor. 80 * 81 * @param mixed $ids See self::add(). 82 * @param boolean $sequence Are $ids message sequence numbers? 83 */ 84 public function __construct($ids = null, $sequence = false) 85 { 86 $this->add($ids); 87 $this->_sequence = $sequence; 88 } 89 90 /** 91 */ 92 public function __get($name) 93 { 94 switch ($name) { 95 case 'all': 96 return ($this->_ids === self::ALL); 97 98 case 'ids': 99 return is_array($this->_ids) 100 ? $this->_ids 101 : array(); 102 103 case 'largest': 104 return ($this->_ids === self::LARGEST); 105 106 case 'max': 107 $this->sort(); 108 return end($this->_ids); 109 110 case 'min': 111 $this->sort(); 112 return reset($this->_ids); 113 114 case 'range_string': 115 if (!count($this)) { 116 return ''; 117 } 118 119 $min = $this->min; 120 $max = $this->max; 121 122 return ($min == $max) 123 ? $min 124 : $min . ':' . $max; 125 126 case 'search_res': 127 return ($this->_ids === self::SEARCH_RES); 128 129 case 'sequence': 130 return (bool)$this->_sequence; 131 132 case 'special': 133 return is_string($this->_ids); 134 135 case 'tostring': 136 case 'tostring_sort': 137 if ($this->all) { 138 return '1:*'; 139 } elseif ($this->largest) { 140 return '*'; 141 } elseif ($this->search_res) { 142 return '$'; 143 } 144 return strval($this->_toSequenceString($name == 'tostring_sort')); 145 } 146 } 147 148 /** 149 */ 150 public function __toString() 151 { 152 return $this->tostring; 153 } 154 155 /** 156 * Add IDs to the current object. 157 * 158 * @param mixed $ids Either self::ALL, self::SEARCH_RES, self::LARGEST, 159 * Horde_Imap_Client_Ids object, array, or sequence 160 * string. 161 */ 162 public function add($ids) 163 { 164 if (!is_null($ids)) { 165 if (is_string($ids) && 166 in_array($ids, array(self::ALL, self::SEARCH_RES, self::LARGEST))) { 167 $this->_ids = $ids; 168 } elseif ($add = $this->_resolveIds($ids)) { 169 if (is_array($this->_ids) && !empty($this->_ids)) { 170 foreach ($add as $val) { 171 $this->_ids[] = $val; 172 } 173 } else { 174 $this->_ids = $add; 175 } 176 if (!$this->duplicates) { 177 $this->_ids = (count($this->_ids) > 25000) 178 ? array_unique($this->_ids) 179 : array_keys(array_flip($this->_ids)); 180 } 181 } 182 183 $this->_sorted = is_array($this->_ids) && (count($this->_ids) === 1); 184 } 185 } 186 187 /** 188 * Removed IDs from the current object. 189 * 190 * @since 2.17.0 191 * 192 * @param mixed $ids Either Horde_Imap_Client_Ids object, array, or 193 * sequence string. 194 */ 195 public function remove($ids) 196 { 197 if (!$this->isEmpty() && 198 ($remove = $this->_resolveIds($ids))) { 199 $this->_ids = array_diff($this->_ids, array_unique($remove)); 200 } 201 } 202 203 /** 204 * Is this object empty (i.e. does not contain IDs)? 205 * 206 * @return boolean True if object is empty. 207 */ 208 public function isEmpty() 209 { 210 return (is_array($this->_ids) && !count($this->_ids)); 211 } 212 213 /** 214 * Reverses the order of the IDs. 215 */ 216 public function reverse() 217 { 218 if (is_array($this->_ids)) { 219 $this->_ids = array_reverse($this->_ids); 220 } 221 } 222 223 /** 224 * Sorts the IDs. 225 */ 226 public function sort() 227 { 228 if (!$this->_sorted && is_array($this->_ids)) { 229 $this->_sort($this->_ids); 230 $this->_sorted = true; 231 } 232 } 233 234 /** 235 * Sorts the IDs numerically. 236 * 237 * @param array $ids The array list. 238 */ 239 protected function _sort(&$ids) 240 { 241 sort($ids, SORT_NUMERIC); 242 } 243 244 /** 245 * Split the sequence string at an approximate length. 246 * 247 * @since 2.7.0 248 * 249 * @param integer $length Length to split. 250 * 251 * @return array A list containing individual sequence strings. 252 */ 253 public function split($length) 254 { 255 $id = new Horde_Stream_Temp(); 256 $id->add($this->tostring_sort, true); 257 258 $out = array(); 259 260 do { 261 $out[] = $id->substring(0, $length) . $id->getToChar(','); 262 } while (!$id->eof()); 263 264 return $out; 265 } 266 267 /** 268 * Resolve the $ids input to add() and remove(). 269 * 270 * @param mixed $ids Either Horde_Imap_Client_Ids object, array, or 271 * sequence string. 272 * 273 * @return array An array of IDs. 274 */ 275 protected function _resolveIds($ids) 276 { 277 if ($ids instanceof Horde_Imap_Client_Ids) { 278 return $ids->ids; 279 } elseif (is_array($ids)) { 280 return $ids; 281 } elseif (is_string($ids) || is_integer($ids)) { 282 return is_numeric($ids) 283 ? array($ids) 284 : $this->_fromSequenceString($ids); 285 } 286 287 return array(); 288 } 289 290 /** 291 * Create an IMAP message sequence string from a list of indices. 292 * 293 * Index Format: range_start:range_end,uid,uid2,... 294 * 295 * @param boolean $sort Numerically sort the IDs before creating the 296 * range? 297 * 298 * @return string The IMAP message sequence string. 299 */ 300 protected function _toSequenceString($sort = true) 301 { 302 if (empty($this->_ids)) { 303 return ''; 304 } 305 306 $in = $this->_ids; 307 308 if ($sort && !$this->_sorted) { 309 $this->_sort($in); 310 } 311 312 $first = $last = array_shift($in); 313 $i = count($in) - 1; 314 $out = array(); 315 316 foreach ($in as $key => $val) { 317 if (($last + 1) == $val) { 318 $last = $val; 319 } 320 321 if (($i == $key) || ($last != $val)) { 322 if ($last == $first) { 323 $out[] = $first; 324 if ($i == $key) { 325 $out[] = $val; 326 } 327 } else { 328 $out[] = $first . ':' . $last; 329 if (($i == $key) && ($last != $val)) { 330 $out[] = $val; 331 } 332 } 333 $first = $last = $val; 334 } 335 } 336 337 return empty($out) 338 ? $first 339 : implode(',', $out); 340 } 341 342 /** 343 * Parse an IMAP message sequence string into a list of indices. 344 * 345 * @see _toSequenceString() 346 * 347 * @param string $str The IMAP message sequence string. 348 * 349 * @return array An array of indices. 350 */ 351 protected function _fromSequenceString($str) 352 { 353 $ids = array(); 354 $str = trim($str); 355 356 if (!strlen($str)) { 357 return $ids; 358 } 359 360 $idarray = explode(',', $str); 361 362 foreach ($idarray as $val) { 363 $range = explode(':', $val); 364 if (isset($range[1])) { 365 for ($i = min($range), $j = max($range); $i <= $j; ++$i) { 366 $ids[] = $i; 367 } 368 } else { 369 $ids[] = $val; 370 } 371 } 372 373 return $ids; 374 } 375 376 /* Countable methods. */ 377 378 /** 379 */ 380 #[ReturnTypeWillChange] 381 public function count() 382 { 383 return is_array($this->_ids) 384 ? count($this->_ids) 385 : 0; 386 } 387 388 /* Iterator methods. */ 389 390 /** 391 */ 392 #[ReturnTypeWillChange] 393 public function current() 394 { 395 return is_array($this->_ids) 396 ? current($this->_ids) 397 : null; 398 } 399 400 /** 401 */ 402 #[ReturnTypeWillChange] 403 public function key() 404 { 405 return is_array($this->_ids) 406 ? key($this->_ids) 407 : null; 408 } 409 410 /** 411 */ 412 #[ReturnTypeWillChange] 413 public function next() 414 { 415 if (is_array($this->_ids)) { 416 next($this->_ids); 417 } 418 } 419 420 /** 421 */ 422 #[ReturnTypeWillChange] 423 public function rewind() 424 { 425 if (is_array($this->_ids)) { 426 reset($this->_ids); 427 } 428 } 429 430 /** 431 */ 432 #[ReturnTypeWillChange] 433 public function valid() 434 { 435 return !is_null($this->key()); 436 } 437 438 public function serialize() 439 { 440 return serialize($this->__serialize()); 441 } 442 443 public function unserialize($data) 444 { 445 $data = @unserialize($data); 446 if (!is_array($data)) { 447 throw new Exception('Cache version change.'); 448 } 449 450 $this->__unserialize($data); 451 } 452 453 /** 454 */ 455 public function __serialize() 456 { 457 $save = array(); 458 459 if ($this->duplicates) { 460 $save['d'] = 1; 461 } 462 463 if ($this->_sequence) { 464 $save['s'] = 1; 465 } 466 467 if ($this->_sorted) { 468 $save['is'] = 1; 469 } 470 471 switch ($this->_ids) { 472 case self::ALL: 473 $save['a'] = true; 474 break; 475 476 case self::LARGEST: 477 $save['l'] = true; 478 break; 479 480 case self::SEARCH_RES: 481 $save['sr'] = true; 482 break; 483 484 default: 485 $save['i'] = strval($this); 486 break; 487 } 488 489 return $save; 490 } 491 492 /** 493 */ 494 public function __unserialize($data) 495 { 496 $this->duplicates = !empty($data['d']); 497 $this->_sequence = !empty($data['s']); 498 $this->_sorted = !empty($data['is']); 499 500 if (isset($data['a'])) { 501 $this->_ids = self::ALL; 502 } elseif (isset($data['l'])) { 503 $this->_ids = self::LARGEST; 504 } elseif (isset($data['sr'])) { 505 $this->_ids = self::SEARCH_RES; 506 } elseif (isset($data['i'])) { 507 $this->add($data['i']); 508 } 509 } 510 511 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body