f9c51bdf6d60dc9d50ddf26ece076a0f2ec4b93d
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
29 require_once "PEAR.php";
32 * Class CRM_Core_Session
34 class CRM_Core_Session
{
37 * Cache of all the session names that we manage
39 static $_managedNames = NULL;
42 * Key is used to allow the application to have multiple top
43 * level scopes rather than a single scope. (avoids naming
44 * conflicts). We also extend this idea further and have local
45 * scopes within a global scope. Allows us to do cool things
46 * like resetting a specific area of the session code while
47 * keeping the rest intact
51 protected $_key = 'CiviCRM';
52 const USER_CONTEXT
= 'userContext';
55 * This is just a reference to the real session. Allows us to
56 * debug this class a wee bit easier
60 protected $_session = NULL;
63 * We only need one instance of this object. So we use the singleton
64 * pattern and cache the instance in this variable
69 static private $_singleton = NULL;
74 * The CMS takes care of initiating the php session handler session_start().
76 * When using CiviCRM standalone (w/o a CMS), we start the session
77 * in index.php and then pass it off to here.
79 * All crm code should always use the session using
80 * CRM_Core_Session. we prefix stuff to avoid collisions with the CMS and also
81 * collisions with other crm modules!
83 * This constructor is invoked whenever any module requests an instance of
84 * the session and one is not available.
86 * @return CRM_Core_Session
88 public function __construct() {
89 $this->_session
= null;
93 * Singleton function used to manage this object
95 * @return CRM_Core_Session
98 public static function &singleton() {
99 if (self
::$_singleton === NULL) {
100 self
::$_singleton = new CRM_Core_Session
;
102 return self
::$_singleton;
106 * Creates an array in the session. All variables now will be stored
109 * @param boolean $isRead is this a read operation, in this case, the session will not be touched
115 public function initialize($isRead = FALSE) {
116 // lets initialize the _session variable just before we need it
117 // hopefully any bootstrapping code will actually load the session from the CMS
118 if (!isset($this->_session
)) {
120 if (!isset($_SESSION) && PHP_SAPI
!== 'cli') {
124 $config =& CRM_Core_Config
::singleton();
125 // FIXME: This belongs in CRM_Utils_System_*
126 if ($config->userSystem
->is_drupal
&& function_exists('drupal_session_start')) {
127 // https://issues.civicrm.org/jira/browse/CRM-14356
128 if (! (isset($GLOBALS['lazy_session']) && $GLOBALS['lazy_session'] == true)) {
129 drupal_session_start();
137 $this->_session
=& $_SESSION;
144 if (!isset($this->_session
[$this->_key
]) ||
145 !is_array($this->_session
[$this->_key
])
147 $this->_session
[$this->_key
] = array();
153 * Resets the session store
161 public function reset($all = 1) {
165 // to make certain we clear it, first initialize it to empty
166 $this->_session
[$this->_key
] = array();
167 unset($this->_session
[$this->_key
]);
170 $this->_session
= array();
177 * Creates a session local scope
179 * @param string $prefix local scope name
180 * @param boolean $isRead is this a read operation, in this case, the session will not be touched
186 public function createScope($prefix, $isRead = FALSE) {
187 $this->initialize($isRead);
189 if ($isRead ||
empty($prefix)) {
193 if (empty($this->_session
[$this->_key
][$prefix])) {
194 $this->_session
[$this->_key
][$prefix] = array();
199 * Resets the session local scope
201 * @param string $prefix local scope name
206 public function resetScope($prefix) {
209 if (empty($prefix)) {
213 if (array_key_exists($prefix, $this->_session
[$this->_key
])) {
214 unset($this->_session
[$this->_key
][$prefix]);
219 * Store the variable with the value in the session scope
221 * This function takes a name, value pair and stores this
222 * in the session scope. Not sure what happens if we try
223 * to store complex objects in the session. I suspect it
224 * is supported but we need to verify this
228 * @param string $name name of the variable
229 * @param mixed $value value of the variable
230 * @param string $prefix a string to prefix the keys in the session with
235 public function set($name, $value = NULL, $prefix = NULL) {
236 // create session scope
237 $this->createScope($prefix);
239 if (empty($prefix)) {
240 $session = &$this->_session
[$this->_key
];
243 $session = &$this->_session
[$this->_key
][$prefix];
246 if (is_array($name)) {
247 foreach ($name as $n => $v) {
252 $session[$name] = $value;
257 * Gets the value of the named variable in the session scope
259 * This function takes a name and retrieves the value of this
260 * variable from the session scope.
264 * @param string $name : name of the variable
265 * @param string $prefix : adds another level of scope to the session
270 public function get($name, $prefix = NULL) {
271 // create session scope
272 $this->createScope($prefix, TRUE);
274 if (empty($this->_session
) ||
empty($this->_session
[$this->_key
])) {
278 if (empty($prefix)) {
279 $session =& $this->_session
[$this->_key
];
282 if (empty($this->_session
[$this->_key
][$prefix])) {
285 $session =& $this->_session
[$this->_key
][$prefix];
288 return CRM_Utils_Array
::value($name, $session);
292 * Gets all the variables in the current session scope
293 * and stuffs them in an associate array
297 * @param array $vars associative array to store name/value pairs
298 * @param string $prefix will be stripped from the key before putting it in the return
303 public function getVars(&$vars, $prefix = '') {
304 // create session scope
305 $this->createScope($prefix, TRUE);
307 if (empty($prefix)) {
308 $values = &$this->_session
[$this->_key
];
311 $values = CRM_Core_BAO_Cache
::getItem('CiviCRM Session', "CiviCRM_{$prefix}");
315 foreach ($values as $name => $value) {
316 $vars[$name] = $value;
322 * Set and check a timer. If it's expired, it will be set again.
323 * Good for showing a message to the user every hour or day (so not bugging them on every page)
324 * Returns true-ish values if the timer is not set or expired, and false if the timer is still running
325 * If you want to get more nuanced, you can check the type of the return to see if it's 'not set' or actually expired at a certain time
329 * @param string $name : name of the timer
330 * @param int $expire : expiry time (in seconds)
335 public function timer($name, $expire) {
336 $ts = $this->get($name, 'timer');
337 if (!$ts ||
$ts < time() - $expire) {
338 $this->set($name, time(), 'timer');
339 return $ts ?
$ts : 'not set';
345 * Adds a userContext to the stack
347 * @param string $userContext the url to return to when done
348 * @param boolean $check should we do a dupe checking with the top element
355 public function pushUserContext($userContext, $check = TRUE) {
356 if (empty($userContext)) {
360 $this->createScope(self
::USER_CONTEXT
);
362 // hack, reset if too big
363 if (count($this->_session
[$this->_key
][self
::USER_CONTEXT
]) > 10) {
364 $this->resetScope(self
::USER_CONTEXT
);
365 $this->createScope(self
::USER_CONTEXT
);
368 $topUC = array_pop($this->_session
[$this->_key
][self
::USER_CONTEXT
]);
370 // see if there is a match between the new UC and the top one. the match needs to be
371 // fuzzy since we use the referer at times
372 // if close enough, lets just replace the top with the new one
373 if ($check && $topUC && CRM_Utils_String
::match($topUC, $userContext)) {
374 array_push($this->_session
[$this->_key
][self
::USER_CONTEXT
], $userContext);
378 array_push($this->_session
[$this->_key
][self
::USER_CONTEXT
], $topUC);
380 array_push($this->_session
[$this->_key
][self
::USER_CONTEXT
], $userContext);
385 * Replace the userContext of the stack with the passed one
387 * @param string $userContext the url to return to when done
394 public function replaceUserContext($userContext) {
395 if (empty($userContext)) {
399 $this->createScope(self
::USER_CONTEXT
);
401 array_pop($this->_session
[$this->_key
][self
::USER_CONTEXT
]);
402 array_push($this->_session
[$this->_key
][self
::USER_CONTEXT
], $userContext);
406 * Pops the top userContext stack
408 * @return string the top of the userContext stack (also pops the top element)
411 public function popUserContext() {
412 $this->createScope(self
::USER_CONTEXT
);
414 return array_pop($this->_session
[$this->_key
][self
::USER_CONTEXT
]);
418 * Reads the top userContext stack
420 * @return string the top of the userContext stack
423 public function readUserContext() {
424 $this->createScope(self
::USER_CONTEXT
);
426 $config = CRM_Core_Config
::singleton();
427 $lastElement = count($this->_session
[$this->_key
][self
::USER_CONTEXT
]) - 1;
428 return $lastElement >= 0 ?
$this->_session
[$this->_key
][self
::USER_CONTEXT
][$lastElement] : $config->userFrameworkBaseURL
;
432 * Dumps the session to the log
435 public function debug($all = 1) {
438 CRM_Core_Error
::debug('CRM Session', $this->_session
);
441 CRM_Core_Error
::debug('CRM Session', $this->_session
[$this->_key
]);
446 * Fetches status messages
448 * @param bool $reset should we reset the status variable?
450 * @return string the status message if any
452 public function getStatus($reset = FALSE) {
456 if (array_key_exists('status', $this->_session
[$this->_key
])) {
457 $status = $this->_session
[$this->_key
]['status'];
460 $this->_session
[$this->_key
]['status'] = NULL;
461 unset($this->_session
[$this->_key
]['status']);
467 * Stores an alert to be displayed to the user via crm-messages
469 * @param $text string
472 * @param $title string
473 * The optional title of this message
475 * @param $type string
476 * The type of this message (printed as a css class). Possible options:
477 * - 'alert' (default)
480 * - 'error' (this message type by default will remain on the screen
481 * until the user dismisses it)
482 * - 'no-popup' (will display in the document like old-school)
484 * @param $options array
485 * Additional options. Possible values:
486 * - 'unique' (default: true) Check if this message was already set before adding
487 * - 'expires' how long to display this message before fadeout (in ms)
488 * set to 0 for no expiration
489 * defaults to 10 seconds for most messages, 5 if it has a title but no body,
490 * or 0 for errors or messages containing links
496 public static function setStatus($text, $title = '', $type = 'alert', $options = array()) {
497 // make sure session is initialized, CRM-8120
498 $session = self
::singleton();
499 $session->initialize();
502 $options +
= array('unique' => TRUE);
504 if (!isset(self
::$_singleton->_session
[self
::$_singleton->_key
]['status'])) {
505 self
::$_singleton->_session
[self
::$_singleton->_key
]['status'] = array();
507 if ($text ||
$title) {
508 if ($options['unique']) {
509 foreach (self
::$_singleton->_session
[self
::$_singleton->_key
]['status'] as $msg) {
510 if ($msg['text'] == $text && $msg['title'] == $title) {
515 unset($options['unique']);
516 self
::$_singleton->_session
[self
::$_singleton->_key
]['status'][] = array(
520 'options' => $options ?
$options : NULL,
526 * @param string|array $names
528 public static function registerAndRetrieveSessionObjects($names) {
529 if (!is_array($names)) {
530 $names = array($names);
533 if (!self
::$_managedNames) {
534 self
::$_managedNames = $names;
537 self
::$_managedNames = array_merge(self
::$_managedNames, $names);
540 CRM_Core_BAO_Cache
::restoreSessionFromCache($names);
546 public static function storeSessionObjects($reset = TRUE) {
547 if (empty(self
::$_managedNames)) {
551 self
::$_managedNames = CRM_Utils_Array
::crmArrayUnique(self
::$_managedNames);
553 CRM_Core_BAO_Cache
::storeSessionToCache(self
::$_managedNames, $reset);
555 self
::$_managedNames = NULL;
559 * Retrieve contact id of the logged in user
560 * @return integer|NULL contact ID of logged in user
562 public static function getLoggedInContactID() {
563 $session = CRM_Core_Session
::singleton();
564 if (!is_numeric($session->get('userID'))) {
567 return $session->get('userID');
573 public function isEmpty() {
574 // check if session is empty, if so we don't cache
575 // stuff that we can get away with
576 // helps proxies like varnish
577 return empty($_SESSION);