Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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  }