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 +- .../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 2626aa8813..264e1ae0ef 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,12 +183,21 @@ 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 29cd69a372..a364045aae 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 6218f0a728..d04d7ec46d 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.20.1