/**
* Adapter for PHP's session handling
- * @todo Once we drop support for PHP < 5.4, use SessionHandlerInterface
- * (should just be a matter of adding "implements SessionHandlerInterface" and
- * changing the session_set_save_handler() call).
* @ingroup Session
* @since 1.27
*/
-class PHPSessionHandler {
+class PHPSessionHandler implements \SessionHandlerInterface {
/** @var PHPSessionHandler */
protected static $instance = null;
ini_set( 'session.use_cookies', 0 );
ini_set( 'session.use_trans_sid', 0 );
+ // T124510: Disable automatic PHP session related cache headers.
+ // MediaWiki adds it's own headers and the default PHP behavior may
+ // set headers such as 'Pragma: no-cache' that cause problems with
+ // some user agents.
+ session_cache_limiter( '' );
+
// Also set a sane serialization handler
\Wikimedia\PhpSessionSerializer::setSerializeHandler();
- session_set_save_handler(
- array( self::$instance, 'open' ),
- array( self::$instance, 'close' ),
- array( self::$instance, 'read' ),
- array( self::$instance, 'write' ),
- array( self::$instance, 'destroy' ),
- array( self::$instance, 'gc' )
- );
-
- // It's necessary to register a shutdown function to call session_write_close(),
- // because by the time the request shutdown function for the session module is
- // called, other needed objects may have already been destroyed. Shutdown functions
- // registered this way are called before object destruction.
- register_shutdown_function( array( self::$instance, 'handleShutdown' ) );
+ // Register this as the save handler, and register an appropriate
+ // shutdown function.
+ session_set_save_handler( self::$instance, true );
}
/**
$session = $this->manager->getSessionById( $id, true );
if ( !$session ) {
+ // This can happen under normal circumstances, if the session exists but is
+ // invalid. Let's emit a log warning instead of a PHP warning.
$this->logger->warning(
- __METHOD__ . ": Session \"$id\" cannot be loaded, skipping write."
- );
- return false;
+ __METHOD__ . ': Session "{session}" cannot be loaded, skipping write.',
+ array(
+ 'session' => $id,
+ ) );
+ return true;
}
// First, decode the string PHP handed us
$changed = false;
$cache = isset( $this->sessionFieldCache[$id] ) ? $this->sessionFieldCache[$id] : array();
foreach ( $data as $key => $value ) {
- if ( !isset( $cache[$key] ) ) {
+ if ( !array_key_exists( $key, $cache ) ) {
if ( $session->exists( $key ) ) {
// New in both, so ignore and log
$this->logger->warning(
// (but not if $_SESSION can't represent it at all)
\Wikimedia\PhpSessionSerializer::setLogger( new \Psr\Log\NullLogger() );
foreach ( $cache as $key => $value ) {
- if ( !isset( $data[$key] ) && $session->exists( $key ) &&
+ if ( !array_key_exists( $key, $data ) && $session->exists( $key ) &&
\Wikimedia\PhpSessionSerializer::encode( array( $key => true ) )
) {
if ( $cache[$key] === $session->get( $key ) ) {
$this->store->deleteObjectsExpiringBefore( $before );
return true;
}
-
- /**
- * Shutdown function.
- *
- * See the comment inside self::install for rationale.
- * @codeCoverageIgnore
- * @private For internal use only
- */
- public function handleShutdown() {
- if ( $this->enable ) {
- session_write_close();
- }
- }
-
}