Merge "OutputPage::getScript() was removed"
[lhc/web/wiklou.git] / includes / session / SessionBackend.php
index a79c5cb..1e2b476 100644 (file)
@@ -79,7 +79,7 @@ final class SessionBackend {
        private $curIndex = 0;
 
        /** @var WebRequest[] Session requests */
-       private $requests = array();
+       private $requests = [];
 
        /** @var SessionProvider provider */
        private $provider;
@@ -135,14 +135,14 @@ final class SessionBackend {
                        !isset( $blob['metadata'] ) || !is_array( $blob['metadata'] ) ||
                        !isset( $blob['data'] ) || !is_array( $blob['data'] )
                ) {
-                       $this->data = array();
+                       $this->data = [];
                        $this->dataDirty = true;
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" is unsaved, marking dirty in constructor',
-                               array(
+                               [
                                        'session' => $this->id,
-                       ) );
+                       ] );
                } else {
                        $this->data = $blob['data'];
                        if ( isset( $blob['metadata']['loggedOut'] ) ) {
@@ -154,9 +154,9 @@ final class SessionBackend {
                                $this->metaDirty = true;
                                $this->logger->debug(
                                        'SessionBackend "{session}" metadata dirty due to missing expiration timestamp',
-                               array(
+                               [
                                        'session' => $this->id,
-                               ) );
+                               ] );
                        }
                }
                $this->dataHash = md5( serialize( $this->data ) );
@@ -170,7 +170,7 @@ final class SessionBackend {
        public function getSession( WebRequest $request ) {
                $index = ++$this->curIndex;
                $this->requests[$index] = $request;
-               $session = new Session( $this, $index );
+               $session = new Session( $this, $index, $this->logger );
                return $session;
        }
 
@@ -225,10 +225,10 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" metadata dirty due to ID reset (formerly "{oldId}")',
-                               array(
+                               [
                                        'session' => $this->id,
                                        'oldId' => $oldId,
-                       ) );
+                       ] );
 
                        if ( $restart ) {
                                session_id( (string)$this->id );
@@ -274,15 +274,44 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" force-persist due to persist()',
-                               array(
+                               [
                                        'session' => $this->id,
-                       ) );
+                       ] );
                        $this->autosave();
                } else {
                        $this->renew();
                }
        }
 
+       /**
+        * Make this session not persisted across requests
+        */
+       public function unpersist() {
+               if ( $this->persist ) {
+                       // Close the PHP session, if we're the one that's open
+                       if ( $this->usePhpSessionHandling && PHPSessionHandler::isEnabled() &&
+                               session_id() === (string)$this->id
+                       ) {
+                               $this->logger->debug(
+                                       'SessionBackend "{session}" Closing PHP session for unpersist',
+                                       [ 'session' => $this->id ]
+                               );
+                               session_write_close();
+                               session_id( '' );
+                       }
+
+                       $this->persist = false;
+                       $this->forcePersist = true;
+                       $this->metaDirty = true;
+
+                       // Delete the session data, so the local cache-only write in
+                       // self::save() doesn't get things out of sync with the backend.
+                       $this->store->delete( wfMemcKey( 'MWSession', (string)$this->id ) );
+
+                       $this->autosave();
+               }
+       }
+
        /**
         * Indicate whether the user should be remembered independently of the
         * session ID.
@@ -303,9 +332,9 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" metadata dirty due to remember-user change',
-                               array(
+                               [
                                        'session' => $this->id,
-                       ) );
+                       ] );
                        $this->autosave();
                }
        }
@@ -364,9 +393,9 @@ final class SessionBackend {
                $this->metaDirty = true;
                $this->logger->debug(
                        'SessionBackend "{session}" metadata dirty due to user change',
-                       array(
+                       [
                                'session' => $this->id,
-               ) );
+               ] );
                $this->autosave();
        }
 
@@ -400,9 +429,9 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" metadata dirty due to force-HTTPS change',
-                               array(
+                               [
                                        'session' => $this->id,
-                       ) );
+                       ] );
                        $this->autosave();
                }
        }
@@ -426,9 +455,9 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" metadata dirty due to logged-out-timestamp change',
-                               array(
+                               [
                                        'session' => $this->id,
-                       ) );
+                       ] );
                        $this->autosave();
                }
        }
@@ -456,9 +485,9 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{session}" metadata dirty due to provider metadata change',
-                               array(
+                               [
                                        'session' => $this->id,
-                       ) );
+                       ] );
                        $this->autosave();
                }
        }
@@ -491,10 +520,10 @@ final class SessionBackend {
                                $this->dataDirty = true;
                                $this->logger->debug(
                                        'SessionBackend "{session}" data dirty due to addData(): {callers}',
-                                       array(
+                                       [
                                                'session' => $this->id,
                                                'callers' => wfGetAllCallers( 5 ),
-                               ) );
+                               ] );
                        }
                }
        }
@@ -507,10 +536,10 @@ final class SessionBackend {
                $this->dataDirty = true;
                $this->logger->debug(
                        'SessionBackend "{session}" data dirty due to dirty(): {callers}',
-                       array(
+                       [
                                'session' => $this->id,
                                'callers' => wfGetAllCallers( 5 ),
-               ) );
+               ] );
        }
 
        /**
@@ -524,18 +553,18 @@ final class SessionBackend {
                        $this->metaDirty = true;
                        $this->logger->debug(
                                'SessionBackend "{callers}" metadata dirty for renew(): {callers}',
-                               array(
+                               [
                                        'session' => $this->id,
                                        'callers' => wfGetAllCallers( 5 ),
-                       ) );
+                       ] );
                        if ( $this->persist ) {
                                $this->forcePersist = true;
                                $this->logger->debug(
                                        'SessionBackend "{session}" force-persist for renew(): {callers}',
-                                       array(
+                                       [
                                                'session' => $this->id,
                                                'callers' => wfGetAllCallers( 5 ),
-                               ) );
+                               ] );
                        }
                }
                $this->autosave();
@@ -549,13 +578,11 @@ final class SessionBackend {
         * @return \ScopedCallback When this goes out of scope, a save will be triggered
         */
        public function delaySave() {
-               $that = $this;
                $this->delaySave++;
-               $ref = &$this->delaySave;
-               return new \ScopedCallback( function () use ( $that, &$ref ) {
-                       if ( --$ref <= 0 ) {
-                               $ref = 0;
-                               $that->save();
+               return new \ScopedCallback( function () {
+                       if ( --$this->delaySave <= 0 ) {
+                               $this->delaySave = 0;
+                               $this->save();
                        }
                } );
        }
@@ -574,27 +601,28 @@ final class SessionBackend {
         * @param bool $closing Whether the session is being closed
         */
        public function save( $closing = false ) {
-               if ( $this->provider->getManager()->isUserSessionPrevented( $this->user->getName() ) ) {
+               $anon = $this->user->isAnon();
+
+               if ( !$anon && $this->provider->getManager()->isUserSessionPrevented( $this->user->getName() ) ) {
                        $this->logger->debug(
                                'SessionBackend "{session}" not saving, user {user} was ' .
                                'passed to SessionManager::preventSessionsForUser',
-                               array(
+                               [
                                        'session' => $this->id,
                                        'user' => $this->user,
-                       ) );
+                       ] );
                        return;
                }
 
                // Ensure the user has a token
                // @codeCoverageIgnoreStart
-               $anon = $this->user->isAnon();
                if ( !$anon && !$this->user->getToken( false ) ) {
                        $this->logger->debug(
                                'SessionBackend "{session}" creating token for user {user} on save',
-                               array(
+                               [
                                        'session' => $this->id,
                                        'user' => $this->user,
-                       ) );
+                       ] );
                        $this->user->setToken();
                        if ( !wfReadOnly() ) {
                                $this->user->saveSettings();
@@ -608,11 +636,11 @@ final class SessionBackend {
                ) {
                        $this->logger->debug(
                                'SessionBackend "{session}" data dirty due to hash mismatch, {expected} !== {got}',
-                               array(
+                               [
                                        'session' => $this->id,
                                        'expected' => $this->dataHash,
                                        'got' => md5( serialize( $this->data ) ),
-                       ) );
+                       ] );
                        $this->dataDirty = true;
                }
 
@@ -623,21 +651,29 @@ final class SessionBackend {
                $this->logger->debug(
                        'SessionBackend "{session}" save: dataDirty={dataDirty} ' .
                        'metaDirty={metaDirty} forcePersist={forcePersist}',
-                       array(
+                       [
                                'session' => $this->id,
                                'dataDirty' => (int)$this->dataDirty,
                                'metaDirty' => (int)$this->metaDirty,
                                'forcePersist' => (int)$this->forcePersist,
-               ) );
+               ] );
 
-               // Persist to the provider, if flagged
-               if ( $this->persist && ( $this->metaDirty || $this->forcePersist ) ) {
-                       foreach ( $this->requests as $request ) {
-                               $request->setSessionId( $this->getSessionId() );
-                               $this->provider->persistSession( $this, $request );
-                       }
-                       if ( !$closing ) {
-                               $this->checkPHPSession();
+               // Persist or unpersist to the provider, if necessary
+               if ( $this->metaDirty || $this->forcePersist ) {
+                       if ( $this->persist ) {
+                               foreach ( $this->requests as $request ) {
+                                       $request->setSessionId( $this->getSessionId() );
+                                       $this->provider->persistSession( $this, $request );
+                               }
+                               if ( !$closing ) {
+                                       $this->checkPHPSession();
+                               }
+                       } else {
+                               foreach ( $this->requests as $request ) {
+                                       if ( $request->getSessionId() === $this->id ) {
+                                               $this->provider->unpersistSession( $request );
+                                       }
+                               }
                        }
                }
 
@@ -648,7 +684,7 @@ final class SessionBackend {
                }
 
                // Save session data to store, if necessary
-               $metadata = $origMetadata = array(
+               $metadata = $origMetadata = [
                        'provider' => (string)$this->provider,
                        'providerMetadata' => $this->providerMetadata,
                        'userId' => $anon ? 0 : $this->user->getId(),
@@ -659,9 +695,9 @@ final class SessionBackend {
                        'expires' => time() + $this->lifetime,
                        'loggedOut' => $this->loggedOut,
                        'persisted' => $this->persist,
-               );
+               ];
 
-               \Hooks::run( 'SessionMetadata', array( $this, &$metadata, $this->requests ) );
+               \Hooks::run( 'SessionMetadata', [ $this, &$metadata, $this->requests ] );
 
                foreach ( $origMetadata as $k => $v ) {
                        if ( $metadata[$k] !== $v ) {
@@ -671,10 +707,10 @@ final class SessionBackend {
 
                $this->store->set(
                        wfMemcKey( 'MWSession', (string)$this->id ),
-                       array(
+                       [
                                'data' => $this->data,
                                'metadata' => $metadata,
-                       ),
+                       ],
                        $metadata['expires'],
                        $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY
                );
@@ -692,9 +728,8 @@ final class SessionBackend {
        private function checkPHPSession() {
                if ( !$this->checkPHPSessionRecursionGuard ) {
                        $this->checkPHPSessionRecursionGuard = true;
-                       $ref = &$this->checkPHPSessionRecursionGuard;
-                       $reset = new \ScopedCallback( function () use ( &$ref ) {
-                               $ref = false;
+                       $reset = new \ScopedCallback( function () {
+                               $this->checkPHPSessionRecursionGuard = false;
                        } );
 
                        if ( $this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() &&
@@ -702,9 +737,9 @@ final class SessionBackend {
                        ) {
                                $this->logger->debug(
                                        'SessionBackend "{session}" Taking over PHP session',
-                                       array(
+                                       [
                                                'session' => $this->id,
-                               ) );
+                               ] );
                                session_id( (string)$this->id );
                                \MediaWiki\quietCall( 'session_start' );
                        }