29use Psr\Log\LoggerInterface;
32use Wikimedia\AtEase\AtEase;
136 $phpSessionHandling = \RequestContext::getMain()->getConfig()->get(
'PHPSessionHandling' );
137 $this->usePhpSessionHandling = $phpSessionHandling !==
'disable';
140 throw new \InvalidArgumentException(
141 "Refusing to create session for unverified user {$info->getUserInfo()}"
145 throw new \InvalidArgumentException(
'Cannot create session without a provider' );
148 throw new \InvalidArgumentException(
'SessionId and SessionInfo don\'t match' );
155 $this->hookRunner =
new HookRunner( $hookContainer );
164 if ( !is_array(
$blob ) ||
165 !isset(
$blob[
'metadata'] ) || !is_array(
$blob[
'metadata'] ) ||
166 !isset(
$blob[
'data'] ) || !is_array(
$blob[
'data'] )
169 $this->dataDirty =
true;
170 $this->metaDirty =
true;
171 $this->logger->debug(
172 'SessionBackend "{session}" is unsaved, marking dirty in constructor',
174 'session' => $this->id->__toString(),
177 $this->data =
$blob[
'data'];
178 if ( isset(
$blob[
'metadata'][
'loggedOut'] ) ) {
179 $this->loggedOut = (int)
$blob[
'metadata'][
'loggedOut'];
181 if ( isset(
$blob[
'metadata'][
'expires'] ) ) {
182 $this->expires = (int)
$blob[
'metadata'][
'expires'];
184 $this->metaDirty =
true;
185 $this->logger->debug(
186 'SessionBackend "{session}" metadata dirty due to missing expiration timestamp',
188 'session' => $this->id->__toString(),
192 $this->dataHash = md5(
serialize( $this->data ) );
202 $this->requests[$index] = $request;
203 $session =
new Session( $this, $index, $this->logger );
213 unset( $this->requests[$index] );
214 if ( !$this->
shutdown && !count( $this->requests ) ) {
216 $this->provider->getManager()->deregisterSessionBackend( $this );
251 if ( $this->provider->persistsSessionId() ) {
252 $oldId = (string)$this->
id;
253 $restart = $this->usePhpSessionHandling && $oldId === session_id() &&
259 session_write_close();
262 $this->provider->getManager()->changeBackendId( $this );
263 $this->provider->sessionIdWasReset( $this, $oldId );
264 $this->metaDirty =
true;
265 $this->logger->debug(
266 'SessionBackend "{session}" metadata dirty due to ID reset (formerly "{oldId}")',
268 'session' => $this->id->__toString(),
273 session_id( (
string)$this->
id );
274 AtEase::quietCall(
'session_start' );
280 $this->store->delete( $this->store->makeKey(
'MWSession', $oldId ) );
314 $this->forcePersist =
true;
315 $this->metaDirty =
true;
316 $this->logger->debug(
317 'SessionBackend "{session}" force-persist due to persist()',
319 'session' => $this->id->__toString(),
334 session_id() === (
string)$this->
id
336 $this->logger->debug(
337 'SessionBackend "{session}" Closing PHP session for unpersist',
338 [
'session' => $this->id->__toString() ]
340 session_write_close();
345 $this->forcePersist =
true;
346 $this->metaDirty =
true;
350 $this->store->delete( $this->store->makeKey(
'MWSession', (
string)$this->id ) );
371 if ( $this->remember !== (
bool)
$remember ) {
373 $this->metaDirty =
true;
374 $this->logger->debug(
375 'SessionBackend "{session}" metadata dirty due to remember-user change',
377 'session' => $this->id->__toString(),
389 if ( !isset( $this->requests[$index] ) ) {
390 throw new \InvalidArgumentException(
'Invalid session index' );
392 return $this->requests[$index];
408 return $this->provider->getAllowedUserRights( $this );
416 return $this->provider->canChangeUser();
428 throw new \BadMethodCallException(
429 'Cannot set user on this session; check $session->canSetUser() first'
434 $this->metaDirty =
true;
435 $this->logger->debug(
436 'SessionBackend "{session}" metadata dirty due to user change',
438 'session' => $this->id->__toString(),
449 if ( !isset( $this->requests[$index] ) ) {
450 throw new \InvalidArgumentException(
'Invalid session index' );
452 return $this->provider->suggestLoginUsername( $this->requests[$index] );
468 if ( $this->forceHTTPS !== (
bool)$force ) {
469 $this->forceHTTPS = (bool)$force;
470 $this->metaDirty =
true;
471 $this->logger->debug(
472 'SessionBackend "{session}" metadata dirty due to force-HTTPS change',
474 'session' => $this->id->__toString(),
494 if ( $this->loggedOut !== $ts ) {
495 $this->loggedOut = $ts;
496 $this->metaDirty =
true;
497 $this->logger->debug(
498 'SessionBackend "{session}" metadata dirty due to logged-out-timestamp change',
500 'session' => $this->id->__toString(),
521 if ( $metadata !==
null && !is_array( $metadata ) ) {
522 throw new \InvalidArgumentException(
'$metadata must be an array or null' );
524 if ( $this->providerMetadata !== $metadata ) {
525 $this->providerMetadata = $metadata;
526 $this->metaDirty =
true;
527 $this->logger->debug(
528 'SessionBackend "{session}" metadata dirty due to provider metadata change',
530 'session' => $this->id->__toString(),
558 foreach ( $newData as $key => $value ) {
559 if ( !array_key_exists( $key,
$data ) ||
$data[$key] !== $value ) {
560 $data[$key] = $value;
561 $this->dataDirty =
true;
562 $this->logger->debug(
563 'SessionBackend "{session}" data dirty due to addData(): {callers}',
565 'session' => $this->id->__toString(),
577 $this->dataDirty =
true;
578 $this->logger->debug(
579 'SessionBackend "{session}" data dirty due to dirty(): {callers}',
581 'session' => $this->id->__toString(),
593 if ( time() + $this->lifetime / 2 > $this->expires ) {
594 $this->metaDirty =
true;
595 $this->logger->debug(
596 'SessionBackend "{callers}" metadata dirty for renew(): {callers}',
598 'session' => $this->id->__toString(),
602 $this->forcePersist =
true;
603 $this->logger->debug(
604 'SessionBackend "{session}" force-persist for renew(): {callers}',
606 'session' => $this->id->__toString(),
623 return new \Wikimedia\ScopedCallback(
function () {
650 public function save( $closing =
false ) {
651 $anon = $this->user->isAnon();
653 if ( !$anon && $this->provider->getManager()->isUserSessionPrevented( $this->user->getName() ) ) {
654 $this->logger->debug(
655 'SessionBackend "{session}" not saving, user {user} was ' .
656 'passed to SessionManager::preventSessionsForUser',
658 'session' => $this->id->__toString(),
659 'user' => $this->user->__toString(),
666 if ( !$anon && !$this->user->getToken(
false ) ) {
667 $this->logger->debug(
668 'SessionBackend "{session}" creating token for user {user} on save',
670 'session' => $this->id->__toString(),
671 'user' => $this->user->__toString(),
673 $this->user->setToken();
677 \DeferredUpdates::addCallableUpdate(
function () use (
$user ) {
681 $this->metaDirty =
true;
685 if ( !$this->metaDirty && !$this->dataDirty &&
686 $this->dataHash !== md5(
serialize( $this->data ) )
688 $this->logger->debug(
689 'SessionBackend "{session}" data dirty due to hash mismatch, {expected} !== {got}',
691 'session' => $this->id->__toString(),
692 'expected' => $this->dataHash,
693 'got' => md5(
serialize( $this->data ) ),
695 $this->dataDirty =
true;
698 if ( !$this->metaDirty && !$this->dataDirty && !$this->forcePersist ) {
702 $this->logger->debug(
703 'SessionBackend "{session}" save: dataDirty={dataDirty} ' .
704 'metaDirty={metaDirty} forcePersist={forcePersist}',
706 'session' => $this->id->__toString(),
707 'dataDirty' => (
int)$this->dataDirty,
708 'metaDirty' => (
int)$this->metaDirty,
709 'forcePersist' => (
int)$this->forcePersist,
713 if ( $this->metaDirty || $this->forcePersist ) {
715 foreach ( $this->requests as $request ) {
717 $this->provider->persistSession( $this, $request );
723 foreach ( $this->requests as $request ) {
724 if ( $request->getSessionId() === $this->id ) {
725 $this->provider->unpersistSession( $request );
731 $this->forcePersist =
false;
733 if ( !$this->metaDirty && !$this->dataDirty ) {
738 $metadata = $origMetadata = [
739 'provider' => (string)$this->provider,
740 'providerMetadata' => $this->providerMetadata,
741 'userId' => $anon ? 0 : $this->user->getId(),
743 'userToken' => $anon ? null : $this->user->getToken(),
751 $this->hookRunner->onSessionMetadata( $this, $metadata, $this->requests );
753 foreach ( $origMetadata as $k => $v ) {
754 if ( $metadata[$k] !== $v ) {
755 throw new \UnexpectedValueException(
"SessionMetadata hook changed metadata key \"$k\"" );
759 $flags = $this->
persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY;
760 $flags |= CachedBagOStuff::WRITE_SYNC;
762 $this->store->makeKey(
'MWSession', (
string)$this->id ),
764 'data' => $this->data,
765 'metadata' => $metadata,
767 $metadata[
'expires'],
771 $this->metaDirty =
false;
772 $this->dataDirty =
false;
773 $this->dataHash = md5(
serialize( $this->data ) );
774 $this->expires = $metadata[
'expires'];
782 if ( !$this->checkPHPSessionRecursionGuard ) {
783 $this->checkPHPSessionRecursionGuard =
true;
784 $reset = new \Wikimedia\ScopedCallback(
function () {
785 $this->checkPHPSessionRecursionGuard =
false;
791 $this->logger->debug(
792 'SessionBackend "{session}" Taking over PHP session',
794 'session' => $this->id->__toString(),
796 session_id( (
string)$this->
id );
797 AtEase::quietCall(
'session_start' );
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetAllCallers( $limit=3)
Return a string consisting of callers in the stack.
Wrapper around a BagOStuff that caches data in memory.
get( $key, $flags=0)
Get an item with the given key.
makeKey( $class,... $components)
Make a cache key, scoped to this instance's keyspace.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static isValidUserName( $name)
Is the input a valid username?
saveSettings()
Save this user's settings into the database.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...