Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
bc77d7c0 TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 | 11 | |
4c6ce474 | 12 | /** |
8eedd10a | 13 | * Class CRM_Core_Session. |
4c6ce474 | 14 | */ |
6a488035 TO |
15 | class CRM_Core_Session { |
16 | ||
17 | /** | |
f9e31d7f | 18 | * Cache of all the session names that we manage. |
518fa0ee | 19 | * @var array |
6a488035 | 20 | */ |
518fa0ee | 21 | public static $_managedNames = NULL; |
6a488035 TO |
22 | |
23 | /** | |
100fef9d | 24 | * Key is used to allow the application to have multiple top |
6a488035 TO |
25 | * level scopes rather than a single scope. (avoids naming |
26 | * conflicts). We also extend this idea further and have local | |
27 | * scopes within a global scope. Allows us to do cool things | |
28 | * like resetting a specific area of the session code while | |
29 | * keeping the rest intact | |
30 | * | |
31 | * @var string | |
32 | */ | |
33 | protected $_key = 'CiviCRM'; | |
7da04cde | 34 | const USER_CONTEXT = 'userContext'; |
6a488035 TO |
35 | |
36 | /** | |
37 | * This is just a reference to the real session. Allows us to | |
38 | * debug this class a wee bit easier | |
39 | * | |
40 | * @var object | |
41 | */ | |
42 | protected $_session = NULL; | |
43 | ||
9b418792 | 44 | /** |
45 | * Current php Session ID : needed to detect if the session is changed | |
46 | * | |
47 | * @var string | |
48 | */ | |
49 | protected $sessionID; | |
50 | ||
6a488035 TO |
51 | /** |
52 | * We only need one instance of this object. So we use the singleton | |
53 | * pattern and cache the instance in this variable | |
54 | * | |
65d27ad8 | 55 | * @var \CRM_Core_Session |
6a488035 | 56 | */ |
65d27ad8 | 57 | static private $_singleton; |
6a488035 TO |
58 | |
59 | /** | |
f9e31d7f | 60 | * Constructor. |
6a488035 | 61 | * |
d4ad6ab3 | 62 | * The CMS takes care of initiating the php session handler session_start(). |
6a488035 | 63 | * |
6a488035 | 64 | * All crm code should always use the session using |
d4ad6ab3 CW |
65 | * CRM_Core_Session. we prefix stuff to avoid collisions with the CMS and also |
66 | * collisions with other crm modules! | |
67 | * | |
6a488035 TO |
68 | * This constructor is invoked whenever any module requests an instance of |
69 | * the session and one is not available. | |
70 | * | |
d4ad6ab3 | 71 | * @return CRM_Core_Session |
6a488035 | 72 | */ |
00be9182 | 73 | public function __construct() { |
2aa397bc | 74 | $this->_session = NULL; |
6a488035 TO |
75 | } |
76 | ||
77 | /** | |
f9e31d7f | 78 | * Singleton function used to manage this object. |
6a488035 | 79 | * |
d46561f5 | 80 | * @return CRM_Core_Session |
6a488035 | 81 | */ |
00be9182 | 82 | public static function &singleton() { |
6a488035 | 83 | if (self::$_singleton === NULL) { |
317fceb4 | 84 | self::$_singleton = new CRM_Core_Session(); |
6a488035 TO |
85 | } |
86 | return self::$_singleton; | |
87 | } | |
88 | ||
bb1d1976 TO |
89 | /** |
90 | * Replace the session object with a fake session. | |
91 | */ | |
92 | public static function useFakeSession() { | |
93 | self::$_singleton = new class() extends CRM_Core_Session { | |
94 | ||
95 | public function initialize($isRead = FALSE) { | |
96 | if ($isRead) { | |
97 | return; | |
98 | } | |
99 | ||
100 | if (!isset($this->_session)) { | |
101 | $this->_session = []; | |
102 | } | |
103 | ||
104 | if (!isset($this->_session[$this->_key]) || !is_array($this->_session[$this->_key])) { | |
105 | $this->_session[$this->_key] = []; | |
106 | } | |
107 | } | |
108 | ||
109 | public function isEmpty() { | |
110 | return empty($this->_session); | |
111 | } | |
112 | ||
113 | }; | |
114 | self::$_singleton->_session = NULL; | |
115 | // This is not a revocable proposition. Should survive, even with things 'System.flush'. | |
116 | if (!defined('_CIVICRM_FAKE_SESSION')) { | |
117 | define('_CIVICRM_FAKE_SESSION', TRUE); | |
118 | } | |
119 | return self::$_singleton; | |
120 | } | |
121 | ||
6a488035 | 122 | /** |
8eedd10a | 123 | * Creates an array in the session. |
124 | * | |
125 | * All variables now will be stored under this array. | |
6a488035 | 126 | * |
6a0b768e TO |
127 | * @param bool $isRead |
128 | * Is this a read operation, in this case, the session will not be touched. | |
6a488035 | 129 | */ |
00be9182 | 130 | public function initialize($isRead = FALSE) { |
9b418792 | 131 | // remove $_SESSION reference if session is changed |
132 | if (($sid = session_id()) !== $this->sessionID) { | |
133 | $this->_session = NULL; | |
134 | $this->sessionID = $sid; | |
135 | } | |
6a488035 TO |
136 | // lets initialize the _session variable just before we need it |
137 | // hopefully any bootstrapping code will actually load the session from the CMS | |
138 | if (!isset($this->_session)) { | |
139 | // CRM-9483 | |
140 | if (!isset($_SESSION) && PHP_SAPI !== 'cli') { | |
141 | if ($isRead) { | |
142 | return; | |
143 | } | |
671f655b | 144 | CRM_Core_Config::singleton()->userSystem->sessionStart(); |
6a488035 TO |
145 | } |
146 | $this->_session =& $_SESSION; | |
147 | } | |
148 | ||
149 | if ($isRead) { | |
150 | return; | |
151 | } | |
152 | ||
153 | if (!isset($this->_session[$this->_key]) || | |
154 | !is_array($this->_session[$this->_key]) | |
155 | ) { | |
be2fb01f | 156 | $this->_session[$this->_key] = []; |
6a488035 | 157 | } |
6a488035 TO |
158 | } |
159 | ||
160 | /** | |
f9e31d7f | 161 | * Resets the session store. |
6a488035 | 162 | * |
dd244018 | 163 | * @param int $all |
6a488035 | 164 | */ |
00be9182 | 165 | public function reset($all = 1) { |
6a488035 TO |
166 | if ($all != 1) { |
167 | $this->initialize(); | |
168 | ||
169 | // to make certain we clear it, first initialize it to empty | |
be2fb01f | 170 | $this->_session[$this->_key] = []; |
6a488035 TO |
171 | unset($this->_session[$this->_key]); |
172 | } | |
173 | else { | |
be2fb01f | 174 | $this->_session = []; |
6a488035 TO |
175 | } |
176 | ||
6a488035 TO |
177 | } |
178 | ||
179 | /** | |
f9e31d7f | 180 | * Creates a session local scope. |
6a488035 | 181 | * |
6a0b768e TO |
182 | * @param string $prefix |
183 | * Local scope name. | |
184 | * @param bool $isRead | |
185 | * Is this a read operation, in this case, the session will not be touched. | |
6a488035 | 186 | */ |
00be9182 | 187 | public function createScope($prefix, $isRead = FALSE) { |
6a488035 TO |
188 | $this->initialize($isRead); |
189 | ||
190 | if ($isRead || empty($prefix)) { | |
191 | return; | |
192 | } | |
193 | ||
a7488080 | 194 | if (empty($this->_session[$this->_key][$prefix])) { |
be2fb01f | 195 | $this->_session[$this->_key][$prefix] = []; |
6a488035 TO |
196 | } |
197 | } | |
198 | ||
199 | /** | |
f9e31d7f | 200 | * Resets the session local scope. |
6a488035 | 201 | * |
6a0b768e TO |
202 | * @param string $prefix |
203 | * Local scope name. | |
6a488035 | 204 | */ |
00be9182 | 205 | public function resetScope($prefix) { |
6a488035 TO |
206 | $this->initialize(); |
207 | ||
208 | if (empty($prefix)) { | |
209 | return; | |
210 | } | |
211 | ||
212 | if (array_key_exists($prefix, $this->_session[$this->_key])) { | |
213 | unset($this->_session[$this->_key][$prefix]); | |
214 | } | |
215 | } | |
216 | ||
217 | /** | |
f9e31d7f | 218 | * Store the variable with the value in the session scope. |
6a488035 TO |
219 | * |
220 | * This function takes a name, value pair and stores this | |
221 | * in the session scope. Not sure what happens if we try | |
222 | * to store complex objects in the session. I suspect it | |
223 | * is supported but we need to verify this | |
224 | * | |
6a488035 | 225 | * |
6a0b768e TO |
226 | * @param string $name |
227 | * Name of the variable. | |
228 | * @param mixed $value | |
229 | * Value of the variable. | |
230 | * @param string $prefix | |
231 | * A string to prefix the keys in the session with. | |
6a488035 | 232 | */ |
00be9182 | 233 | public function set($name, $value = NULL, $prefix = NULL) { |
6a488035 TO |
234 | // create session scope |
235 | $this->createScope($prefix); | |
236 | ||
237 | if (empty($prefix)) { | |
238 | $session = &$this->_session[$this->_key]; | |
239 | } | |
240 | else { | |
241 | $session = &$this->_session[$this->_key][$prefix]; | |
242 | } | |
243 | ||
244 | if (is_array($name)) { | |
245 | foreach ($name as $n => $v) { | |
246 | $session[$n] = $v; | |
247 | } | |
248 | } | |
249 | else { | |
250 | $session[$name] = $value; | |
251 | } | |
252 | } | |
253 | ||
254 | /** | |
f9e31d7f | 255 | * Gets the value of the named variable in the session scope. |
6a488035 TO |
256 | * |
257 | * This function takes a name and retrieves the value of this | |
258 | * variable from the session scope. | |
259 | * | |
6a488035 | 260 | * |
6a0b768e | 261 | * @param string $name |
16b10e64 | 262 | * name of the variable. |
6a0b768e | 263 | * @param string $prefix |
16b10e64 | 264 | * adds another level of scope to the session. |
6a488035 TO |
265 | * |
266 | * @return mixed | |
6a488035 | 267 | */ |
00be9182 | 268 | public function get($name, $prefix = NULL) { |
6a488035 TO |
269 | // create session scope |
270 | $this->createScope($prefix, TRUE); | |
271 | ||
272 | if (empty($this->_session) || empty($this->_session[$this->_key])) { | |
2aa397bc | 273 | return NULL; |
6a488035 TO |
274 | } |
275 | ||
276 | if (empty($prefix)) { | |
277 | $session =& $this->_session[$this->_key]; | |
278 | } | |
279 | else { | |
280 | if (empty($this->_session[$this->_key][$prefix])) { | |
2aa397bc | 281 | return NULL; |
6a488035 TO |
282 | } |
283 | $session =& $this->_session[$this->_key][$prefix]; | |
284 | } | |
285 | ||
914d3734 | 286 | return $session[$name] ?? NULL; |
6a488035 TO |
287 | } |
288 | ||
289 | /** | |
8eedd10a | 290 | * Gets all the variables in the current session scope and stuffs them in an associate array. |
6a488035 | 291 | * |
6a0b768e TO |
292 | * @param array $vars |
293 | * Associative array to store name/value pairs. | |
294 | * @param string $prefix | |
295 | * Will be stripped from the key before putting it in the return. | |
6a488035 | 296 | */ |
00be9182 | 297 | public function getVars(&$vars, $prefix = '') { |
6a488035 TO |
298 | // create session scope |
299 | $this->createScope($prefix, TRUE); | |
300 | ||
301 | if (empty($prefix)) { | |
302 | $values = &$this->_session[$this->_key]; | |
303 | } | |
304 | else { | |
19707a63 | 305 | $values = Civi::cache('session')->get("CiviCRM_{$prefix}"); |
6a488035 TO |
306 | } |
307 | ||
308 | if ($values) { | |
309 | foreach ($values as $name => $value) { | |
310 | $vars[$name] = $value; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | /** | |
8eedd10a | 316 | * Set and check a timer. |
317 | * | |
318 | * If it's expired, it will be set again. | |
319 | * | |
6a488035 TO |
320 | * Good for showing a message to the user every hour or day (so not bugging them on every page) |
321 | * Returns true-ish values if the timer is not set or expired, and false if the timer is still running | |
322 | * 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 | |
323 | * | |
6a488035 | 324 | * |
6a0b768e | 325 | * @param string $name |
16b10e64 | 326 | * name of the timer. |
6a0b768e | 327 | * @param int $expire |
16b10e64 | 328 | * expiry time (in seconds). |
6a488035 TO |
329 | * |
330 | * @return mixed | |
6a488035 | 331 | */ |
00be9182 | 332 | public function timer($name, $expire) { |
6a488035 TO |
333 | $ts = $this->get($name, 'timer'); |
334 | if (!$ts || $ts < time() - $expire) { | |
335 | $this->set($name, time(), 'timer'); | |
336 | return $ts ? $ts : 'not set'; | |
337 | } | |
2aa397bc | 338 | return FALSE; |
6a488035 TO |
339 | } |
340 | ||
341 | /** | |
f9e31d7f | 342 | * Adds a userContext to the stack. |
6a488035 | 343 | * |
6a0b768e TO |
344 | * @param string $userContext |
345 | * The url to return to when done. | |
346 | * @param bool $check | |
347 | * Should we do a dupe checking with the top element. | |
6a488035 | 348 | */ |
00be9182 | 349 | public function pushUserContext($userContext, $check = TRUE) { |
6a488035 TO |
350 | if (empty($userContext)) { |
351 | return; | |
352 | } | |
353 | ||
354 | $this->createScope(self::USER_CONTEXT); | |
355 | ||
356 | // hack, reset if too big | |
357 | if (count($this->_session[$this->_key][self::USER_CONTEXT]) > 10) { | |
358 | $this->resetScope(self::USER_CONTEXT); | |
359 | $this->createScope(self::USER_CONTEXT); | |
360 | } | |
361 | ||
362 | $topUC = array_pop($this->_session[$this->_key][self::USER_CONTEXT]); | |
363 | ||
364 | // see if there is a match between the new UC and the top one. the match needs to be | |
365 | // fuzzy since we use the referer at times | |
366 | // if close enough, lets just replace the top with the new one | |
367 | if ($check && $topUC && CRM_Utils_String::match($topUC, $userContext)) { | |
368 | array_push($this->_session[$this->_key][self::USER_CONTEXT], $userContext); | |
369 | } | |
370 | else { | |
371 | if ($topUC) { | |
372 | array_push($this->_session[$this->_key][self::USER_CONTEXT], $topUC); | |
373 | } | |
374 | array_push($this->_session[$this->_key][self::USER_CONTEXT], $userContext); | |
375 | } | |
376 | } | |
377 | ||
378 | /** | |
f9e31d7f | 379 | * Replace the userContext of the stack with the passed one. |
6a488035 | 380 | * |
6a0b768e TO |
381 | * @param string $userContext |
382 | * The url to return to when done. | |
6a488035 | 383 | */ |
00be9182 | 384 | public function replaceUserContext($userContext) { |
6a488035 TO |
385 | if (empty($userContext)) { |
386 | return; | |
387 | } | |
388 | ||
389 | $this->createScope(self::USER_CONTEXT); | |
390 | ||
391 | array_pop($this->_session[$this->_key][self::USER_CONTEXT]); | |
392 | array_push($this->_session[$this->_key][self::USER_CONTEXT], $userContext); | |
393 | } | |
394 | ||
395 | /** | |
f9e31d7f | 396 | * Pops the top userContext stack. |
6a488035 | 397 | * |
a6c01b45 CW |
398 | * @return string |
399 | * the top of the userContext stack (also pops the top element) | |
6a488035 | 400 | */ |
00be9182 | 401 | public function popUserContext() { |
6a488035 TO |
402 | $this->createScope(self::USER_CONTEXT); |
403 | ||
404 | return array_pop($this->_session[$this->_key][self::USER_CONTEXT]); | |
405 | } | |
406 | ||
407 | /** | |
f9e31d7f | 408 | * Reads the top userContext stack. |
6a488035 | 409 | * |
a6c01b45 CW |
410 | * @return string |
411 | * the top of the userContext stack | |
6a488035 | 412 | */ |
00be9182 | 413 | public function readUserContext() { |
6a488035 TO |
414 | $this->createScope(self::USER_CONTEXT); |
415 | ||
416 | $config = CRM_Core_Config::singleton(); | |
417 | $lastElement = count($this->_session[$this->_key][self::USER_CONTEXT]) - 1; | |
418 | return $lastElement >= 0 ? $this->_session[$this->_key][self::USER_CONTEXT][$lastElement] : $config->userFrameworkBaseURL; | |
419 | } | |
420 | ||
421 | /** | |
f9e31d7f | 422 | * Dumps the session to the log. |
8eedd10a | 423 | * |
d4ad6ab3 | 424 | * @param int $all |
6a488035 | 425 | */ |
00be9182 | 426 | public function debug($all = 1) { |
6a488035 TO |
427 | $this->initialize(); |
428 | if ($all != 1) { | |
429 | CRM_Core_Error::debug('CRM Session', $this->_session); | |
430 | } | |
431 | else { | |
432 | CRM_Core_Error::debug('CRM Session', $this->_session[$this->_key]); | |
433 | } | |
434 | } | |
435 | ||
436 | /** | |
f9e31d7f | 437 | * Fetches status messages. |
6a488035 | 438 | * |
6a0b768e TO |
439 | * @param bool $reset |
440 | * Should we reset the status variable?. | |
6a488035 | 441 | * |
a6c01b45 CW |
442 | * @return string |
443 | * the status message if any | |
6a488035 | 444 | */ |
00be9182 | 445 | public function getStatus($reset = FALSE) { |
6a488035 TO |
446 | $this->initialize(); |
447 | ||
448 | $status = NULL; | |
449 | if (array_key_exists('status', $this->_session[$this->_key])) { | |
450 | $status = $this->_session[$this->_key]['status']; | |
451 | } | |
452 | if ($reset) { | |
453 | $this->_session[$this->_key]['status'] = NULL; | |
454 | unset($this->_session[$this->_key]['status']); | |
455 | } | |
456 | return $status; | |
457 | } | |
458 | ||
459 | /** | |
8eedd10a | 460 | * Stores an alert to be displayed to the user via crm-messages. |
6a488035 | 461 | * |
5a4f6742 | 462 | * @param string $text |
6a488035 TO |
463 | * The status message |
464 | * | |
5a4f6742 | 465 | * @param string $title |
6a488035 TO |
466 | * The optional title of this message |
467 | * | |
5a4f6742 | 468 | * @param string $type |
6a488035 TO |
469 | * The type of this message (printed as a css class). Possible options: |
470 | * - 'alert' (default) | |
471 | * - 'info' | |
472 | * - 'success' | |
473 | * - 'error' (this message type by default will remain on the screen | |
474 | * until the user dismisses it) | |
475 | * - 'no-popup' (will display in the document like old-school) | |
476 | * | |
5a4f6742 | 477 | * @param array $options |
6a488035 TO |
478 | * Additional options. Possible values: |
479 | * - 'unique' (default: true) Check if this message was already set before adding | |
480 | * - 'expires' how long to display this message before fadeout (in ms) | |
481 | * set to 0 for no expiration | |
482 | * defaults to 10 seconds for most messages, 5 if it has a title but no body, | |
483 | * or 0 for errors or messages containing links | |
6a488035 | 484 | */ |
be2fb01f | 485 | public static function setStatus($text, $title = '', $type = 'alert', $options = []) { |
6a488035 TO |
486 | // make sure session is initialized, CRM-8120 |
487 | $session = self::singleton(); | |
488 | $session->initialize(); | |
489 | ||
98b2a189 SM |
490 | // Sanitize any HTML we're displaying. This helps prevent reflected XSS in error messages. |
491 | $text = CRM_Utils_String::purifyHTML($text); | |
492 | $title = CRM_Utils_String::purifyHTML($title); | |
493 | ||
6a488035 | 494 | // default options |
be2fb01f | 495 | $options += ['unique' => TRUE]; |
6a488035 TO |
496 | |
497 | if (!isset(self::$_singleton->_session[self::$_singleton->_key]['status'])) { | |
be2fb01f | 498 | self::$_singleton->_session[self::$_singleton->_key]['status'] = []; |
6a488035 | 499 | } |
bc86ffa9 | 500 | if ($text || $title) { |
6a488035 TO |
501 | if ($options['unique']) { |
502 | foreach (self::$_singleton->_session[self::$_singleton->_key]['status'] as $msg) { | |
503 | if ($msg['text'] == $text && $msg['title'] == $title) { | |
504 | return; | |
505 | } | |
506 | } | |
507 | } | |
508 | unset($options['unique']); | |
be2fb01f | 509 | self::$_singleton->_session[self::$_singleton->_key]['status'][] = [ |
6a488035 TO |
510 | 'text' => $text, |
511 | 'title' => $title, | |
512 | 'type' => $type, | |
03a7ec8f | 513 | 'options' => $options ? $options : NULL, |
be2fb01f | 514 | ]; |
6a488035 TO |
515 | } |
516 | } | |
517 | ||
a0ee3941 | 518 | /** |
8eedd10a | 519 | * Register and retrieve session objects. |
520 | * | |
d4ad6ab3 | 521 | * @param string|array $names |
a0ee3941 | 522 | */ |
00be9182 | 523 | public static function registerAndRetrieveSessionObjects($names) { |
6a488035 | 524 | if (!is_array($names)) { |
be2fb01f | 525 | $names = [$names]; |
6a488035 TO |
526 | } |
527 | ||
528 | if (!self::$_managedNames) { | |
529 | self::$_managedNames = $names; | |
530 | } | |
531 | else { | |
532 | self::$_managedNames = array_merge(self::$_managedNames, $names); | |
533 | } | |
534 | ||
535 | CRM_Core_BAO_Cache::restoreSessionFromCache($names); | |
536 | } | |
537 | ||
a0ee3941 | 538 | /** |
8eedd10a | 539 | * Store session objects. |
540 | * | |
a0ee3941 EM |
541 | * @param bool $reset |
542 | */ | |
00be9182 | 543 | public static function storeSessionObjects($reset = TRUE) { |
6a488035 TO |
544 | if (empty(self::$_managedNames)) { |
545 | return; | |
546 | } | |
547 | ||
548 | self::$_managedNames = CRM_Utils_Array::crmArrayUnique(self::$_managedNames); | |
549 | ||
550 | CRM_Core_BAO_Cache::storeSessionToCache(self::$_managedNames, $reset); | |
551 | ||
552 | self::$_managedNames = NULL; | |
553 | } | |
554 | ||
bb341097 | 555 | /** |
f9e31d7f | 556 | * Retrieve contact id of the logged in user. |
8eedd10a | 557 | * |
e97c66ff | 558 | * @return int|null |
72b3a70c | 559 | * contact ID of logged in user |
bb341097 | 560 | */ |
00be9182 | 561 | public static function getLoggedInContactID() { |
bb341097 EM |
562 | $session = CRM_Core_Session::singleton(); |
563 | if (!is_numeric($session->get('userID'))) { | |
564 | return NULL; | |
565 | } | |
566 | return $session->get('userID'); | |
567 | } | |
568 | ||
8f864776 | 569 | /** |
570 | * Get display name of the logged in user. | |
571 | * | |
572 | * @return string | |
573 | * | |
574 | * @throws CiviCRM_API3_Exception | |
575 | */ | |
576 | public function getLoggedInContactDisplayName() { | |
3bdcd4ec | 577 | $userContactID = CRM_Core_Session::getLoggedInContactID(); |
8f864776 | 578 | if (!$userContactID) { |
579 | return ''; | |
580 | } | |
be2fb01f | 581 | return civicrm_api3('Contact', 'getvalue', ['id' => $userContactID, 'return' => 'display_name']); |
8f864776 | 582 | } |
583 | ||
a0ee3941 | 584 | /** |
8eedd10a | 585 | * Check if session is empty. |
586 | * | |
587 | * if so we don't cache stuff that we can get away with, helps proxies like varnish. | |
588 | * | |
a0ee3941 EM |
589 | * @return bool |
590 | */ | |
00be9182 | 591 | public function isEmpty() { |
d4ad6ab3 | 592 | return empty($_SESSION); |
6a488035 | 593 | } |
96025800 | 594 | |
6a488035 | 595 | } |