Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 /** 3 * Copyright 2012-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file LICENSE for license information (BSD). If you 6 * did not receive this file, see http://www.horde.org/licenses/bsd. 7 * 8 * @category Horde 9 * @copyright 2012-2017 Horde LLC 10 * @license http://www.horde.org/licenses/bsd New BSD License 11 * @package Mail 12 */ 13 14 /** 15 * Container object for a collection of RFC 822 elements. 16 * 17 * @author Michael Slusarz <slusarz@horde.org> 18 * @category Horde 19 * @copyright 2012-2017 Horde LLC 20 * @license http://www.horde.org/licenses/bsd New BSD License 21 * @package Mail 22 * 23 * @property-read array $addresses The list of all addresses (address 24 * w/personal parts). 25 * @property-read array $bare_addresses The list of all addresses (mail@host). 26 * @property-read array $bare_addresses_idn The list of all addresses 27 * (mail@host; IDN encoded). 28 * (@since 2.1.0) 29 * @property-read array $base_addresses The list of ONLY base addresses 30 * (Address objects). 31 * @property-read array $raw_addresses The list of all addresses (Address 32 * objects). 33 */ 34 class Horde_Mail_Rfc822_List 35 extends Horde_Mail_Rfc822_Object 36 implements ArrayAccess, Countable, SeekableIterator, Serializable 37 { 38 /** Filter masks. */ 39 const HIDE_GROUPS = 1; 40 const BASE_ELEMENTS = 2; 41 42 /** 43 * List data. 44 * 45 * @var array 46 */ 47 protected $_data = array(); 48 49 /** 50 * Current Iterator filter. 51 * 52 * @var array 53 */ 54 protected $_filter = array(); 55 56 /** 57 * Current Iterator pointer. 58 * 59 * @var array 60 */ 61 protected $_ptr; 62 63 /** 64 * Constructor. 65 * 66 * @param mixed $obs Address data to store in this object. 67 */ 68 public function __construct($obs = null) 69 { 70 if (!is_null($obs)) { 71 $this->add($obs); 72 } 73 } 74 75 /** 76 */ 77 public function __get($name) 78 { 79 switch ($name) { 80 case 'addresses': 81 case 'bare_addresses': 82 case 'bare_addresses_idn': 83 case 'base_addresses': 84 case 'raw_addresses': 85 $old = $this->_filter; 86 $mask = ($name == 'base_addresses') 87 ? self::BASE_ELEMENTS 88 : self::HIDE_GROUPS; 89 $this->setIteratorFilter($mask, empty($old['filter']) ? null : $old['filter']); 90 91 $out = array(); 92 foreach ($this as $val) { 93 switch ($name) { 94 case 'addresses': 95 $out[] = strval($val); 96 break; 97 98 case 'bare_addresses': 99 $out[] = $val->bare_address; 100 break; 101 102 case 'bare_addresses_idn': 103 $out[] = $val->bare_address_idn; 104 break; 105 106 case 'base_addresses': 107 case 'raw_addresses': 108 $out[] = clone $val; 109 break; 110 } 111 } 112 113 $this->_filter = $old; 114 return $out; 115 } 116 } 117 118 /** 119 * Add objects to the container. 120 * 121 * @param mixed $obs Address data to store in this object. 122 */ 123 public function add($obs) 124 { 125 foreach ($this->_normalize($obs) as $val) { 126 $this->_data[] = $val; 127 } 128 } 129 130 /** 131 * Remove addresses from the container. This method ignores Group objects. 132 * 133 * @param mixed $obs Addresses to remove. 134 */ 135 public function remove($obs) 136 { 137 $old = $this->_filter; 138 $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS); 139 140 foreach ($this->_normalize($obs) as $val) { 141 $remove = array(); 142 143 foreach ($this as $key => $val2) { 144 if ($val2->match($val)) { 145 $remove[] = $key; 146 } 147 } 148 149 foreach (array_reverse($remove) as $key) { 150 unset($this[$key]); 151 } 152 } 153 154 $this->_filter = $old; 155 } 156 157 /** 158 * Removes duplicate addresses from list. This method ignores Group 159 * objects. 160 */ 161 public function unique() 162 { 163 $exist = $remove = array(); 164 165 $old = $this->_filter; 166 $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS); 167 168 // For duplicates, we use the first address that contains personal 169 // information. 170 foreach ($this as $key => $val) { 171 $bare = $val->bare_address; 172 if (isset($exist[$bare])) { 173 if (($exist[$bare] == -1) || is_null($val->personal)) { 174 $remove[] = $key; 175 } else { 176 $remove[] = $exist[$bare]; 177 $exist[$bare] = -1; 178 } 179 } else { 180 $exist[$bare] = is_null($val->personal) 181 ? $key 182 : -1; 183 } 184 } 185 186 foreach (array_reverse($remove) as $key) { 187 unset($this[$key]); 188 } 189 190 $this->_filter = $old; 191 } 192 193 /** 194 * Group count. 195 * 196 * @return integer The number of groups in the list. 197 */ 198 public function groupCount() 199 { 200 $ret = 0; 201 202 foreach ($this->_data as $val) { 203 if ($val instanceof Horde_Mail_Rfc822_Group) { 204 ++$ret; 205 } 206 } 207 208 return $ret; 209 } 210 211 /** 212 * Set the Iterator filter. 213 * 214 * @param integer $mask Filter masks. 215 * @param mixed $filter An e-mail, or as list of e-mails, to filter by. 216 */ 217 public function setIteratorFilter($mask = 0, $filter = null) 218 { 219 $this->_filter = array(); 220 221 if ($mask) { 222 $this->_filter['mask'] = $mask; 223 } 224 225 if (!is_null($filter)) { 226 $rfc822 = new Horde_Mail_Rfc822(); 227 $this->_filter['filter'] = $rfc822->parseAddressList($filter); 228 } 229 } 230 231 /** 232 */ 233 protected function _writeAddress($opts) 234 { 235 $out = array(); 236 237 foreach ($this->_data as $val) { 238 $out[] = $val->writeAddress($opts); 239 } 240 241 return implode(', ', $out); 242 } 243 244 /** 245 */ 246 public function match($ob) 247 { 248 if (!($ob instanceof Horde_Mail_Rfc822_List)) { 249 $ob = new Horde_Mail_Rfc822_List($ob); 250 } 251 252 $a = $this->bare_addresses; 253 sort($a); 254 $b = $ob->bare_addresses; 255 sort($b); 256 257 return ($a == $b); 258 } 259 260 /** 261 * Does this list contain the given e-mail address? 262 * 263 * @param mixed $address An e-mail address. 264 * 265 * @return boolean True if the e-mail address is contained in the list. 266 */ 267 public function contains($address) 268 { 269 $ob = new Horde_Mail_Rfc822_Address($address); 270 271 foreach ($this->raw_addresses as $val) { 272 if ($val->match($ob)) { 273 return true; 274 } 275 } 276 277 return false; 278 } 279 280 /** 281 * Convenience method to return the first element in a list. 282 * 283 * Useful since it allows chaining; older PHP versions did not allow array 284 * access dereferencing from the results of a function call. 285 * 286 * @since 2.5.0 287 * 288 * @return Horde_Mail_Rfc822_Object Rfc822 object, or null if no object. 289 */ 290 public function first() 291 { 292 return $this[0]; 293 } 294 295 /** 296 * Normalize objects to add to list. 297 * 298 * @param mixed $obs Address data to store in this object. 299 * 300 * @return array Entries to add. 301 */ 302 protected function _normalize($obs) 303 { 304 $add = array(); 305 306 if (!($obs instanceof Horde_Mail_Rfc822_List) && 307 !is_array($obs)) { 308 $obs = array($obs); 309 } 310 311 foreach ($obs as $val) { 312 if (is_string($val)) { 313 $rfc822 = new Horde_Mail_Rfc822(); 314 $val = $rfc822->parseAddressList($val); 315 } 316 317 if ($val instanceof Horde_Mail_Rfc822_List) { 318 $val->setIteratorFilter(self::BASE_ELEMENTS); 319 foreach ($val as $val2) { 320 $add[] = $val2; 321 } 322 } elseif ($val instanceof Horde_Mail_Rfc822_Object) { 323 $add[] = $val; 324 } 325 } 326 327 return $add; 328 } 329 330 /* ArrayAccess methods. */ 331 332 /** 333 */ 334 public function offsetExists($offset) 335 { 336 return !is_null($this[$offset]); 337 } 338 339 /** 340 */ 341 public function offsetGet($offset) 342 { 343 try { 344 $this->seek($offset); 345 return $this->current(); 346 } catch (OutOfBoundsException $e) { 347 return null; 348 } 349 } 350 351 /** 352 */ 353 public function offsetSet($offset, $value) 354 { 355 if ($ob = $this[$offset]) { 356 if (is_null($this->_ptr['subidx'])) { 357 $tmp = $this->_normalize($value); 358 if (isset($tmp[0])) { 359 $this->_data[$this->_ptr['idx']] = $tmp[0]; 360 } 361 } else { 362 $ob[$offset] = $value; 363 } 364 $this->_ptr = null; 365 } 366 } 367 368 /** 369 */ 370 public function offsetUnset($offset) 371 { 372 if ($ob = $this[$offset]) { 373 if (is_null($this->_ptr['subidx'])) { 374 unset($this->_data[$this->_ptr['idx']]); 375 $this->_data = array_values($this->_data); 376 } else { 377 unset($ob->addresses[$this->_ptr['subidx']]); 378 } 379 $this->_ptr = null; 380 } 381 } 382 383 /* Countable methods. */ 384 385 /** 386 * Address count. 387 * 388 * @return integer The number of addresses. 389 */ 390 public function count() 391 { 392 return count($this->addresses); 393 } 394 395 /* Iterator methods. */ 396 397 public function current() 398 { 399 if (!$this->valid()) { 400 return null; 401 } 402 403 $ob = $this->_data[$this->_ptr['idx']]; 404 405 return is_null($this->_ptr['subidx']) 406 ? $ob 407 : $ob->addresses[$this->_ptr['subidx']]; 408 } 409 410 public function key() 411 { 412 return $this->_ptr['key']; 413 } 414 415 public function next() 416 { 417 if (is_null($this->_ptr['subidx'])) { 418 $curr = $this->current(); 419 if (($curr instanceof Horde_Mail_Rfc822_Group) && count($curr)) { 420 $this->_ptr['subidx'] = 0; 421 } else { 422 ++$this->_ptr['idx']; 423 } 424 $curr = $this->current(); 425 } elseif (!($curr = $this->_data[$this->_ptr['idx']]->addresses[++$this->_ptr['subidx']])) { 426 $this->_ptr['subidx'] = null; 427 ++$this->_ptr['idx']; 428 $curr = $this->current(); 429 } 430 431 if (!is_null($curr)) { 432 if (!empty($this->_filter) && $this->_iteratorFilter($curr)) { 433 $this->next(); 434 } else { 435 ++$this->_ptr['key']; 436 } 437 } 438 } 439 440 public function rewind() 441 { 442 $this->_ptr = array( 443 'idx' => 0, 444 'key' => 0, 445 'subidx' => null 446 ); 447 448 if ($this->valid() && 449 !empty($this->_filter) && 450 $this->_iteratorFilter($this->current())) { 451 $this->next(); 452 $this->_ptr['key'] = 0; 453 } 454 } 455 456 public function valid() 457 { 458 return (!empty($this->_ptr) && isset($this->_data[$this->_ptr['idx']])); 459 } 460 461 public function seek($position) 462 { 463 if (!$this->valid() || 464 ($position < $this->_ptr['key'])) { 465 $this->rewind(); 466 } 467 468 for ($i = $this->_ptr['key']; ; ++$i) { 469 if ($i == $position) { 470 return; 471 } 472 473 $this->next(); 474 if (!$this->valid()) { 475 throw new OutOfBoundsException('Position not found.'); 476 } 477 } 478 } 479 480 protected function _iteratorFilter($ob) 481 { 482 if (!empty($this->_filter['mask'])) { 483 if (($this->_filter['mask'] & self::HIDE_GROUPS) && 484 ($ob instanceof Horde_Mail_Rfc822_Group)) { 485 return true; 486 } 487 488 if (($this->_filter['mask'] & self::BASE_ELEMENTS) && 489 !is_null($this->_ptr['subidx'])) { 490 return true; 491 } 492 } 493 494 if (!empty($this->_filter['filter']) && 495 ($ob instanceof Horde_Mail_Rfc822_Address)) { 496 foreach ($this->_filter['filter'] as $val) { 497 if ($ob->match($val)) { 498 return true; 499 } 500 } 501 } 502 503 return false; 504 } 505 506 /* Serializable methods. */ 507 508 public function serialize() 509 { 510 return serialize($this->_data); 511 } 512 513 public function unserialize($data) 514 { 515 $this->_data = unserialize($data); 516 } 517 518 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body