PageRenderTime 119ms CodeModel.GetById 17ms RepoModel.GetById 2ms app.codeStats 0ms

/lib/horde/framework/Horde/Imap/Client/Cache/Backend/Hashtable.php

https://gitlab.com/unofficial-mirrors/moodle
PHP | 421 lines | 243 code | 55 blank | 123 comment | 22 complexity | 5fe1162c1a33e65497d2b5c61ff2f7c5 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 2013-2017 Horde LLC (http://www.horde.org/)
  4. *
  5. * See the enclosed file COPYING 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 2013-2017 Horde LLC
  10. * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
  11. * @package Imap_Client
  12. */
  13. /**
  14. * A Horde_HashTable implementation for caching IMAP/POP data.
  15. * Requires the Horde_HashTable and Horde_Pack packages.
  16. *
  17. * @author Michael Slusarz <slusarz@horde.org>
  18. * @category Horde
  19. * @copyright 2013-2017 Horde LLC
  20. * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
  21. * @package Imap_Client
  22. * @since 2.17.0
  23. */
  24. class Horde_Imap_Client_Cache_Backend_Hashtable
  25. extends Horde_Imap_Client_Cache_Backend
  26. {
  27. /** Separator for CID between mailbox and UID. */
  28. const CID_SEPARATOR = '|';
  29. /**
  30. * The working data for the current pageload. All changes take place to
  31. * this data.
  32. *
  33. * @var array
  34. */
  35. protected $_data = array();
  36. /**
  37. * HashTable object.
  38. *
  39. * @var Horde_HashTable
  40. */
  41. protected $_hash;
  42. /**
  43. * Mailbox level data.
  44. *
  45. * @var array
  46. */
  47. protected $_mbox = array();
  48. /**
  49. * Horde_Pack singleton object.
  50. *
  51. * @var Horde_Pack
  52. */
  53. protected $_pack;
  54. /**
  55. * List of mailbox/UIDs to update.
  56. * Keys are mailboxes. Values are arrays with three possible keys:
  57. * <pre>
  58. * - d: UIDs to delete
  59. * - m: Was metadata updated?
  60. * - u: UIDs to update
  61. * </pre>
  62. *
  63. * @var array
  64. */
  65. protected $_update = array();
  66. /**
  67. * Constructor.
  68. *
  69. * @param array $params Configuration parameters:
  70. * <pre>
  71. * - REQUIRED parameters:
  72. * - hashtable: (Horde_HashTable) A HashTable object.
  73. *
  74. * - Optional Parameters:
  75. * - lifetime: (integer) The lifetime of the cache data (in seconds).
  76. * DEFAULT: 604800 seconds (1 week) [@since 2.19.0]
  77. * </pre>
  78. */
  79. public function __construct(array $params = array())
  80. {
  81. if (!isset($params['hashtable'])) {
  82. throw new InvalidArgumentException('Missing hashtable parameter.');
  83. }
  84. parent::__construct(array_merge(array(
  85. 'lifetime' => 604800
  86. ), $params));
  87. }
  88. /**
  89. */
  90. protected function _initOb()
  91. {
  92. $this->_hash = $this->_params['hashtable'];
  93. $this->_pack = new Horde_Pack();
  94. register_shutdown_function(array($this, 'save'));
  95. }
  96. /**
  97. */
  98. public function get($mailbox, $uids, $fields, $uidvalid)
  99. {
  100. $ret = array();
  101. if (empty($uids)) {
  102. return $ret;
  103. }
  104. $this->_loadUids($mailbox, $uids, $uidvalid);
  105. if (empty($this->_data[$mailbox])) {
  106. return $ret;
  107. }
  108. if (!empty($fields)) {
  109. $fields = array_flip($fields);
  110. }
  111. $ptr = &$this->_data[$mailbox];
  112. $to_delete = array();
  113. foreach ($uids as $val) {
  114. if (isset($ptr[$val])) {
  115. if (is_string($ptr[$val])) {
  116. try {
  117. $ptr[$val] = $this->_pack->unpack($ptr[$val]);
  118. } catch (Horde_Pack_Exception $e) {
  119. $to_delete[] = $val;
  120. continue;
  121. }
  122. }
  123. $ret[$val] = (empty($fields) || empty($ptr[$val]))
  124. ? $ptr[$val]
  125. : array_intersect_key($ptr[$val], $fields);
  126. } else {
  127. $to_delete[] = $val;
  128. }
  129. }
  130. $this->deleteMsgs($mailbox, $to_delete);
  131. return $ret;
  132. }
  133. /**
  134. */
  135. public function getCachedUids($mailbox, $uidvalid)
  136. {
  137. $this->_loadMailbox($mailbox, $uidvalid);
  138. return $this->_mbox[$mailbox]['u']->ids;
  139. }
  140. /**
  141. */
  142. public function set($mailbox, $data, $uidvalid)
  143. {
  144. $this->_loadUids($mailbox, array_keys($data), $uidvalid);
  145. $d = &$this->_data[$mailbox];
  146. $to_add = array();
  147. foreach ($data as $k => $v) {
  148. if (isset($d[$k]) && is_string($d[$k])) {
  149. try {
  150. $d[$k] = $this->_pack->unpack($d[$k]);
  151. } catch (Horde_Pack_Exception $e) {
  152. continue;
  153. }
  154. }
  155. $d[$k] = (isset($d[$k]) && is_array($d[$k]))
  156. ? array_merge($d[$k], $v)
  157. : $v;
  158. $this->_update[$mailbox]['u'][$k] = true;
  159. unset($this->_update[$mailbox]['d'][$k]);
  160. $to_add[] = $k;
  161. }
  162. if (!empty($to_add)) {
  163. $this->_mbox[$mailbox]['u']->add($to_add);
  164. $this->_update[$mailbox]['m'] = true;
  165. }
  166. }
  167. /**
  168. */
  169. public function getMetaData($mailbox, $uidvalid, $entries)
  170. {
  171. $this->_loadMailbox($mailbox, $uidvalid);
  172. return empty($entries)
  173. ? $this->_mbox[$mailbox]['d']
  174. : array_intersect_key($this->_mbox[$mailbox]['d'], array_flip($entries));
  175. }
  176. /**
  177. */
  178. public function setMetaData($mailbox, $data)
  179. {
  180. $this->_loadMailbox($mailbox, isset($data['uidvalid']) ? $data['uidvalid'] : null);
  181. $this->_mbox[$mailbox]['d'] = array_merge(
  182. $this->_mbox[$mailbox]['d'],
  183. $data
  184. );
  185. $this->_update[$mailbox]['m'] = true;
  186. }
  187. /**
  188. */
  189. public function deleteMsgs($mailbox, $uids)
  190. {
  191. if (empty($uids)) {
  192. return;
  193. }
  194. $this->_loadMailbox($mailbox);
  195. foreach ($uids as $val) {
  196. unset(
  197. $this->_data[$mailbox][$val],
  198. $this->_update[$mailbox]['u'][$val]
  199. );
  200. $this->_update[$mailbox]['d'][$val] = true;
  201. }
  202. $this->_mbox[$mailbox]['u']->remove($uids);
  203. $this->_update[$mailbox]['m'] = true;
  204. }
  205. /**
  206. */
  207. public function deleteMailbox($mailbox)
  208. {
  209. /* Do this action immediately, instead of at shutdown. Makes coding
  210. * simpler. */
  211. $this->_loadMailbox($mailbox);
  212. $this->_hash->delete(array_merge(
  213. array($this->_getCid($mailbox)),
  214. array_values($this->_getMsgCids($mailbox, $this->_mbox[$mailbox]['u']))
  215. ));
  216. unset(
  217. $this->_data[$mailbox],
  218. $this->_mbox[$mailbox],
  219. $this->_update[$mailbox]
  220. );
  221. }
  222. /**
  223. */
  224. public function clear($lifetime)
  225. {
  226. /* Only can clear mailboxes we know about. */
  227. foreach (array_keys($this->_mbox) as $val) {
  228. $this->deleteMailbox($val);
  229. }
  230. $this->_data = $this->_mbox = $this->_update = array();
  231. }
  232. /**
  233. * Updates the cache.
  234. */
  235. public function save()
  236. {
  237. foreach ($this->_update as $mbox => $val) {
  238. try {
  239. if (!empty($val['u'])) {
  240. $ptr = &$this->_data[$mbox];
  241. foreach ($this->_getMsgCids($mbox, array_keys($val['u'])) as $k2 => $v2) {
  242. try {
  243. $this->_hash->set(
  244. $v2,
  245. $this->_pack->pack($ptr[$k2]),
  246. array('expire' => $this->_params['lifetime'])
  247. );
  248. } catch (Horde_Pack_Exception $e) {
  249. $this->deleteMsgs($mbox, array($v2));
  250. $val['d'][] = $v2;
  251. }
  252. }
  253. }
  254. if (!empty($val['d'])) {
  255. $this->_hash->delete(array_values(
  256. $this->_getMsgCids($mbox, $val['d'])
  257. ));
  258. }
  259. if (!empty($val['m'])) {
  260. try {
  261. $this->_hash->set(
  262. $this->_getCid($mbox),
  263. $this->_pack->pack($this->_mbox[$mbox]),
  264. array('expire' => $this->_params['lifetime'])
  265. );
  266. } catch (Horde_Pack_Exception $e) {}
  267. }
  268. } catch (Horde_Exception $e) {
  269. }
  270. }
  271. $this->_update = array();
  272. }
  273. /**
  274. * Loads basic mailbox information.
  275. *
  276. * @param string $mailbox The mailbox to load.
  277. * @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
  278. */
  279. protected function _loadMailbox($mailbox, $uidvalid = null)
  280. {
  281. if (!isset($this->_mbox[$mailbox]) &&
  282. ($ob = $this->_hash->get($this->_getCid($mailbox)))) {
  283. try {
  284. $this->_mbox[$mailbox] = $this->_pack->unpack($ob);
  285. } catch (Horde_Pack_Exception $e) {}
  286. }
  287. if (isset($this->_mbox[$mailbox])) {
  288. if (is_null($uidvalid) ||
  289. ($uidvalid == $this->_mbox[$mailbox]['d']['uidvalid'])) {
  290. return;
  291. }
  292. $this->deleteMailbox($mailbox);
  293. }
  294. $this->_mbox[$mailbox] = array(
  295. // Metadata storage
  296. // By default includes UIDVALIDITY of mailbox.
  297. 'd' => array('uidvalid' => $uidvalid),
  298. // List of UIDs
  299. 'u' => new Horde_Imap_Client_Ids()
  300. );
  301. }
  302. /**
  303. * Load UIDs by regenerating from the cache.
  304. *
  305. * @param string $mailbox The mailbox to load.
  306. * @param array $uids The UIDs to load.
  307. * @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
  308. */
  309. protected function _loadUids($mailbox, $uids, $uidvalid = null)
  310. {
  311. if (!isset($this->_data[$mailbox])) {
  312. $this->_data[$mailbox] = array();
  313. }
  314. $this->_loadMailbox($mailbox, $uidvalid);
  315. if (empty($uids)) {
  316. return;
  317. }
  318. $ptr = &$this->_data[$mailbox];
  319. $load = array_flip(
  320. array_diff_key(
  321. $this->_getMsgCids(
  322. $mailbox,
  323. array_unique(array_intersect($this->_mbox[$mailbox]['u']->ids, $uids))
  324. ),
  325. $this->_data[$mailbox]
  326. )
  327. );
  328. foreach (array_filter($this->_hash->get(array_keys($load))) as $key => $val) {
  329. $ptr[$load[$key]] = $val;
  330. }
  331. }
  332. /**
  333. * Create the unique ID used to store the mailbox data in the cache.
  334. *
  335. * @param string $mailbox The mailbox to cache.
  336. *
  337. * @return string The cache ID.
  338. */
  339. protected function _getCid($mailbox)
  340. {
  341. return implode(self::CID_SEPARATOR, array(
  342. 'horde_imap_client',
  343. $this->_params['username'],
  344. $mailbox,
  345. $this->_params['hostspec'],
  346. $this->_params['port']
  347. ));
  348. }
  349. /**
  350. * Return a list of cache IDs for mailbox/UID pairs.
  351. *
  352. * @param string $mailbox The mailbox to cache.
  353. * @param array $ids The UID list.
  354. *
  355. * @return array List of UIDs => cache IDs.
  356. */
  357. protected function _getMsgCids($mailbox, $ids)
  358. {
  359. $cid = $this->_getCid($mailbox);
  360. $out = array();
  361. foreach ($ids as $val) {
  362. $out[$val] = $cid . self::CID_SEPARATOR . $val;
  363. }
  364. return $out;
  365. }
  366. }