Test fixes for All kind of Custom Values
[civicrm-core.git] / CRM / Core / BAO / Cache.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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 +--------------------------------------------------------------------+
26 */
27
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 */
39 class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
40
41 /**
42 * @var array ($cacheKey => $cacheValue)
43 */
44 static $_cache = NULL;
45
46 /**
47 * Retrieve an item from the DB cache.
48 *
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).
55 *
56 * @return object
57 * The data if present in cache, else null
58 */
59 public static function &getItem($group, $path, $componentID = NULL) {
60 if (self::$_cache === NULL) {
61 self::$_cache = array();
62 }
63
64 $argString = "CRM_CT_{$group}_{$path}_{$componentID}";
65 if (!array_key_exists($argString, self::$_cache)) {
66 $cache = CRM_Utils_Cache::singleton();
67 self::$_cache[$argString] = $cache->get($argString);
68 if (!self::$_cache[$argString]) {
69 $dao = new CRM_Core_DAO_Cache();
70
71 $dao->group_name = $group;
72 $dao->path = $path;
73 $dao->component_id = $componentID;
74
75 $data = NULL;
76 if ($dao->find(TRUE)) {
77 $data = unserialize($dao->data);
78 }
79 $dao->free();
80 self::$_cache[$argString] = $data;
81 $cache->set($argString, self::$_cache[$argString]);
82 }
83 }
84 return self::$_cache[$argString];
85 }
86
87 /**
88 * Retrieve all items in a group.
89 *
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).
94 *
95 * @return object
96 * The data if present in cache, else null
97 */
98 public static function &getItems($group, $componentID = NULL) {
99 if (self::$_cache === NULL) {
100 self::$_cache = array();
101 }
102
103 $argString = "CRM_CT_CI_{$group}_{$componentID}";
104 if (!array_key_exists($argString, self::$_cache)) {
105 $cache = CRM_Utils_Cache::singleton();
106 self::$_cache[$argString] = $cache->get($argString);
107 if (!self::$_cache[$argString]) {
108 $dao = new CRM_Core_DAO_Cache();
109
110 $dao->group_name = $group;
111 $dao->component_id = $componentID;
112 $dao->find();
113
114 $result = array();
115 while ($dao->fetch()) {
116 $result[$dao->path] = unserialize($dao->data);
117 }
118 $dao->free();
119
120 self::$_cache[$argString] = $result;
121 $cache->set($argString, self::$_cache[$argString]);
122 }
123 }
124
125 return self::$_cache[$argString];
126 }
127
128 /**
129 * Store an item in the DB cache.
130 *
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).
139 */
140 public static function setItem(&$data, $group, $path, $componentID = NULL) {
141 if (self::$_cache === NULL) {
142 self::$_cache = array();
143 }
144
145 $dao = new CRM_Core_DAO_Cache();
146
147 $dao->group_name = $group;
148 $dao->path = $path;
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 $lock = Civi\Core\Container::singleton()->get('lockManager')->acquire("cache.{$group}_{$path}._{$componentID}");
155 if (!$lock->isAcquired()) {
156 CRM_Core_Error::fatal();
157 }
158
159 $dao->find(TRUE);
160 $dao->data = serialize($data);
161 $dao->created_date = date('YmdHis');
162 $dao->save();
163
164 $lock->release();
165
166 $dao->free();
167
168 // cache coherency - refresh or remove dependent caches
169
170 $argString = "CRM_CT_{$group}_{$path}_{$componentID}";
171 $cache = CRM_Utils_Cache::singleton();
172 $data = unserialize($dao->data);
173 self::$_cache[$argString] = $data;
174 $cache->set($argString, $data);
175
176 $argString = "CRM_CT_CI_{$group}_{$componentID}";
177 unset(self::$_cache[$argString]);
178 $cache->delete($argString);
179 }
180
181 /**
182 * Delete all the cache elements that belong to a group OR delete the entire cache if group is not specified.
183 *
184 * @param string $group
185 * The group name of the entries to be deleted.
186 * @param string $path
187 * Path of the item that needs to be deleted.
188 * @param bool $clearAll clear all caches
189 */
190 public static function deleteGroup($group = NULL, $path = NULL, $clearAll = TRUE) {
191 $dao = new CRM_Core_DAO_Cache();
192
193 if (!empty($group)) {
194 $dao->group_name = $group;
195 }
196
197 if (!empty($path)) {
198 $dao->path = $path;
199 }
200
201 $dao->delete();
202
203 if ($clearAll) {
204 // also reset ACL Cache
205 CRM_ACL_BAO_Cache::resetCache();
206
207 // also reset memory cache if any
208 CRM_Utils_System::flushCache();
209 }
210 }
211
212 /**
213 * The next two functions are internal functions used to store and retrieve session from
214 * the database cache. This keeps the session to a limited size and allows us to
215 * create separate session scopes for each form in a tab
216 */
217
218 /**
219 * This function takes entries from the session array and stores it in the cache.
220 *
221 * It also deletes the entries from the $_SESSION object (for a smaller session size)
222 *
223 * @param array $names
224 * Array of session values that should be persisted.
225 * This is either a form name + qfKey or just a form name
226 * (in the case of profile)
227 * @param bool $resetSession
228 * Should session state be reset on completion of DB store?.
229 */
230 public static function storeSessionToCache($names, $resetSession = TRUE) {
231 foreach ($names as $key => $sessionName) {
232 if (is_array($sessionName)) {
233 $value = NULL;
234 if (!empty($_SESSION[$sessionName[0]][$sessionName[1]])) {
235 $value = $_SESSION[$sessionName[0]][$sessionName[1]];
236 }
237 self::setItem($value, 'CiviCRM Session', "{$sessionName[0]}_{$sessionName[1]}");
238 if ($resetSession) {
239 $_SESSION[$sessionName[0]][$sessionName[1]] = NULL;
240 unset($_SESSION[$sessionName[0]][$sessionName[1]]);
241 }
242 }
243 else {
244 $value = NULL;
245 if (!empty($_SESSION[$sessionName])) {
246 $value = $_SESSION[$sessionName];
247 }
248 self::setItem($value, 'CiviCRM Session', $sessionName);
249 if ($resetSession) {
250 $_SESSION[$sessionName] = NULL;
251 unset($_SESSION[$sessionName]);
252 }
253 }
254 }
255
256 self::cleanup();
257 }
258
259 /* Retrieve the session values from the cache and populate the $_SESSION array
260 *
261 * @param array $names
262 * Array of session values that should be persisted.
263 * This is either a form name + qfKey or just a form name
264 * (in the case of profile)
265 *
266 * @return void
267 */
268
269 /**
270 * Restore session from cache.
271 *
272 * @param string $names
273 */
274 public static function restoreSessionFromCache($names) {
275 foreach ($names as $key => $sessionName) {
276 if (is_array($sessionName)) {
277 $value = self::getItem('CiviCRM Session',
278 "{$sessionName[0]}_{$sessionName[1]}"
279 );
280 if ($value) {
281 $_SESSION[$sessionName[0]][$sessionName[1]] = $value;
282 }
283 }
284 else {
285 $value = self::getItem('CiviCRM Session',
286 $sessionName
287 );
288 if ($value) {
289 $_SESSION[$sessionName] = $value;
290 }
291 }
292 }
293 }
294
295 /**
296 * Do periodic cleanup of the CiviCRM session table.
297 *
298 * Also delete all session cache entries which are a couple of days old.
299 * This keeps the session cache to a manageable size
300 *
301 * @param bool $session
302 * @param bool $table
303 * @param bool $prevNext
304 */
305 public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE) {
306 // clean up the session cache every $cacheCleanUpNumber probabilistically
307 $cleanUpNumber = 757;
308
309 // clean up all sessions older than $cacheTimeIntervalDays days
310 $timeIntervalDays = 2;
311 $timeIntervalMins = 30;
312
313 if (mt_rand(1, 100000) % $cleanUpNumber == 0) {
314 $session = $table = $prevNext = TRUE;
315 }
316
317 if (!$session && !$table && !$prevNext) {
318 return;
319 }
320
321 if ($prevNext) {
322 // delete all PrevNext caches
323 CRM_Core_BAO_PrevNextCache::cleanupCache();
324 }
325
326 if ($table) {
327 CRM_Core_Config::clearTempTables($timeIntervalDays . ' day');
328 }
329
330 if ($session) {
331 // first delete all sessions which are related to any potential transaction
332 // page
333 $transactionPages = array(
334 'CRM_Contribute_Controller_Contribution',
335 'CRM_Event_Controller_Registration',
336 );
337
338 $params = array(
339 1 => array(
340 date('Y-m-d H:i:s', time() - $timeIntervalMins * 60),
341 'String',
342 ),
343 );
344 foreach ($transactionPages as $trPage) {
345 $params[] = array("%${trPage}%", 'String');
346 $where[] = 'path LIKE %' . count($params);
347 }
348
349 $sql = "
350 DELETE FROM civicrm_cache
351 WHERE group_name = 'CiviCRM Session'
352 AND created_date <= %1
353 AND (" . implode(' OR ', $where) . ")";
354 CRM_Core_DAO::executeQuery($sql, $params);
355
356 $sql = "
357 DELETE FROM civicrm_cache
358 WHERE group_name = 'CiviCRM Session'
359 AND created_date < date_sub( NOW( ), INTERVAL $timeIntervalDays DAY )
360 ";
361 CRM_Core_DAO::executeQuery($sql);
362 }
363 }
364
365 }