/lib/horde/framework/Horde/Imap/Client/Ids/Map.php

https://github.com/markn86/moodle · PHP · 242 lines · 128 code · 32 blank · 82 comment · 20 complexity · 3c03e4019ae13482ce8b52d76effa99d MD5 · raw file

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