diff options
Diffstat (limited to 'includes/context/RequestContext.php')
-rw-r--r-- | includes/context/RequestContext.php | 137 |
1 files changed, 117 insertions, 20 deletions
diff --git a/includes/context/RequestContext.php b/includes/context/RequestContext.php index 9e7837d9..6aefc98e 100644 --- a/includes/context/RequestContext.php +++ b/includes/context/RequestContext.php @@ -28,7 +28,6 @@ * Group all the pieces relevant to the context of a request into one instance */ class RequestContext implements IContextSource { - /** * @var WebRequest */ @@ -67,7 +66,7 @@ class RequestContext implements IContextSource { /** * Set the WebRequest object * - * @param $r WebRequest object + * @param WebRequest $r */ public function setRequest( WebRequest $r ) { $this->request = $r; @@ -89,10 +88,12 @@ class RequestContext implements IContextSource { /** * Set the Title object * - * @param $t Title object + * @param Title $t */ public function setTitle( Title $t ) { $this->title = $t; + // Erase the WikiPage so a new one with the new title gets created. + $this->wikipage = null; } /** @@ -135,9 +136,15 @@ class RequestContext implements IContextSource { * Set the WikiPage object * * @since 1.19 - * @param $p WikiPage object + * @param WikiPage $p */ public function setWikiPage( WikiPage $p ) { + $contextTitle = $this->getTitle(); + $pageTitle = $p->getTitle(); + if ( !$contextTitle || !$pageTitle->equals( $contextTitle ) ) { + $this->setTitle( $pageTitle ); + } + // Defer this to the end since setTitle sets it to null. $this->wikipage = $p; } @@ -148,6 +155,7 @@ class RequestContext implements IContextSource { * canUseWikiPage() to check whether this method can be called safely. * * @since 1.19 + * @throws MWException * @return WikiPage */ public function getWikiPage() { @@ -171,7 +179,7 @@ class RequestContext implements IContextSource { /** * Get the OutputPage object * - * @return OutputPage object + * @return OutputPage */ public function getOutput() { if ( $this->output === null ) { @@ -183,7 +191,7 @@ class RequestContext implements IContextSource { /** * Set the User object * - * @param $u User + * @param User $u */ public function setUser( User $u ) { $this->user = $u; @@ -204,7 +212,7 @@ class RequestContext implements IContextSource { /** * Accepts a language code and ensures it's sane. Outputs a cleaned up language * code and replaces with $wgLanguageCode if not sane. - * @param $code string + * @param string $code Language code * @return string */ public static function sanitizeLangCode( $code ) { @@ -214,7 +222,7 @@ class RequestContext implements IContextSource { $code = strtolower( $code ); # Validate $code - if( empty( $code ) || !Language::isValidCode( $code ) || ( $code === 'qqq' ) ) { + if ( empty( $code ) || !Language::isValidCode( $code ) || ( $code === 'qqq' ) ) { wfDebug( "Invalid user language code\n" ); $code = $wgLanguageCode; } @@ -226,7 +234,7 @@ class RequestContext implements IContextSource { * Set the Language object * * @deprecated 1.19 Use setLanguage instead - * @param $l Mixed Language instance or language code + * @param Language|string $l Language instance or language code */ public function setLang( $l ) { wfDeprecated( __METHOD__, '1.19' ); @@ -236,7 +244,8 @@ class RequestContext implements IContextSource { /** * Set the Language object * - * @param $l Mixed Language instance or language code + * @param Language|string $l Language instance or language code + * @throws MWException * @since 1.19 */ public function setLanguage( $l ) { @@ -289,7 +298,7 @@ class RequestContext implements IContextSource { wfRunHooks( 'UserGetLanguageObject', array( $user, &$code, $this ) ); - if( $code === $wgLanguageCode ) { + if ( $code === $wgLanguageCode ) { $this->lang = $wgContLang; } else { $obj = Language::factory( $code ); @@ -305,7 +314,7 @@ class RequestContext implements IContextSource { /** * Set the Skin object * - * @param $s Skin + * @param Skin $s */ public function setSkin( Skin $s ) { $this->skin = clone $s; @@ -320,14 +329,14 @@ class RequestContext implements IContextSource { public function getSkin() { if ( $this->skin === null ) { wfProfileIn( __METHOD__ . '-createskin' ); - + $skin = null; wfRunHooks( 'RequestContextCreateSkin', array( $this, &$skin ) ); // If the hook worked try to set a skin from it if ( $skin instanceof Skin ) { $this->skin = $skin; - } elseif ( is_string($skin) ) { + } elseif ( is_string( $skin ) ) { $this->skin = Skin::newFromKey( $skin ); } @@ -335,7 +344,7 @@ class RequestContext implements IContextSource { // then go through the normal processing to load a skin if ( $this->skin === null ) { global $wgHiddenPrefs; - if( !in_array( 'skin', $wgHiddenPrefs ) ) { + if ( !in_array( 'skin', $wgHiddenPrefs ) ) { # get the user skin $userSkin = $this->getUser()->getOption( 'skin' ); $userSkin = $this->getRequest()->getVal( 'useskin', $userSkin ); @@ -361,7 +370,7 @@ class RequestContext implements IContextSource { * Get a Message object with context set * Parameters are the same as wfMessage() * - * @return Message object + * @return Message */ public function msg() { $args = func_get_args(); @@ -373,7 +382,7 @@ class RequestContext implements IContextSource { /** * Get the RequestContext object associated with the main request * - * @return RequestContext object + * @return RequestContext */ public static function getMain() { static $instance = null; @@ -384,6 +393,96 @@ class RequestContext implements IContextSource { } /** + * Export the resolved user IP, HTTP headers, user ID, and session ID. + * The result will be reasonably sized to allow for serialization. + * + * @return Array + * @since 1.21 + */ + public function exportSession() { + return array( + 'ip' => $this->getRequest()->getIP(), + 'headers' => $this->getRequest()->getAllHeaders(), + 'sessionId' => session_id(), + 'userId' => $this->getUser()->getId() + ); + } + + /** + * Import the resolved user IP, HTTP headers, user ID, and session ID. + * This sets the current session and sets $wgUser and $wgRequest. + * Once the return value falls out of scope, the old context is restored. + * This function can only be called within CLI mode scripts. + * + * This will setup the session from the given ID. This is useful when + * background scripts inherit context when acting on behalf of a user. + * + * $param array $params Result of RequestContext::exportSession() + * @return ScopedCallback + * @throws MWException + * @since 1.21 + */ + public static function importScopedSession( array $params ) { + if ( PHP_SAPI !== 'cli' ) { + // Don't send random private cookies or turn $wgRequest into FauxRequest + throw new MWException( "Sessions can only be imported in cli mode." ); + } elseif ( !strlen( $params['sessionId'] ) ) { + throw new MWException( "No session ID was specified." ); + } + + if ( $params['userId'] ) { // logged-in user + $user = User::newFromId( $params['userId'] ); + if ( !$user ) { + throw new MWException( "No user with ID '{$params['userId']}'." ); + } + } elseif ( !IP::isValid( $params['ip'] ) ) { + throw new MWException( "Could not load user '{$params['ip']}'." ); + } else { // anon user + $user = User::newFromName( $params['ip'], false ); + } + + $importSessionFunction = function( User $user, array $params ) { + global $wgRequest, $wgUser; + + $context = RequestContext::getMain(); + // Commit and close any current session + session_write_close(); // persist + session_id( '' ); // detach + $_SESSION = array(); // clear in-memory array + // Remove any user IP or agent information + $context->setRequest( new FauxRequest() ); + $wgRequest = $context->getRequest(); // b/c + // Now that all private information is detached from the user, it should + // be safe to load the new user. If errors occur or an exception is thrown + // and caught (leaving the main context in a mixed state), there is no risk + // of the User object being attached to the wrong IP, headers, or session. + $context->setUser( $user ); + $wgUser = $context->getUser(); // b/c + if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID + wfSetupSession( $params['sessionId'] ); // sets $_SESSION + } + $request = new FauxRequest( array(), false, $_SESSION ); + $request->setIP( $params['ip'] ); + foreach ( $params['headers'] as $name => $value ) { + $request->setHeader( $name, $value ); + } + // Set the current context to use the new WebRequest + $context->setRequest( $request ); + $wgRequest = $context->getRequest(); // b/c + }; + + // Stash the old session and load in the new one + $oUser = self::getMain()->getUser(); + $oParams = self::getMain()->exportSession(); + $importSessionFunction( $user, $params ); + + // Set callback to save and close the new session and reload the old one + return new ScopedCallback( function() use ( $importSessionFunction, $oUser, $oParams ) { + $importSessionFunction( $oUser, $oParams ); + } ); + } + + /** * Create a new extraneous context. The context is filled with information * external to the current session. * - Title is specified by argument @@ -397,7 +496,7 @@ class RequestContext implements IContextSource { * @param WebRequest|array $request A WebRequest or data to use for a FauxRequest * @return RequestContext */ - public static function newExtraneousContext( Title $title, $request=array() ) { + public static function newExtraneousContext( Title $title, $request = array() ) { $context = new self; $context->setTitle( $title ); if ( $request instanceof WebRequest ) { @@ -408,6 +507,4 @@ class RequestContext implements IContextSource { $context->user = User::newFromName( '127.0.0.1', false ); return $context; } - } - |