SHA-1 of the data) */ protected static $hashCache = array(); /** * Install a session handler for the current web request */ static function install() { session_set_save_handler( array( __CLASS__, 'open' ), array( __CLASS__, 'close' ), array( __CLASS__, 'read' ), array( __CLASS__, 'write' ), array( __CLASS__, 'destroy' ), array( __CLASS__, '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, $wgMemc has already been destroyed. Shutdown functions registered // this way are called before object destruction. register_shutdown_function( array( __CLASS__, 'handleShutdown' ) ); } /** * Get the cache storage object to use for session storage * @return BagOStuff */ protected static function getCache() { global $wgSessionCacheType; return ObjectCache::getInstance( $wgSessionCacheType ); } /** * Get a cache key for the given session id. * * @param string $id Session id * @return string Cache key */ protected static function getKey( $id ) { return wfMemcKey( 'session', $id ); } /** * @param mixed $data * @return string */ protected static function getHash( $data ) { return sha1( serialize( $data ) ); } /** * Callback when opening a session. * * @param string $save_path Path used to store session files, unused * @param string $session_name Session name * @return bool Success */ static function open( $save_path, $session_name ) { return true; } /** * Callback when closing a session. * NOP. * * @return bool Success */ static function close() { return true; } /** * Callback when reading session data. * * @param string $id Session id * @return mixed Session data */ static function read( $id ) { $data = self::getCache()->get( self::getKey( $id ) ); self::$hashCache = array( $id => self::getHash( $data ) ); return ( $data === false ) ? '' : $data; } /** * Callback when writing session data. * * @param string $id Session id * @param string $data Session data * @return bool Success */ static function write( $id, $data ) { global $wgObjectCacheSessionExpiry; // Only issue a write if anything changed (PHP 5.6 already does this) if ( !isset( self::$hashCache[$id] ) || self::getHash( $data ) !== self::$hashCache[$id] ) { self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry ); } return true; } /** * Callback to destroy a session when calling session_destroy(). * * @param string $id Session id * @return bool Success */ static function destroy( $id ) { self::getCache()->delete( self::getKey( $id ) ); return true; } /** * Callback to execute garbage collection. * NOP: Object caches perform garbage collection implicitly * * @param int $maxlifetime Maximum session life time * @return bool Success */ static function gc( $maxlifetime ) { return true; } /** * Shutdown function. See the comment inside ObjectCacheSessionHandler::install * for rationale. */ static function handleShutdown() { session_write_close(); } }