SessionBackend::renew()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 0
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
1
<?php
2
/**
3
 * MediaWiki session backend
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup Session
22
 */
23
24
namespace MediaWiki\Session;
25
26
use CachedBagOStuff;
27
use Psr\Log\LoggerInterface;
28
use User;
29
use WebRequest;
30
31
/**
32
 * This is the actual workhorse for Session.
33
 *
34
 * Most code does not need to use this class, you want \MediaWiki\Session\Session.
35
 * The exceptions are SessionProviders and SessionMetadata hook functions,
36
 * which get an instance of this class rather than Session.
37
 *
38
 * The reasons for this split are:
39
 * 1. A session can be attached to multiple requests, but we want the Session
40
 *    object to have some features that correspond to just one of those
41
 *    requests.
42
 * 2. We want reasonable garbage collection behavior, but we also want the
43
 *    SessionManager to hold a reference to every active session so it can be
44
 *    saved when the request ends.
45
 *
46
 * @ingroup Session
47
 * @since 1.27
48
 */
49
final class SessionBackend {
50
	/** @var SessionId */
51
	private $id;
52
53
	private $persist = false;
54
	private $remember = false;
55
	private $forceHTTPS = false;
56
57
	/** @var array|null */
58
	private $data = null;
59
60
	private $forcePersist = false;
61
	private $metaDirty = false;
62
	private $dataDirty = false;
63
64
	/** @var string Used to detect subarray modifications */
65
	private $dataHash = null;
66
67
	/** @var CachedBagOStuff */
68
	private $store;
69
70
	/** @var LoggerInterface */
71
	private $logger;
72
73
	/** @var int */
74
	private $lifetime;
75
76
	/** @var User */
77
	private $user;
78
79
	private $curIndex = 0;
80
81
	/** @var WebRequest[] Session requests */
82
	private $requests = [];
83
84
	/** @var SessionProvider provider */
85
	private $provider;
86
87
	/** @var array|null provider-specified metadata */
88
	private $providerMetadata = null;
89
90
	private $expires = 0;
91
	private $loggedOut = 0;
92
	private $delaySave = 0;
93
94
	private $usePhpSessionHandling = true;
95
	private $checkPHPSessionRecursionGuard = false;
96
97
	private $shutdown = false;
98
99
	/**
100
	 * @param SessionId $id Session ID object
101
	 * @param SessionInfo $info Session info to populate from
102
	 * @param CachedBagOStuff $store Backend data store
103
	 * @param LoggerInterface $logger
104
	 * @param int $lifetime Session data lifetime in seconds
105
	 */
106
	public function __construct(
107
		SessionId $id, SessionInfo $info, CachedBagOStuff $store, LoggerInterface $logger, $lifetime
108
	) {
109
		$phpSessionHandling = \RequestContext::getMain()->getConfig()->get( 'PHPSessionHandling' );
110
		$this->usePhpSessionHandling = $phpSessionHandling !== 'disable';
111
112
		if ( $info->getUserInfo() && !$info->getUserInfo()->isVerified() ) {
113
			throw new \InvalidArgumentException(
114
				"Refusing to create session for unverified user {$info->getUserInfo()}"
115
			);
116
		}
117
		if ( $info->getProvider() === null ) {
118
			throw new \InvalidArgumentException( 'Cannot create session without a provider' );
119
		}
120
		if ( $info->getId() !== $id->getId() ) {
121
			throw new \InvalidArgumentException( 'SessionId and SessionInfo don\'t match' );
122
		}
123
124
		$this->id = $id;
125
		$this->user = $info->getUserInfo() ? $info->getUserInfo()->getUser() : new User;
126
		$this->store = $store;
127
		$this->logger = $logger;
128
		$this->lifetime = $lifetime;
129
		$this->provider = $info->getProvider();
130
		$this->persist = $info->wasPersisted();
131
		$this->remember = $info->wasRemembered();
132
		$this->forceHTTPS = $info->forceHTTPS();
133
		$this->providerMetadata = $info->getProviderMetadata();
134
135
		$blob = $store->get( wfMemcKey( 'MWSession', (string)$this->id ) );
136
		if ( !is_array( $blob ) ||
137
			!isset( $blob['metadata'] ) || !is_array( $blob['metadata'] ) ||
138
			!isset( $blob['data'] ) || !is_array( $blob['data'] )
139
		) {
140
			$this->data = [];
141
			$this->dataDirty = true;
142
			$this->metaDirty = true;
143
			$this->logger->debug(
144
				'SessionBackend "{session}" is unsaved, marking dirty in constructor',
145
				[
146
					'session' => $this->id,
147
			] );
148
		} else {
149
			$this->data = $blob['data'];
150
			if ( isset( $blob['metadata']['loggedOut'] ) ) {
151
				$this->loggedOut = (int)$blob['metadata']['loggedOut'];
152
			}
153
			if ( isset( $blob['metadata']['expires'] ) ) {
154
				$this->expires = (int)$blob['metadata']['expires'];
155
			} else {
156
				$this->metaDirty = true;
157
				$this->logger->debug(
158
					'SessionBackend "{session}" metadata dirty due to missing expiration timestamp',
159
				[
160
					'session' => $this->id,
161
				] );
162
			}
163
		}
164
		$this->dataHash = md5( serialize( $this->data ) );
165
	}
166
167
	/**
168
	 * Return a new Session for this backend
169
	 * @param WebRequest $request
170
	 * @return Session
171
	 */
172
	public function getSession( WebRequest $request ) {
173
		$index = ++$this->curIndex;
174
		$this->requests[$index] = $request;
175
		$session = new Session( $this, $index, $this->logger );
176
		return $session;
177
	}
178
179
	/**
180
	 * Deregister a Session
181
	 * @private For use by \MediaWiki\Session\Session::__destruct() only
182
	 * @param int $index
183
	 */
184
	public function deregisterSession( $index ) {
185
		unset( $this->requests[$index] );
186
		if ( !$this->shutdown && !count( $this->requests ) ) {
187
			$this->save( true );
188
			$this->provider->getManager()->deregisterSessionBackend( $this );
189
		}
190
	}
191
192
	/**
193
	 * Shut down a session
194
	 * @private For use by \MediaWiki\Session\SessionManager::shutdown() only
195
	 */
196
	public function shutdown() {
197
		$this->save( true );
198
		$this->shutdown = true;
199
	}
200
201
	/**
202
	 * Returns the session ID.
203
	 * @return string
204
	 */
205
	public function getId() {
206
		return (string)$this->id;
207
	}
208
209
	/**
210
	 * Fetch the SessionId object
211
	 * @private For internal use by WebRequest
212
	 * @return SessionId
213
	 */
214
	public function getSessionId() {
215
		return $this->id;
216
	}
217
218
	/**
219
	 * Changes the session ID
220
	 * @return string New ID (might be the same as the old)
221
	 */
222
	public function resetId() {
223
		if ( $this->provider->persistsSessionId() ) {
224
			$oldId = (string)$this->id;
225
			$restart = $this->usePhpSessionHandling && $oldId === session_id() &&
226
				PHPSessionHandler::isEnabled();
227
228
			if ( $restart ) {
229
				// If this session is the one behind PHP's $_SESSION, we need
230
				// to close then reopen it.
231
				session_write_close();
232
			}
233
234
			$this->provider->getManager()->changeBackendId( $this );
235
			$this->provider->sessionIdWasReset( $this, $oldId );
236
			$this->metaDirty = true;
237
			$this->logger->debug(
238
				'SessionBackend "{session}" metadata dirty due to ID reset (formerly "{oldId}")',
239
				[
240
					'session' => $this->id,
241
					'oldId' => $oldId,
242
			] );
243
244
			if ( $restart ) {
245
				session_id( (string)$this->id );
246
				\MediaWiki\quietCall( 'session_start' );
247
			}
248
249
			$this->autosave();
250
251
			// Delete the data for the old session ID now
252
			$this->store->delete( wfMemcKey( 'MWSession', $oldId ) );
253
		}
254
	}
255
256
	/**
257
	 * Fetch the SessionProvider for this session
258
	 * @return SessionProviderInterface
259
	 */
260
	public function getProvider() {
261
		return $this->provider;
262
	}
263
264
	/**
265
	 * Indicate whether this session is persisted across requests
266
	 *
267
	 * For example, if cookies are set.
268
	 *
269
	 * @return bool
270
	 */
271
	public function isPersistent() {
272
		return $this->persist;
273
	}
274
275
	/**
276
	 * Make this session persisted across requests
277
	 *
278
	 * If the session is already persistent, equivalent to calling
279
	 * $this->renew().
280
	 */
281
	public function persist() {
282
		if ( !$this->persist ) {
283
			$this->persist = true;
284
			$this->forcePersist = true;
285
			$this->metaDirty = true;
286
			$this->logger->debug(
287
				'SessionBackend "{session}" force-persist due to persist()',
288
				[
289
					'session' => $this->id,
290
			] );
291
			$this->autosave();
292
		} else {
293
			$this->renew();
294
		}
295
	}
296
297
	/**
298
	 * Make this session not persisted across requests
299
	 */
300
	public function unpersist() {
301
		if ( $this->persist ) {
302
			// Close the PHP session, if we're the one that's open
303 View Code Duplication
			if ( $this->usePhpSessionHandling && PHPSessionHandler::isEnabled() &&
304
				session_id() === (string)$this->id
305
			) {
306
				$this->logger->debug(
307
					'SessionBackend "{session}" Closing PHP session for unpersist',
308
					[ 'session' => $this->id ]
309
				);
310
				session_write_close();
311
				session_id( '' );
312
			}
313
314
			$this->persist = false;
315
			$this->forcePersist = true;
316
			$this->metaDirty = true;
317
318
			// Delete the session data, so the local cache-only write in
319
			// self::save() doesn't get things out of sync with the backend.
320
			$this->store->delete( wfMemcKey( 'MWSession', (string)$this->id ) );
321
322
			$this->autosave();
323
		}
324
	}
325
326
	/**
327
	 * Indicate whether the user should be remembered independently of the
328
	 * session ID.
329
	 * @return bool
330
	 */
331
	public function shouldRememberUser() {
332
		return $this->remember;
333
	}
334
335
	/**
336
	 * Set whether the user should be remembered independently of the session
337
	 * ID.
338
	 * @param bool $remember
339
	 */
340 View Code Duplication
	public function setRememberUser( $remember ) {
341
		if ( $this->remember !== (bool)$remember ) {
342
			$this->remember = (bool)$remember;
343
			$this->metaDirty = true;
344
			$this->logger->debug(
345
				'SessionBackend "{session}" metadata dirty due to remember-user change',
346
				[
347
					'session' => $this->id,
348
			] );
349
			$this->autosave();
350
		}
351
	}
352
353
	/**
354
	 * Returns the request associated with a Session
355
	 * @param int $index Session index
356
	 * @return WebRequest
357
	 */
358
	public function getRequest( $index ) {
359
		if ( !isset( $this->requests[$index] ) ) {
360
			throw new \InvalidArgumentException( 'Invalid session index' );
361
		}
362
		return $this->requests[$index];
363
	}
364
365
	/**
366
	 * Returns the authenticated user for this session
367
	 * @return User
368
	 */
369
	public function getUser() {
370
		return $this->user;
371
	}
372
373
	/**
374
	 * Fetch the rights allowed the user when this session is active.
375
	 * @return null|string[] Allowed user rights, or null to allow all.
376
	 */
377
	public function getAllowedUserRights() {
378
		return $this->provider->getAllowedUserRights( $this );
379
	}
380
381
	/**
382
	 * Indicate whether the session user info can be changed
383
	 * @return bool
384
	 */
385
	public function canSetUser() {
386
		return $this->provider->canChangeUser();
387
	}
388
389
	/**
390
	 * Set a new user for this session
391
	 * @note This should only be called when the user has been authenticated via a login process
392
	 * @param User $user User to set on the session.
393
	 *   This may become a "UserValue" in the future, or User may be refactored
394
	 *   into such.
395
	 */
396
	public function setUser( $user ) {
397
		if ( !$this->canSetUser() ) {
398
			throw new \BadMethodCallException(
399
				'Cannot set user on this session; check $session->canSetUser() first'
400
			);
401
		}
402
403
		$this->user = $user;
404
		$this->metaDirty = true;
405
		$this->logger->debug(
406
			'SessionBackend "{session}" metadata dirty due to user change',
407
			[
408
				'session' => $this->id,
409
		] );
410
		$this->autosave();
411
	}
412
413
	/**
414
	 * Get a suggested username for the login form
415
	 * @param int $index Session index
416
	 * @return string|null
417
	 */
418
	public function suggestLoginUsername( $index ) {
419
		if ( !isset( $this->requests[$index] ) ) {
420
			throw new \InvalidArgumentException( 'Invalid session index' );
421
		}
422
		return $this->provider->suggestLoginUsername( $this->requests[$index] );
423
	}
424
425
	/**
426
	 * Whether HTTPS should be forced
427
	 * @return bool
428
	 */
429
	public function shouldForceHTTPS() {
430
		return $this->forceHTTPS;
431
	}
432
433
	/**
434
	 * Set whether HTTPS should be forced
435
	 * @param bool $force
436
	 */
437 View Code Duplication
	public function setForceHTTPS( $force ) {
438
		if ( $this->forceHTTPS !== (bool)$force ) {
439
			$this->forceHTTPS = (bool)$force;
440
			$this->metaDirty = true;
441
			$this->logger->debug(
442
				'SessionBackend "{session}" metadata dirty due to force-HTTPS change',
443
				[
444
					'session' => $this->id,
445
			] );
446
			$this->autosave();
447
		}
448
	}
449
450
	/**
451
	 * Fetch the "logged out" timestamp
452
	 * @return int
453
	 */
454
	public function getLoggedOutTimestamp() {
455
		return $this->loggedOut;
456
	}
457
458
	/**
459
	 * Set the "logged out" timestamp
460
	 * @param int $ts
461
	 */
462
	public function setLoggedOutTimestamp( $ts = null ) {
463
		$ts = (int)$ts;
464
		if ( $this->loggedOut !== $ts ) {
465
			$this->loggedOut = $ts;
466
			$this->metaDirty = true;
467
			$this->logger->debug(
468
				'SessionBackend "{session}" metadata dirty due to logged-out-timestamp change',
469
				[
470
					'session' => $this->id,
471
			] );
472
			$this->autosave();
473
		}
474
	}
475
476
	/**
477
	 * Fetch provider metadata
478
	 * @protected For use by SessionProvider subclasses only
479
	 * @return array|null
480
	 */
481
	public function getProviderMetadata() {
482
		return $this->providerMetadata;
483
	}
484
485
	/**
486
	 * Set provider metadata
487
	 * @protected For use by SessionProvider subclasses only
488
	 * @param array|null $metadata
489
	 */
490
	public function setProviderMetadata( $metadata ) {
491
		if ( $metadata !== null && !is_array( $metadata ) ) {
492
			throw new \InvalidArgumentException( '$metadata must be an array or null' );
493
		}
494
		if ( $this->providerMetadata !== $metadata ) {
495
			$this->providerMetadata = $metadata;
496
			$this->metaDirty = true;
497
			$this->logger->debug(
498
				'SessionBackend "{session}" metadata dirty due to provider metadata change',
499
				[
500
					'session' => $this->id,
501
			] );
502
			$this->autosave();
503
		}
504
	}
505
506
	/**
507
	 * Fetch the session data array
508
	 *
509
	 * Note the caller is responsible for calling $this->dirty() if anything in
510
	 * the array is changed.
511
	 *
512
	 * @private For use by \MediaWiki\Session\Session only.
513
	 * @return array
514
	 */
515
	public function &getData() {
516
		return $this->data;
517
	}
518
519
	/**
520
	 * Add data to the session.
521
	 *
522
	 * Overwrites any existing data under the same keys.
523
	 *
524
	 * @param array $newData Key-value pairs to add to the session
525
	 */
526
	public function addData( array $newData ) {
527
		$data = &$this->getData();
528
		foreach ( $newData as $key => $value ) {
529
			if ( !array_key_exists( $key, $data ) || $data[$key] !== $value ) {
530
				$data[$key] = $value;
531
				$this->dataDirty = true;
532
				$this->logger->debug(
533
					'SessionBackend "{session}" data dirty due to addData(): {callers}',
534
					[
535
						'session' => $this->id,
536
						'callers' => wfGetAllCallers( 5 ),
537
				] );
538
			}
539
		}
540
	}
541
542
	/**
543
	 * Mark data as dirty
544
	 * @private For use by \MediaWiki\Session\Session only.
545
	 */
546
	public function dirty() {
547
		$this->dataDirty = true;
548
		$this->logger->debug(
549
			'SessionBackend "{session}" data dirty due to dirty(): {callers}',
550
			[
551
				'session' => $this->id,
552
				'callers' => wfGetAllCallers( 5 ),
553
		] );
554
	}
555
556
	/**
557
	 * Renew the session by resaving everything
558
	 *
559
	 * Resets the TTL in the backend store if the session is near expiring, and
560
	 * re-persists the session to any active WebRequests if persistent.
561
	 */
562
	public function renew() {
563
		if ( time() + $this->lifetime / 2 > $this->expires ) {
564
			$this->metaDirty = true;
565
			$this->logger->debug(
566
				'SessionBackend "{callers}" metadata dirty for renew(): {callers}',
567
				[
568
					'session' => $this->id,
569
					'callers' => wfGetAllCallers( 5 ),
570
			] );
571
			if ( $this->persist ) {
572
				$this->forcePersist = true;
573
				$this->logger->debug(
574
					'SessionBackend "{session}" force-persist for renew(): {callers}',
575
					[
576
						'session' => $this->id,
577
						'callers' => wfGetAllCallers( 5 ),
578
				] );
579
			}
580
		}
581
		$this->autosave();
582
	}
583
584
	/**
585
	 * Delay automatic saving while multiple updates are being made
586
	 *
587
	 * Calls to save() will not be delayed.
588
	 *
589
	 * @return \Wikimedia\ScopedCallback When this goes out of scope, a save will be triggered
590
	 */
591
	public function delaySave() {
592
		$this->delaySave++;
593
		return new \Wikimedia\ScopedCallback( function () {
594
			if ( --$this->delaySave <= 0 ) {
595
				$this->delaySave = 0;
596
				$this->save();
597
			}
598
		} );
599
	}
600
601
	/**
602
	 * Save the session, unless delayed
603
	 * @see SessionBackend::save()
604
	 */
605
	private function autosave() {
606
		if ( $this->delaySave <= 0 ) {
607
			$this->save();
608
		}
609
	}
610
611
	/**
612
	 * Save the session
613
	 *
614
	 * Update both the backend data and the associated WebRequest(s) to
615
	 * reflect the state of the the SessionBackend. This might include
616
	 * persisting or unpersisting the session.
617
	 *
618
	 * @param bool $closing Whether the session is being closed
619
	 */
620
	public function save( $closing = false ) {
621
		$anon = $this->user->isAnon();
622
623
		if ( !$anon && $this->provider->getManager()->isUserSessionPrevented( $this->user->getName() ) ) {
624
			$this->logger->debug(
625
				'SessionBackend "{session}" not saving, user {user} was ' .
626
				'passed to SessionManager::preventSessionsForUser',
627
				[
628
					'session' => $this->id,
629
					'user' => $this->user,
630
			] );
631
			return;
632
		}
633
634
		// Ensure the user has a token
635
		// @codeCoverageIgnoreStart
636
		if ( !$anon && !$this->user->getToken( false ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->user->getToken(false) of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
637
			$this->logger->debug(
638
				'SessionBackend "{session}" creating token for user {user} on save',
639
				[
640
					'session' => $this->id,
641
					'user' => $this->user,
642
			] );
643
			$this->user->setToken();
644
			if ( !wfReadOnly() ) {
645
				// Promise that the token set here will be valid; save it at end of request
646
				$user = $this->user;
647
				\DeferredUpdates::addCallableUpdate( function () use ( $user ) {
648
					$user->saveSettings();
649
				} );
650
			}
651
			$this->metaDirty = true;
652
		}
653
		// @codeCoverageIgnoreEnd
654
655
		if ( !$this->metaDirty && !$this->dataDirty &&
656
			$this->dataHash !== md5( serialize( $this->data ) )
657
		) {
658
			$this->logger->debug(
659
				'SessionBackend "{session}" data dirty due to hash mismatch, {expected} !== {got}',
660
				[
661
					'session' => $this->id,
662
					'expected' => $this->dataHash,
663
					'got' => md5( serialize( $this->data ) ),
664
			] );
665
			$this->dataDirty = true;
666
		}
667
668
		if ( !$this->metaDirty && !$this->dataDirty && !$this->forcePersist ) {
669
			return;
670
		}
671
672
		$this->logger->debug(
673
			'SessionBackend "{session}" save: dataDirty={dataDirty} ' .
674
			'metaDirty={metaDirty} forcePersist={forcePersist}',
675
			[
676
				'session' => $this->id,
677
				'dataDirty' => (int)$this->dataDirty,
678
				'metaDirty' => (int)$this->metaDirty,
679
				'forcePersist' => (int)$this->forcePersist,
680
		] );
681
682
		// Persist or unpersist to the provider, if necessary
683
		if ( $this->metaDirty || $this->forcePersist ) {
684
			if ( $this->persist ) {
685
				foreach ( $this->requests as $request ) {
686
					$request->setSessionId( $this->getSessionId() );
687
					$this->provider->persistSession( $this, $request );
688
				}
689
				if ( !$closing ) {
690
					$this->checkPHPSession();
691
				}
692
			} else {
693
				foreach ( $this->requests as $request ) {
694
					if ( $request->getSessionId() === $this->id ) {
695
						$this->provider->unpersistSession( $request );
696
					}
697
				}
698
			}
699
		}
700
701
		$this->forcePersist = false;
702
703
		if ( !$this->metaDirty && !$this->dataDirty ) {
704
			return;
705
		}
706
707
		// Save session data to store, if necessary
708
		$metadata = $origMetadata = [
709
			'provider' => (string)$this->provider,
710
			'providerMetadata' => $this->providerMetadata,
711
			'userId' => $anon ? 0 : $this->user->getId(),
712
			'userName' => User::isValidUserName( $this->user->getName() ) ? $this->user->getName() : null,
713
			'userToken' => $anon ? null : $this->user->getToken(),
714
			'remember' => !$anon && $this->remember,
715
			'forceHTTPS' => $this->forceHTTPS,
716
			'expires' => time() + $this->lifetime,
717
			'loggedOut' => $this->loggedOut,
718
			'persisted' => $this->persist,
719
		];
720
721
		\Hooks::run( 'SessionMetadata', [ $this, &$metadata, $this->requests ] );
722
723
		foreach ( $origMetadata as $k => $v ) {
724
			if ( $metadata[$k] !== $v ) {
725
				throw new \UnexpectedValueException( "SessionMetadata hook changed metadata key \"$k\"" );
726
			}
727
		}
728
729
		$flags = $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY;
730
		$flags |= CachedBagOStuff::WRITE_SYNC; // write to all datacenters
731
		$this->store->set(
732
			wfMemcKey( 'MWSession', (string)$this->id ),
733
			[
734
				'data' => $this->data,
735
				'metadata' => $metadata,
736
			],
737
			$metadata['expires'],
738
			$flags
739
		);
740
741
		$this->metaDirty = false;
742
		$this->dataDirty = false;
743
		$this->dataHash = md5( serialize( $this->data ) );
744
		$this->expires = $metadata['expires'];
745
	}
746
747
	/**
748
	 * For backwards compatibility, open the PHP session when the global
749
	 * session is persisted
750
	 */
751
	private function checkPHPSession() {
752
		if ( !$this->checkPHPSessionRecursionGuard ) {
753
			$this->checkPHPSessionRecursionGuard = true;
754
			$reset = new \Wikimedia\ScopedCallback( function () {
0 ignored issues
show
Unused Code introduced by
$reset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
755
				$this->checkPHPSessionRecursionGuard = false;
756
			} );
757
758 View Code Duplication
			if ( $this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() &&
759
				SessionManager::getGlobalSession()->getId() === (string)$this->id
760
			) {
761
				$this->logger->debug(
762
					'SessionBackend "{session}" Taking over PHP session',
763
					[
764
						'session' => $this->id,
765
				] );
766
				session_id( (string)$this->id );
767
				\MediaWiki\quietCall( 'session_start' );
768
			}
769
		}
770
	}
771
772
}
773