ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilSession.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22{
28 public const int SESSION_CLOSE_USER = 1; // manual logout
29 public const int SESSION_CLOSE_EXPIRE = 2; // has expired
30 public const int SESSION_CLOSE_LOGIN = 6; // anonymous => login
31 public const int SESSION_CLOSE_PUBLIC = 7; // => anonymous
32 public const int SESSION_CLOSE_TIME = 8; // account time limit reached
33 public const int SESSION_CLOSE_IP = 9; // wrong ip
34 public const int SESSION_CLOSE_SIMUL = 10; // simultaneous login
35 public const int SESSION_CLOSE_INACTIVE = 11; // inactive account
36
37 private static ?int $closing_context = null;
38
39 protected static bool $enable_web_access_without_session = false;
40
50 public static function _getData(string $a_session_id): string
51 {
52 if (!$a_session_id) {
53 // fix for php #70520
54 return '';
55 }
56 global $DIC;
57
58 $ilDB = $DIC['ilDB'];
59
60 $q = 'SELECT data FROM usr_session WHERE session_id = ' .
61 $ilDB->quote($a_session_id, 'text');
62 $set = $ilDB->query($q);
63 $rec = $ilDB->fetchAssoc($set);
64 if (!is_array($rec)) {
65 return '';
66 }
67
68 // fix for php #70520
69 return (string) $rec['data'];
70 }
71
77 public static function lookupExpireTime(string $a_session_id): int
78 {
79 global $DIC;
80
81 $ilDB = $DIC['ilDB'];
82
83 $query = 'SELECT expires FROM usr_session WHERE session_id = ' .
84 $ilDB->quote($a_session_id, 'text');
85 $res = $ilDB->query($query);
86 if ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
87 return (int) $row->expires;
88 }
89 return 0;
90 }
91
92 public static function _writeData(string $a_session_id, string $a_data): bool
93 {
94 global $DIC;
95
97 $ilDB = $DIC['ilDB'];
99 $ilClientIniFile = $DIC['ilClientIniFile'];
100
101 if (self::isWebAccessWithoutSessionEnabled()) {
102 // Prevent session data written for web access checker
103 // when no cookie was sent (e.g. for pdf files linking others).
104 // This would result in new session records for each request.
105 return true;
106 }
107
108 if (!$a_session_id) {
109 return true;
110 }
111
112 $now = time();
113
114 // prepare session data
115 $fields = [
116 'user_id' => [ilDBConstants::T_INTEGER, (int) (self::get('_authsession_user_id') ?? 0)],
117 'expires' => [ilDBConstants::T_INTEGER, self::getExpireValue()],
118 'data' => [ilDBConstants::T_CLOB, $a_data],
119 'ctime' => [ilDBConstants::T_INTEGER, $now],
120 'type' => [ilDBConstants::T_INTEGER, (int) (self::get('SessionType') ?? 0)]
121 ];
122 if ($ilClientIniFile->readVariable('session', 'save_ip')) {
123 $fields['remote_addr'] = [ilDBConstants::T_TEXT, $_SERVER['REMOTE_ADDR'] ?? ''];
124 }
125
126 if (self::_exists($a_session_id)) {
127 // note that we do this only when inserting the new record
128 // updating may get us other contexts for the same session, especially ilContextWAC, which we do not want
129 if (class_exists('ilContext') && ilContext::isSessionMainContext()) {
130 $fields['context'] = [ilDBConstants::T_TEXT, ilContext::getType()];
131 }
132 $ilDB->update(
133 'usr_session',
134 $fields,
135 ['session_id' => [ilDBConstants::T_TEXT, $a_session_id]]
136 );
137 } else {
138 $fields['session_id'] = [ilDBConstants::T_TEXT, $a_session_id];
139 $fields['createtime'] = [ilDBConstants::T_INTEGER, $now];
140
141 // note that we do this only when inserting the new record
142 // updating may get us other contexts for the same session, especially ilContextWAC, which we do not want
143 if (class_exists('ilContext')) {
144 $fields['context'] = [ilDBConstants::T_TEXT, ilContext::getType()];
145 }
146
147 $insert_fields = implode(', ', array_keys($fields));
148 $insert_values = implode(
149 ', ',
150 array_map(
151 static fn(string $type, $value): string => $ilDB->quote($value, $type),
152 array_column($fields, 0),
153 array_column($fields, 1)
154 )
155 );
156
157 $update_fields = array_filter(
158 $fields,
159 static fn(string $field): bool => !in_array($field, ['session_id', 'user_id', 'createtime'], true),
160 ARRAY_FILTER_USE_KEY
161 );
162 $update_values = implode(
163 ', ',
164 array_map(
165 static fn(string $field, string $type, $value): string => $field . ' = ' . $ilDB->quote(
166 $value,
167 $type
168 ),
169 array_keys($update_fields),
170 array_column($update_fields, 0),
171 array_column($update_fields, 1)
172 )
173 );
174
175 $ilDB->manipulate(
176 'INSERT INTO usr_session (' . $insert_fields . ') '
177 . 'VALUES (' . $insert_values . ') '
178 . 'ON DUPLICATE KEY UPDATE ' . $update_values
179 );
180
181 // check type against session control
182 $type = (int) $fields['type'][1];
183 if (in_array($type, ilSessionControl::$session_types_controlled, true)) {
184 ilSessionStatistics::createRawEntry(
185 $fields['session_id'][1],
186 $type,
187 $fields['createtime'][1],
188 $fields['user_id'][1]
189 );
190 }
191 }
192
193 if (!$DIC->cron()->manager()->isJobActive('auth_destroy_expired_sessions')) {
194 $r = new \Random\Randomizer();
195 if ($r->getInt(0, 50) === 2) {
196 // get time _before_ destroying expired sessions
199 }
200 }
201
202 return true;
203 }
204
205 public static function _exists(string $a_session_id): bool
206 {
207 if (!$a_session_id) {
208 return false;
209 }
210 global $DIC;
211
212 $ilDB = $DIC['ilDB'];
213
214 $q = 'SELECT 1 FROM usr_session WHERE session_id = ' . $ilDB->quote($a_session_id, 'text');
215 $set = $ilDB->query($q);
216
217 return $ilDB->numRows($set) > 0;
218 }
219
227 public static function _destroy($a_session_id, ?int $a_closing_context = null, $a_expired_at = null): bool
228 {
229 global $DIC;
230
231 if (!$a_closing_context) {
232 $a_closing_context = self::$closing_context;
233 }
234
235 ilSessionStatistics::closeRawEntry($a_session_id, $a_closing_context, $a_expired_at);
236
237 $deletion_queries = [];
238 if (is_array($a_session_id)) {
239 // array: id => timestamp - so we get rid of timestamps
240 if ($a_expired_at) {
241 $a_session_id = array_keys($a_session_id);
242 }
243
244 $chunk_size = 500;
245 $batches = array_chunk($a_session_id, $chunk_size);
246 foreach ($batches as $batch) {
247 $deletion_queries[] = 'DELETE FROM usr_session WHERE ' .
248 $DIC->database()->in('session_id', $batch, false, ilDBConstants::T_TEXT);
249 }
250 } else {
251 $deletion_queries[] = 'DELETE FROM usr_session WHERE session_id = ' .
252 $DIC->database()->quote($a_session_id, ilDBConstants::T_TEXT);
253 }
254
256
257 foreach ($deletion_queries as $deletion_query) {
258 $DIC->database()->manipulate($deletion_query);
259 }
260
261 if (ilContext::usesHTTP()) {
262 try {
263 // only delete session cookie if it is set in the current request
264 if ($DIC->http()->wrapper()->cookie()->has(session_name()) &&
265 $DIC->http()->wrapper()->cookie()->retrieve(
266 session_name(),
267 $DIC->refinery()->kindlyTo()->string()
268 ) === $a_session_id) {
269 $cookieJar = $DIC->http()->cookieJar()->without(session_name());
270 $cookieJar->renderIntoResponseHeader($DIC->http()->response());
271 }
272 } catch (Throwable) {
273 // ignore
274 // this is needed for "header already" sent errors when the random cleanup of expired sessions is triggered
275 }
276 }
277
278 return true;
279 }
280
286 public static function _destroyByUserId(int $a_user_id): bool
287 {
288 global $DIC;
289
290 $ilDB = $DIC['ilDB'];
291
292 $q = 'DELETE FROM usr_session WHERE user_id = ' .
293 $ilDB->quote($a_user_id, 'integer');
294 $ilDB->manipulate($q);
295
296 return true;
297 }
298
303 public static function _destroyExpiredSessions(): int
304 {
305 global $DIC;
306
307 $ilDB = $DIC['ilDB'];
308
309 $q = 'SELECT session_id, expires FROM usr_session WHERE expires < ' . $ilDB->quote(time(), ilDBConstants::T_INTEGER);
310 $res = $ilDB->query($q);
311 $ids = [];
312 while ($row = $ilDB->fetchAssoc($res)) {
313 $ids[$row['session_id']] = (int) $row['expires'];
314 }
315 if ($ids !== []) {
316 self::_destroy($ids, self::SESSION_CLOSE_EXPIRE, true);
317 }
318
319 return count($ids);
320 }
321
328 public static function _duplicate(string $a_session_id): string
329 {
330 global $DIC;
331
332 $ilDB = $DIC['ilDB'];
333
334 // Create new session id
335 $new_session = $a_session_id;
336 do {
337 $new_session = md5($new_session);
338 $q = 'SELECT * FROM usr_session WHERE ' .
339 'session_id = ' . $ilDB->quote($new_session, 'text');
340 $res = $ilDB->query($q);
341 } while ($ilDB->fetchAssoc($res));
342
343 $query = 'SELECT * FROM usr_session ' .
344 'WHERE session_id = ' . $ilDB->quote($a_session_id, 'text');
345 $res = $ilDB->query($query);
346
347 if ($row = $ilDB->fetchObject($res)) {
348 self::_writeData($new_session, $row->data);
349 return $new_session;
350 }
351 //TODO check if throwing an excpetion might be a better choice
352 return '';
353 }
354
358 public static function getExpireValue(): int
359 {
360 return time() + self::getIdleValue();
361 }
362
366 public static function getIdleValue(): int
367 {
368 global $DIC;
369
370 $ilClientIniFile = $DIC['ilClientIniFile'];
371
372 return (int) $ilClientIniFile->readVariable('session', 'expire');
373 }
374
378 public static function getSessionExpireValue(): int
379 {
380 return self::getIdleValue();
381 }
382
386 public static function set(string $a_var, $a_val): void
387 {
388 $_SESSION[$a_var] = $a_val;
389 }
390
394 public static function get(string $a_var)
395 {
396 return $_SESSION[$a_var] ?? null;
397 }
398
399 public static function has($a_var): bool
400 {
401 return isset($_SESSION[$a_var]);
402 }
403
404 public static function clear(string $a_var): void
405 {
406 if (isset($_SESSION[$a_var])) {
407 unset($_SESSION[$a_var]);
408 }
409 }
410
411 public static function dumpToString(): string
412 {
413 return print_r($_SESSION, true);
414 }
415
419 public static function setClosingContext(int $a_context): void
420 {
421 self::$closing_context = $a_context;
422 }
423
427 public static function getClosingContext(): int
428 {
430 }
431
432 public static function isWebAccessWithoutSessionEnabled(): bool
433 {
435 }
436
438 {
439 self::$enable_web_access_without_session = $enable_web_access_without_session;
440 }
441}
static isSessionMainContext()
Context that are not only temporary in a session (e.g.
Definition: ilContext.php:183
static getType()
Get context type.
Definition: ilContext.php:165
static usesHTTP()
Uses HTTP aka browser.
Definition: ilContext.php:117
const FETCHMODE_OBJECT
static array $session_types_controlled
static destroySession($a_session_id)
Destroy session(s).
static _destroyByUserId(int $a_user_id)
Destroy session.
static _exists(string $a_session_id)
const int SESSION_CLOSE_SIMUL
static dumpToString()
static _duplicate(string $a_session_id)
Duplicate session.
const int SESSION_CLOSE_LOGIN
static int $closing_context
static getClosingContext()
get closing context (for statistics)
static isWebAccessWithoutSessionEnabled()
static getIdleValue()
Returns the idle time in seconds.
static clear(string $a_var)
static _destroyExpiredSessions()
Destroy expired sessions.
static bool $enable_web_access_without_session
const int SESSION_CLOSE_PUBLIC
static setClosingContext(int $a_context)
set closing context (for statistics)
const int SESSION_CLOSE_EXPIRE
static _destroy($a_session_id, ?int $a_closing_context=null, $a_expired_at=null)
Destroy session.
const int SESSION_CLOSE_TIME
const int SESSION_CLOSE_INACTIVE
const int SESSION_CLOSE_IP
static getExpireValue()
Returns the expiration timestamp in seconds.
static getSessionExpireValue()
Returns the session expiration value.
static enableWebAccessWithoutSession(bool $enable_web_access_without_session)
const int SESSION_CLOSE_USER
static _getData(string $a_session_id)
Get session data from table.
static has($a_var)
static lookupExpireTime(string $a_session_id)
Lookup expire time for a specific session.
$res
Definition: ltiservices.php:69
get(string $class_name)
$_SERVER['HTTP_HOST']
Definition: raiseError.php:26
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:25