Merge pull request #5550 from civicrm/4.5
[civicrm-core.git] / CRM / Core / BAO / Cache.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035 27
6a488035
TO
28/**
29 * BAO object for civicrm_cache table. This is a database cache and is persisted across sessions. Typically we use
30 * this to store meta data (like profile fields, custom fields etc).
31 *
32 * The group_name column is used for grouping together all cache elements that logically belong to the same set.
33 * Thus all session cache entries are grouped under 'CiviCRM Session'. This allows us to delete all entries of
34 * a specific group if needed.
35 *
36 * The path column allows us to differentiate between items in that group. Thus for the session cache, the path is
37 * the unique form name for each form (per user)
38 */
39class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
40
e79e9c87
TO
41 /**
42 * @var array ($cacheKey => $cacheValue)
43 */
44 static $_cache = NULL;
45
6a488035 46 /**
fe482240 47 * Retrieve an item from the DB cache.
6a488035 48 *
6a0b768e
TO
49 * @param string $group
50 * (required) The group name of the item.
51 * @param string $path
52 * (required) The path under which this item is stored.
53 * @param int $componentID
54 * The optional component ID (so componenets can share the same name space).
6a488035 55 *
a6c01b45
CW
56 * @return object
57 * The data if present in cache, else null
6a488035 58 */
00be9182 59 public static function &getItem($group, $path, $componentID = NULL) {
e79e9c87
TO
60 if (self::$_cache === NULL) {
61 self::$_cache = array();
def0681b 62 }
6a488035 63
def0681b 64 $argString = "CRM_CT_{$group}_{$path}_{$componentID}";
e79e9c87 65 if (!array_key_exists($argString, self::$_cache)) {
def0681b 66 $cache = CRM_Utils_Cache::singleton();
e79e9c87
TO
67 self::$_cache[$argString] = $cache->get($argString);
68 if (!self::$_cache[$argString]) {
def0681b 69 $dao = new CRM_Core_DAO_Cache();
6a488035 70
f9f40af3
TO
71 $dao->group_name = $group;
72 $dao->path = $path;
def0681b
KJ
73 $dao->component_id = $componentID;
74
75 $data = NULL;
76 if ($dao->find(TRUE)) {
77 $data = unserialize($dao->data);
78 }
79 $dao->free();
e79e9c87
TO
80 self::$_cache[$argString] = $data;
81 $cache->set($argString, self::$_cache[$argString]);
def0681b 82 }
6a488035 83 }
e79e9c87 84 return self::$_cache[$argString];
6a488035
TO
85 }
86
87 /**
fe482240 88 * Retrieve all items in a group.
6a488035 89 *
6a0b768e
TO
90 * @param string $group
91 * (required) The group name of the item.
92 * @param int $componentID
93 * The optional component ID (so componenets can share the same name space).
6a488035 94 *
a6c01b45
CW
95 * @return object
96 * The data if present in cache, else null
6a488035 97 */
00be9182 98 public static function &getItems($group, $componentID = NULL) {
e79e9c87
TO
99 if (self::$_cache === NULL) {
100 self::$_cache = array();
def0681b 101 }
6a488035 102
def0681b 103 $argString = "CRM_CT_CI_{$group}_{$componentID}";
e79e9c87 104 if (!array_key_exists($argString, self::$_cache)) {
def0681b 105 $cache = CRM_Utils_Cache::singleton();
e79e9c87
TO
106 self::$_cache[$argString] = $cache->get($argString);
107 if (!self::$_cache[$argString]) {
def0681b
KJ
108 $dao = new CRM_Core_DAO_Cache();
109
f9f40af3 110 $dao->group_name = $group;
def0681b
KJ
111 $dao->component_id = $componentID;
112 $dao->find();
6a488035 113
1a4e6781 114 $result = array();
def0681b
KJ
115 while ($dao->fetch()) {
116 $result[$dao->path] = unserialize($dao->data);
117 }
118 $dao->free();
119
e79e9c87
TO
120 self::$_cache[$argString] = $result;
121 $cache->set($argString, self::$_cache[$argString]);
def0681b 122 }
6a488035 123 }
def0681b 124
e79e9c87 125 return self::$_cache[$argString];
6a488035
TO
126 }
127
128 /**
fe482240 129 * Store an item in the DB cache.
6a488035 130 *
6a0b768e
TO
131 * @param object $data
132 * (required) A reference to the data that will be serialized and stored.
133 * @param string $group
134 * (required) The group name of the item.
135 * @param string $path
136 * (required) The path under which this item is stored.
137 * @param int $componentID
138 * The optional component ID (so componenets can share the same name space).
6a488035 139 */
00be9182 140 public static function setItem(&$data, $group, $path, $componentID = NULL) {
e79e9c87
TO
141 if (self::$_cache === NULL) {
142 self::$_cache = array();
def0681b
KJ
143 }
144
6a488035
TO
145 $dao = new CRM_Core_DAO_Cache();
146
f9f40af3
TO
147 $dao->group_name = $group;
148 $dao->path = $path;
6a488035
TO
149 $dao->component_id = $componentID;
150
151 // get a lock so that multiple ajax requests on the same page
152 // dont trample on each other
153 // CRM-11234
154 $lockName = "civicrm.cache.{$group}_{$path}._{$componentID}";
155 $lock = new CRM_Core_Lock($lockName);
156 if (!$lock->isAcquired()) {
157 CRM_Core_Error::fatal();
158 }
159
160 $dao->find(TRUE);
161 $dao->data = serialize($data);
162 $dao->created_date = date('YmdHis');
163 $dao->save();
164
165 $lock->release();
166
167 $dao->free();
def0681b 168
80259ba2
TO
169 // cache coherency - refresh or remove dependent caches
170
def0681b
KJ
171 $argString = "CRM_CT_{$group}_{$path}_{$componentID}";
172 $cache = CRM_Utils_Cache::singleton();
173 $data = unserialize($dao->data);
e79e9c87 174 self::$_cache[$argString] = $data;
def0681b 175 $cache->set($argString, $data);
80259ba2
TO
176
177 $argString = "CRM_CT_CI_{$group}_{$componentID}";
e79e9c87 178 unset(self::$_cache[$argString]);
80259ba2 179 $cache->delete($argString);
6a488035
TO
180 }
181
182 /**
1a4e6781 183 * Delete all the cache elements that belong to a group OR delete the entire cache if group is not specified.
6a488035 184 *
6a0b768e
TO
185 * @param string $group
186 * The group name of the entries to be deleted.
187 * @param string $path
188 * Path of the item that needs to be deleted.
1a4e6781 189 * @param bool $clearAll clear all caches
6a488035 190 */
00be9182 191 public static function deleteGroup($group = NULL, $path = NULL, $clearAll = TRUE) {
6a488035
TO
192 $dao = new CRM_Core_DAO_Cache();
193
194 if (!empty($group)) {
195 $dao->group_name = $group;
196 }
197
198 if (!empty($path)) {
199 $dao->path = $path;
200 }
201
202 $dao->delete();
203
204 if ($clearAll) {
205 // also reset ACL Cache
206 CRM_ACL_BAO_Cache::resetCache();
207
208 // also reset memory cache if any
209 CRM_Utils_System::flushCache();
210 }
211 }
212
213 /**
214 * The next two functions are internal functions used to store and retrieve session from
215 * the database cache. This keeps the session to a limited size and allows us to
216 * create separate session scopes for each form in a tab
6a488035
TO
217 */
218
219 /**
220 * This function takes entries from the session array and stores it in the cache.
1a4e6781 221 *
6a488035
TO
222 * It also deletes the entries from the $_SESSION object (for a smaller session size)
223 *
6a0b768e
TO
224 * @param array $names
225 * Array of session values that should be persisted.
6a488035
TO
226 * This is either a form name + qfKey or just a form name
227 * (in the case of profile)
6a0b768e
TO
228 * @param bool $resetSession
229 * Should session state be reset on completion of DB store?.
6a488035 230 */
00be9182 231 public static function storeSessionToCache($names, $resetSession = TRUE) {
6a488035
TO
232 foreach ($names as $key => $sessionName) {
233 if (is_array($sessionName)) {
2aa397bc 234 $value = NULL;
6a488035
TO
235 if (!empty($_SESSION[$sessionName[0]][$sessionName[1]])) {
236 $value = $_SESSION[$sessionName[0]][$sessionName[1]];
237 }
238 self::setItem($value, 'CiviCRM Session', "{$sessionName[0]}_{$sessionName[1]}");
2aa397bc
TO
239 if ($resetSession) {
240 $_SESSION[$sessionName[0]][$sessionName[1]] = NULL;
241 unset($_SESSION[$sessionName[0]][$sessionName[1]]);
6a488035 242 }
2aa397bc 243 }
6a488035 244 else {
2aa397bc 245 $value = NULL;
6a488035
TO
246 if (!empty($_SESSION[$sessionName])) {
247 $value = $_SESSION[$sessionName];
248 }
249 self::setItem($value, 'CiviCRM Session', $sessionName);
2aa397bc
TO
250 if ($resetSession) {
251 $_SESSION[$sessionName] = NULL;
252 unset($_SESSION[$sessionName]);
6a488035
TO
253 }
254 }
2aa397bc 255 }
6a488035
TO
256
257 self::cleanup();
258 }
259
260 /* Retrieve the session values from the cache and populate the $_SESSION array
006389de
TO
261 *
262 * @param array $names
263 * Array of session values that should be persisted.
264 * This is either a form name + qfKey or just a form name
265 * (in the case of profile)
266 *
267 * @return void
268 */
6a488035 269
b5c2afd0 270 /**
1a4e6781
EM
271 * Restore session from cache.
272 *
100fef9d 273 * @param string $names
b5c2afd0 274 */
00be9182 275 public static function restoreSessionFromCache($names) {
6a488035
TO
276 foreach ($names as $key => $sessionName) {
277 if (is_array($sessionName)) {
278 $value = self::getItem('CiviCRM Session',
279 "{$sessionName[0]}_{$sessionName[1]}"
280 );
281 if ($value) {
282 $_SESSION[$sessionName[0]][$sessionName[1]] = $value;
283 }
284 }
285 else {
286 $value = self::getItem('CiviCRM Session',
287 $sessionName
288 );
289 if ($value) {
290 $_SESSION[$sessionName] = $value;
291 }
292 }
293 }
294 }
295
296 /**
1a4e6781
EM
297 * Do periodic cleanup of the CiviCRM session table.
298 *
299 * Also delete all session cache entries which are a couple of days old.
300 * This keeps the session cache to a manageable size
6a488035 301 *
554259a7
EM
302 * @param bool $session
303 * @param bool $table
304 * @param bool $prevNext
6a488035 305 */
2aa397bc 306 public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE) {
6a488035
TO
307 // clean up the session cache every $cacheCleanUpNumber probabilistically
308 $cleanUpNumber = 757;
309
310 // clean up all sessions older than $cacheTimeIntervalDays days
311 $timeIntervalDays = 2;
312 $timeIntervalMins = 30;
313
314 if (mt_rand(1, 100000) % $cleanUpNumber == 0) {
2aa397bc 315 $session = $table = $prevNext = TRUE;
6a488035
TO
316 }
317
f9f40af3 318 if (!$session && !$table && !$prevNext) {
6a488035
TO
319 return;
320 }
321
f9f40af3 322 if ($prevNext) {
6a488035
TO
323 // delete all PrevNext caches
324 CRM_Core_BAO_PrevNextCache::cleanupCache();
325 }
326
f9f40af3 327 if ($table) {
40c712ed 328 CRM_Core_Config::clearTempTables($timeIntervalDays . ' day');
6a488035
TO
329 }
330
f9f40af3 331 if ($session) {
6a488035
TO
332 // first delete all sessions which are related to any potential transaction
333 // page
334 $transactionPages = array(
f9f40af3
TO
335 'CRM_Contribute_Controller_Contribution',
336 'CRM_Event_Controller_Registration',
337 );
6a488035
TO
338
339 $params = array(
1a4e6781
EM
340 1 => array(
341 date('Y-m-d H:i:s', time() - $timeIntervalMins * 60),
342 'String',
343 ),
6a488035
TO
344 );
345 foreach ($transactionPages as $trPage) {
346 $params[] = array("%${trPage}%", 'String');
795492f3 347 $where[] = 'path LIKE %' . count($params);
6a488035
TO
348 }
349
350 $sql = "
351DELETE FROM civicrm_cache
352WHERE group_name = 'CiviCRM Session'
353AND created_date <= %1
f9f40af3 354AND (" . implode(' OR ', $where) . ")";
6a488035
TO
355 CRM_Core_DAO::executeQuery($sql, $params);
356
357 $sql = "
358DELETE FROM civicrm_cache
359WHERE group_name = 'CiviCRM Session'
360AND created_date < date_sub( NOW( ), INTERVAL $timeIntervalDays DAY )
361";
362 CRM_Core_DAO::executeQuery($sql);
8c52547a 363 }
6a488035 364 }
96025800 365
6a488035 366}