From f1a751db941558b7a480730cb6e009bc227784ef Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Wed, 20 Apr 2016 12:27:26 -0400 Subject: [PATCH] SessionManager: Ignore Session object destruction during global shutdown We already save all open SessionBackends when shutdown handlers are run, which *should* make the Session object destructors that run during global shutdown not have anything to save. But it can get fooled if the Session data contains other objects that have already gotten destroyed during the global shutdown, leading to spurious warnings and errors as it tries to access partically-destroyed objects. The solution is to set a flag when we do the shutdown handlers and just ignore the last gasps from Session::__destruct() that might come after. Change-Id: Ic3eb0bac2d29a30488c84b6525ad796a7f1c9ce9 --- includes/session/SessionBackend.php | 13 ++++++++++++- includes/session/SessionManager.php | 2 +- tests/phpunit/includes/session/SessionManagerTest.php | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/includes/session/SessionBackend.php b/includes/session/SessionBackend.php index 2626aa88134..264e1ae0efd 100644 --- a/includes/session/SessionBackend.php +++ b/includes/session/SessionBackend.php @@ -94,6 +94,8 @@ final class SessionBackend { private $usePhpSessionHandling = true; private $checkPHPSessionRecursionGuard = false; + private $shutdown = false; + /** * @param SessionId $id Session ID object * @param SessionInfo $info Session info to populate from @@ -181,13 +183,22 @@ final class SessionBackend { */ public function deregisterSession( $index ) { unset( $this->requests[$index] ); - if ( !count( $this->requests ) ) { + if ( !$this->shutdown && !count( $this->requests ) ) { $this->save( true ); $this->provider->getManager()->deregisterSessionBackend( $this ); } } /** + * Shut down a session + * @private For use by \MediaWiki\Session\SessionManager::shutdown() only + */ + public function shutdown() { + $this->save( true ); + $this->shutdown = true; + } + + /** * Returns the session ID. * @return string */ diff --git a/includes/session/SessionManager.php b/includes/session/SessionManager.php index 29cd69a3720..a364045aaeb 100644 --- a/includes/session/SessionManager.php +++ b/includes/session/SessionManager.php @@ -626,7 +626,7 @@ final class SessionManager implements SessionManagerInterface { } // @codeCoverageIgnoreEnd foreach ( $this->allSessionBackends as $backend ) { - $backend->save( true ); + $backend->shutdown(); } } } diff --git a/tests/phpunit/includes/session/SessionManagerTest.php b/tests/phpunit/includes/session/SessionManagerTest.php index 6218f0a728d..d04d7ec46da 100644 --- a/tests/phpunit/includes/session/SessionManagerTest.php +++ b/tests/phpunit/includes/session/SessionManagerTest.php @@ -751,8 +751,8 @@ class SessionManagerTest extends MediaWikiTestCase { $manager = \TestingAccessWrapper::newFromObject( $this->getManager() ); $manager->setLogger( new \Psr\Log\NullLogger() ); - $mock = $this->getMock( 'stdClass', [ 'save' ] ); - $mock->expects( $this->once() )->method( 'save' ); + $mock = $this->getMock( 'stdClass', [ 'shutdown' ] ); + $mock->expects( $this->once() )->method( 'shutdown' ); $manager->allSessionBackends = [ $mock ]; $manager->shutdown(); -- 2.11.4.GIT