Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
<?php
/**
 * Copyright 2012-2017 Horde LLC (http://www.horde.org/)
 *
 * See the enclosed file LICENSE for license information (BSD). If you
 * did not receive this file, see http://www.horde.org/licenses/bsd.
 *
 * @category  Horde
 * @copyright 2012-2017 Horde LLC
 * @license   http://www.horde.org/licenses/bsd New BSD License
 * @package   Mail
 */

/**
 * Container object for a collection of RFC 822 elements.
 *
 * @author    Michael Slusarz <slusarz@horde.org>
 * @category  Horde
 * @copyright 2012-2017 Horde LLC
 * @license   http://www.horde.org/licenses/bsd New BSD License
 * @package   Mail
 *
 * @property-read array $addresses  The list of all addresses (address
 *                                  w/personal parts).
 * @property-read array $bare_addresses  The list of all addresses (mail@host).
 * @property-read array $bare_addresses_idn  The list of all addresses
 *                                           (mail@host; IDN encoded).
 *                                           (@since 2.1.0)
 * @property-read array $base_addresses  The list of ONLY base addresses
 *                                       (Address objects).
 * @property-read array $raw_addresses  The list of all addresses (Address
 *                                      objects).
 */
class Horde_Mail_Rfc822_List
    extends Horde_Mail_Rfc822_Object
    implements ArrayAccess, Countable, SeekableIterator, Serializable
{
    /** Filter masks. */
    const HIDE_GROUPS = 1;
    const BASE_ELEMENTS = 2;

    /**
     * List data.
     *
     * @var array
     */
    protected $_data = array();

    /**
     * Current Iterator filter.
     *
     * @var array
     */
    protected $_filter = array();

    /**
     * Current Iterator pointer.
     *
     * @var array
     */
    protected $_ptr;

    /**
     * Constructor.
     *
     * @param mixed $obs  Address data to store in this object.
     */
    public function __construct($obs = null)
    {
        if (!is_null($obs)) {
            $this->add($obs);
        }
    }

    /**
     */
    public function __get($name)
    {
        switch ($name) {
        case 'addresses':
        case 'bare_addresses':
        case 'bare_addresses_idn':
        case 'base_addresses':
        case 'raw_addresses':
            $old = $this->_filter;
            $mask = ($name == 'base_addresses')
                ? self::BASE_ELEMENTS
                : self::HIDE_GROUPS;
            $this->setIteratorFilter($mask, empty($old['filter']) ? null : $old['filter']);

            $out = array();
            foreach ($this as $val) {
                switch ($name) {
                case 'addresses':
                    $out[] = strval($val);
                    break;

                case 'bare_addresses':
                    $out[] = $val->bare_address;
                    break;

                case 'bare_addresses_idn':
                    $out[] = $val->bare_address_idn;
                    break;

                case 'base_addresses':
                case 'raw_addresses':
                    $out[] = clone $val;
                    break;
                }
            }

            $this->_filter = $old;
            return $out;
        }
    }

    /**
     * Add objects to the container.
     *
     * @param mixed $obs  Address data to store in this object.
     */
    public function add($obs)
    {
        foreach ($this->_normalize($obs) as $val) {
            $this->_data[] = $val;
        }
    }

    /**
     * Remove addresses from the container. This method ignores Group objects.
     *
     * @param mixed $obs  Addresses to remove.
     */
    public function remove($obs)
    {
        $old = $this->_filter;
        $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);

        foreach ($this->_normalize($obs) as $val) {
            $remove = array();

            foreach ($this as $key => $val2) {
                if ($val2->match($val)) {
                    $remove[] = $key;
                }
            }

            foreach (array_reverse($remove) as $key) {
                unset($this[$key]);
            }
        }

        $this->_filter = $old;
    }

    /**
     * Removes duplicate addresses from list. This method ignores Group
     * objects.
     */
    public function unique()
    {
        $exist = $remove = array();

        $old = $this->_filter;
        $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);

        // For duplicates, we use the first address that contains personal
        // information.
        foreach ($this as $key => $val) {
            $bare = $val->bare_address;
            if (isset($exist[$bare])) {
                if (($exist[$bare] == -1) || is_null($val->personal)) {
                    $remove[] = $key;
                } else {
                    $remove[] = $exist[$bare];
                    $exist[$bare] = -1;
                }
            } else {
                $exist[$bare] = is_null($val->personal)
                    ? $key
                    : -1;
            }
        }

        foreach (array_reverse($remove) as $key) {
            unset($this[$key]);
        }

        $this->_filter = $old;
    }

    /**
     * Group count.
     *
     * @return integer  The number of groups in the list.
     */
    public function groupCount()
    {
        $ret = 0;

        foreach ($this->_data as $val) {
            if ($val instanceof Horde_Mail_Rfc822_Group) {
                ++$ret;
            }
        }

        return $ret;
    }

    /**
     * Set the Iterator filter.
     *
     * @param integer $mask  Filter masks.
     * @param mixed $filter  An e-mail, or as list of e-mails, to filter by.
     */
    public function setIteratorFilter($mask = 0, $filter = null)
    {
        $this->_filter = array();

        if ($mask) {
            $this->_filter['mask'] = $mask;
        }

        if (!is_null($filter)) {
            $rfc822 = new Horde_Mail_Rfc822();
            $this->_filter['filter'] = $rfc822->parseAddressList($filter);
        }
    }

    /**
     */
    protected function _writeAddress($opts)
    {
        $out = array();

        foreach ($this->_data as $val) {
            $out[] = $val->writeAddress($opts);
        }

        return implode(', ', $out);
    }

    /**
     */
    public function match($ob)
    {
        if (!($ob instanceof Horde_Mail_Rfc822_List)) {
            $ob = new Horde_Mail_Rfc822_List($ob);
        }

        $a = $this->bare_addresses;
        sort($a);
        $b = $ob->bare_addresses;
        sort($b);

        return ($a == $b);
    }

    /**
     * Does this list contain the given e-mail address?
     *
     * @param mixed $address  An e-mail address.
     *
     * @return boolean  True if the e-mail address is contained in the list.
     */
    public function contains($address)
    {
        $ob = new Horde_Mail_Rfc822_Address($address);

        foreach ($this->raw_addresses as $val) {
            if ($val->match($ob)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Convenience method to return the first element in a list.
     *
     * Useful since it allows chaining; older PHP versions did not allow array
     * access dereferencing from the results of a function call.
     *
     * @since 2.5.0
     *
     * @return Horde_Mail_Rfc822_Object  Rfc822 object, or null if no object.
     */
    public function first()
    {
        return $this[0];
    }

    /**
     * Normalize objects to add to list.
     *
     * @param mixed $obs  Address data to store in this object.
     *
     * @return array  Entries to add.
     */
    protected function _normalize($obs)
    {
        $add = array();

        if (!($obs instanceof Horde_Mail_Rfc822_List) &&
            !is_array($obs)) {
            $obs = array($obs);
        }

        foreach ($obs as $val) {
            if (is_string($val)) {
                $rfc822 = new Horde_Mail_Rfc822();
                $val = $rfc822->parseAddressList($val);
            }

            if ($val instanceof Horde_Mail_Rfc822_List) {
                $val->setIteratorFilter(self::BASE_ELEMENTS);
                foreach ($val as $val2) {
                    $add[] = $val2;
                }
            } elseif ($val instanceof Horde_Mail_Rfc822_Object) {
                $add[] = $val;
            }
        }

        return $add;
    }

    /* ArrayAccess methods. */

    /**
     */
> #[\ReturnTypeWillChange]
public function offsetExists($offset) { return !is_null($this[$offset]); } /** */
> #[\ReturnTypeWillChange]
public function offsetGet($offset) { try { $this->seek($offset); return $this->current(); } catch (OutOfBoundsException $e) { return null; } } /** */
> #[\ReturnTypeWillChange]
public function offsetSet($offset, $value) { if ($ob = $this[$offset]) { if (is_null($this->_ptr['subidx'])) { $tmp = $this->_normalize($value); if (isset($tmp[0])) { $this->_data[$this->_ptr['idx']] = $tmp[0]; } } else { $ob[$offset] = $value; } $this->_ptr = null; } } /** */
> #[\ReturnTypeWillChange]
public function offsetUnset($offset) { if ($ob = $this[$offset]) { if (is_null($this->_ptr['subidx'])) { unset($this->_data[$this->_ptr['idx']]); $this->_data = array_values($this->_data); } else { unset($ob->addresses[$this->_ptr['subidx']]); } $this->_ptr = null; } } /* Countable methods. */ /** * Address count. * * @return integer The number of addresses. */
> #[\ReturnTypeWillChange]
public function count() { return count($this->addresses); } /* Iterator methods. */
> #[\ReturnTypeWillChange]
public function current() { if (!$this->valid()) { return null; } $ob = $this->_data[$this->_ptr['idx']]; return is_null($this->_ptr['subidx']) ? $ob : $ob->addresses[$this->_ptr['subidx']]; }
> #[\ReturnTypeWillChange]
public function key() { return $this->_ptr['key']; }
> #[\ReturnTypeWillChange]
public function next() { if (is_null($this->_ptr['subidx'])) { $curr = $this->current(); if (($curr instanceof Horde_Mail_Rfc822_Group) && count($curr)) { $this->_ptr['subidx'] = 0; } else { ++$this->_ptr['idx']; } $curr = $this->current(); } elseif (!($curr = $this->_data[$this->_ptr['idx']]->addresses[++$this->_ptr['subidx']])) { $this->_ptr['subidx'] = null; ++$this->_ptr['idx']; $curr = $this->current(); } if (!is_null($curr)) { if (!empty($this->_filter) && $this->_iteratorFilter($curr)) { $this->next(); } else { ++$this->_ptr['key']; } } }
> #[\ReturnTypeWillChange]
public function rewind() { $this->_ptr = array( 'idx' => 0, 'key' => 0, 'subidx' => null ); if ($this->valid() && !empty($this->_filter) && $this->_iteratorFilter($this->current())) { $this->next(); $this->_ptr['key'] = 0; } }
> #[\ReturnTypeWillChange]
public function valid() { return (!empty($this->_ptr) && isset($this->_data[$this->_ptr['idx']])); }
> #[\ReturnTypeWillChange]
public function seek($position) { if (!$this->valid() || ($position < $this->_ptr['key'])) { $this->rewind(); } for ($i = $this->_ptr['key']; ; ++$i) { if ($i == $position) { return; } $this->next(); if (!$this->valid()) { throw new OutOfBoundsException('Position not found.'); } } } protected function _iteratorFilter($ob) { if (!empty($this->_filter['mask'])) { if (($this->_filter['mask'] & self::HIDE_GROUPS) && ($ob instanceof Horde_Mail_Rfc822_Group)) { return true; } if (($this->_filter['mask'] & self::BASE_ELEMENTS) && !is_null($this->_ptr['subidx'])) { return true; } } if (!empty($this->_filter['filter']) && ($ob instanceof Horde_Mail_Rfc822_Address)) { foreach ($this->_filter['filter'] as $val) { if ($ob->match($val)) { return true; } } } return false; } /* Serializable methods. */ public function serialize() { return serialize($this->_data); } public function unserialize($data) { $this->_data = unserialize($data); }
> > public function __serialize() { } > return array( > 'data' => $this->_data > ); > } > > public function __unserialize(array $data) { > $this->_data = $data['data']; > }