ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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)) {
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 $ilDB = $DIC['ilDB'];
232
233 if (!$a_closing_context) {
234 $a_closing_context = self::$closing_context;
235 }
236
237 ilSessionStatistics::closeRawEntry($a_session_id, $a_closing_context, $a_expired_at);
238
239 if (is_array($a_session_id)) {
240 // array: id => timestamp - so we get rid of timestamps
241 if ($a_expired_at) {
242 $a_session_id = array_keys($a_session_id);
243 }
244 $q = 'DELETE FROM usr_session WHERE ' .
245 $ilDB->in('session_id', $a_session_id, false, 'text');
246 } else {
247 $q = 'DELETE FROM usr_session WHERE session_id = ' .
248 $ilDB->quote($a_session_id, 'text');
249 }
250
252
253 $ilDB->manipulate($q);
254
255 if (ilContext::usesHTTP()) {
256 try {
257 // only delete session cookie if it is set in the current request
258 if ($DIC->http()->wrapper()->cookie()->has(session_name()) &&
259 $DIC->http()->wrapper()->cookie()->retrieve(
260 session_name(),
261 $DIC->refinery()->kindlyTo()->string()
262 ) === $a_session_id) {
263 $cookieJar = $DIC->http()->cookieJar()->without(session_name());
264 $cookieJar->renderIntoResponseHeader($DIC->http()->response());
265 }
266 } catch (Throwable) {
267 // ignore
268 // this is needed for "header already" sent errors when the random cleanup of expired sessions is triggered
269 }
270 }
271
272 return true;
273 }
274
280 public static function _destroyByUserId(int $a_user_id): bool
281 {
282 global $DIC;
283
284 $ilDB = $DIC['ilDB'];
285
286 $q = 'DELETE FROM usr_session WHERE user_id = ' .
287 $ilDB->quote($a_user_id, 'integer');
288 $ilDB->manipulate($q);
289
290 return true;
291 }
292
297 public static function _destroyExpiredSessions(): int
298 {
299 global $DIC;
300
301 $ilDB = $DIC['ilDB'];
302
303 $q = 'SELECT session_id, expires FROM usr_session WHERE expires < ' . $ilDB->quote(time(), ilDBConstants::T_INTEGER);
304 $res = $ilDB->query($q);
305 $ids = [];
306 while ($row = $ilDB->fetchAssoc($res)) {
307 $ids[$row['session_id']] = (int) $row['expires'];
308 }
309 if ($ids !== []) {
310 self::_destroy($ids, self::SESSION_CLOSE_EXPIRE, true);
311 }
312
313 return count($ids);
314 }
315
322 public static function _duplicate(string $a_session_id): string
323 {
324 global $DIC;
325
326 $ilDB = $DIC['ilDB'];
327
328 // Create new session id
329 $new_session = $a_session_id;
330 do {
331 $new_session = md5($new_session);
332 $q = 'SELECT * FROM usr_session WHERE ' .
333 'session_id = ' . $ilDB->quote($new_session, 'text');
334 $res = $ilDB->query($q);
335 } while ($ilDB->fetchAssoc($res));
336
337 $query = 'SELECT * FROM usr_session ' .
338 'WHERE session_id = ' . $ilDB->quote($a_session_id, 'text');
339 $res = $ilDB->query($query);
340
341 if ($row = $ilDB->fetchObject($res)) {
342 self::_writeData($new_session, $row->data);
343 return $new_session;
344 }
345 //TODO check if throwing an excpetion might be a better choice
346 return '';
347 }
348
352 public static function getExpireValue(): int
353 {
354 return time() + self::getIdleValue();
355 }
356
360 public static function getIdleValue(): int
361 {
362 global $DIC;
363
364 $ilClientIniFile = $DIC['ilClientIniFile'];
365
366 return (int) $ilClientIniFile->readVariable('session', 'expire');
367 }
368
372 public static function getSessionExpireValue(): int
373 {
374 return self::getIdleValue();
375 }
376
380 public static function set(string $a_var, $a_val): void
381 {
382 $_SESSION[$a_var] = $a_val;
383 }
384
388 public static function get(string $a_var)
389 {
390 return $_SESSION[$a_var] ?? null;
391 }
392
393 public static function has($a_var): bool
394 {
395 return isset($_SESSION[$a_var]);
396 }
397
398 public static function clear(string $a_var): void
399 {
400 if (isset($_SESSION[$a_var])) {
401 unset($_SESSION[$a_var]);
402 }
403 }
404
405 public static function dumpToString(): string
406 {
407 return print_r($_SESSION, true);
408 }
409
413 public static function setClosingContext(int $a_context): void
414 {
415 self::$closing_context = $a_context;
416 }
417
421 public static function getClosingContext(): int
422 {
424 }
425
426 public static function isWebAccessWithoutSessionEnabled(): bool
427 {
429 }
430
432 {
433 self::$enable_web_access_without_session = $enable_web_access_without_session;
434 }
435}
static isSessionMainContext()
Context that are not only temporary in a session (e.g.
static getType()
Get context type.
static usesHTTP()
Uses HTTP aka browser.
static array $session_types_controlled
static destroySession($a_session_id)
Destroy session(s).
static closeRawEntry($a_session_id, ?int $a_context=null, $a_expired_at=null)
Close raw data entry.
static aggretateRaw(int $a_now)
Aggregate raw session data (older than given time)
static createRawEntry(string $a_session_id, int $a_session_type, int $a_timestamp, int $a_user_id)
Create raw data entry.
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:23