MediaWiki  REL1_22
RequestContext.php
Go to the documentation of this file.
00001 <?php
00030 class RequestContext implements IContextSource {
00034     private $request;
00035 
00039     private $title;
00040 
00044     private $wikipage;
00045 
00049     private $output;
00050 
00054     private $user;
00055 
00059     private $lang;
00060 
00064     private $skin;
00065 
00071     public function setRequest( WebRequest $r ) {
00072         $this->request = $r;
00073     }
00074 
00080     public function getRequest() {
00081         if ( $this->request === null ) {
00082             global $wgRequest; # fallback to $wg till we can improve this
00083             $this->request = $wgRequest;
00084         }
00085         return $this->request;
00086     }
00087 
00093     public function setTitle( $t ) {
00094         if ( $t !== null && !$t instanceof Title ) {
00095             throw new MWException( __METHOD__ . " expects an instance of Title" );
00096         }
00097         $this->title = $t;
00098         // Erase the WikiPage so a new one with the new title gets created.
00099         $this->wikipage = null;
00100     }
00101 
00107     public function getTitle() {
00108         if ( $this->title === null ) {
00109             global $wgTitle; # fallback to $wg till we can improve this
00110             $this->title = $wgTitle;
00111         }
00112         return $this->title;
00113     }
00114 
00123     public function canUseWikiPage() {
00124         if ( $this->wikipage !== null ) {
00125             # If there's a WikiPage object set, we can for sure get it
00126             return true;
00127         }
00128         $title = $this->getTitle();
00129         if ( $title === null ) {
00130             # No Title, no WikiPage
00131             return false;
00132         } else {
00133             # Only namespaces whose pages are stored in the database can have WikiPage
00134             return $title->canExist();
00135         }
00136     }
00137 
00144     public function setWikiPage( WikiPage $p ) {
00145         $contextTitle = $this->getTitle();
00146         $pageTitle = $p->getTitle();
00147         if ( !$contextTitle || !$pageTitle->equals( $contextTitle ) ) {
00148             $this->setTitle( $pageTitle );
00149         }
00150         // Defer this to the end since setTitle sets it to null.
00151         $this->wikipage = $p;
00152     }
00153 
00164     public function getWikiPage() {
00165         if ( $this->wikipage === null ) {
00166             $title = $this->getTitle();
00167             if ( $title === null ) {
00168                 throw new MWException( __METHOD__ . ' called without Title object set' );
00169             }
00170             $this->wikipage = WikiPage::factory( $title );
00171         }
00172         return $this->wikipage;
00173     }
00174 
00178     public function setOutput( OutputPage $o ) {
00179         $this->output = $o;
00180     }
00181 
00187     public function getOutput() {
00188         if ( $this->output === null ) {
00189             $this->output = new OutputPage( $this );
00190         }
00191         return $this->output;
00192     }
00193 
00199     public function setUser( User $u ) {
00200         $this->user = $u;
00201     }
00202 
00208     public function getUser() {
00209         if ( $this->user === null ) {
00210             $this->user = User::newFromSession( $this->getRequest() );
00211         }
00212         return $this->user;
00213     }
00214 
00221     public static function sanitizeLangCode( $code ) {
00222         global $wgLanguageCode;
00223 
00224         // BCP 47 - letter case MUST NOT carry meaning
00225         $code = strtolower( $code );
00226 
00227         # Validate $code
00228         if ( empty( $code ) || !Language::isValidCode( $code ) || ( $code === 'qqq' ) ) {
00229             wfDebug( "Invalid user language code\n" );
00230             $code = $wgLanguageCode;
00231         }
00232 
00233         return $code;
00234     }
00235 
00242     public function setLang( $l ) {
00243         wfDeprecated( __METHOD__, '1.19' );
00244         $this->setLanguage( $l );
00245     }
00246 
00254     public function setLanguage( $l ) {
00255         if ( $l instanceof Language ) {
00256             $this->lang = $l;
00257         } elseif ( is_string( $l ) ) {
00258             $l = self::sanitizeLangCode( $l );
00259             $obj = Language::factory( $l );
00260             $this->lang = $obj;
00261         } else {
00262             throw new MWException( __METHOD__ . " was passed an invalid type of data." );
00263         }
00264     }
00265 
00270     public function getLang() {
00271         wfDeprecated( __METHOD__, '1.19' );
00272         return $this->getLanguage();
00273     }
00274 
00282     public function getLanguage() {
00283         if ( isset( $this->recursion ) ) {
00284             trigger_error( "Recursion detected in " . __METHOD__, E_USER_WARNING );
00285             $e = new Exception;
00286             wfDebugLog( 'recursion-guard', "Recursion detected:\n" . $e->getTraceAsString() );
00287 
00288             global $wgLanguageCode;
00289             $code = ( $wgLanguageCode ) ? $wgLanguageCode : 'en';
00290             $this->lang = Language::factory( $code );
00291         } elseif ( $this->lang === null ) {
00292             $this->recursion = true;
00293 
00294             global $wgLanguageCode, $wgContLang;
00295 
00296             $request = $this->getRequest();
00297             $user = $this->getUser();
00298 
00299             $code = $request->getVal( 'uselang', $user->getOption( 'language' ) );
00300             $code = self::sanitizeLangCode( $code );
00301 
00302             wfRunHooks( 'UserGetLanguageObject', array( $user, &$code, $this ) );
00303 
00304             if ( $code === $wgLanguageCode ) {
00305                 $this->lang = $wgContLang;
00306             } else {
00307                 $obj = Language::factory( $code );
00308                 $this->lang = $obj;
00309             }
00310 
00311             unset( $this->recursion );
00312         }
00313 
00314         return $this->lang;
00315     }
00316 
00322     public function setSkin( Skin $s ) {
00323         $this->skin = clone $s;
00324         $this->skin->setContext( $this );
00325     }
00326 
00332     public function getSkin() {
00333         if ( $this->skin === null ) {
00334             wfProfileIn( __METHOD__ . '-createskin' );
00335 
00336             $skin = null;
00337             wfRunHooks( 'RequestContextCreateSkin', array( $this, &$skin ) );
00338 
00339             // If the hook worked try to set a skin from it
00340             if ( $skin instanceof Skin ) {
00341                 $this->skin = $skin;
00342             } elseif ( is_string( $skin ) ) {
00343                 $this->skin = Skin::newFromKey( $skin );
00344             }
00345 
00346             // If this is still null (the hook didn't run or didn't work)
00347             // then go through the normal processing to load a skin
00348             if ( $this->skin === null ) {
00349                 global $wgHiddenPrefs;
00350                 if ( !in_array( 'skin', $wgHiddenPrefs ) ) {
00351                     # get the user skin
00352                     $userSkin = $this->getUser()->getOption( 'skin' );
00353                     $userSkin = $this->getRequest()->getVal( 'useskin', $userSkin );
00354                 } else {
00355                     # if we're not allowing users to override, then use the default
00356                     global $wgDefaultSkin;
00357                     $userSkin = $wgDefaultSkin;
00358                 }
00359 
00360                 $this->skin = Skin::newFromKey( $userSkin );
00361             }
00362 
00363             // After all that set a context on whatever skin got created
00364             $this->skin->setContext( $this );
00365             wfProfileOut( __METHOD__ . '-createskin' );
00366         }
00367         return $this->skin;
00368     }
00369 
00378     public function msg() {
00379         $args = func_get_args();
00380         return call_user_func_array( 'wfMessage', $args )->setContext( $this );
00381     }
00382 
00390     public static function getMain() {
00391         static $instance = null;
00392         if ( $instance === null ) {
00393             $instance = new self;
00394         }
00395         return $instance;
00396     }
00397 
00405     public function exportSession() {
00406         return array(
00407             'ip' => $this->getRequest()->getIP(),
00408             'headers' => $this->getRequest()->getAllHeaders(),
00409             'sessionId' => session_id(),
00410             'userId' => $this->getUser()->getId()
00411         );
00412     }
00413 
00430     public static function importScopedSession( array $params ) {
00431         if ( PHP_SAPI !== 'cli' ) {
00432             // Don't send random private cookies or turn $wgRequest into FauxRequest
00433             throw new MWException( "Sessions can only be imported in cli mode." );
00434         } elseif ( !strlen( $params['sessionId'] ) ) {
00435             throw new MWException( "No session ID was specified." );
00436         }
00437 
00438         if ( $params['userId'] ) { // logged-in user
00439             $user = User::newFromId( $params['userId'] );
00440             if ( !$user ) {
00441                 throw new MWException( "No user with ID '{$params['userId']}'." );
00442             }
00443         } elseif ( !IP::isValid( $params['ip'] ) ) {
00444             throw new MWException( "Could not load user '{$params['ip']}'." );
00445         } else { // anon user
00446             $user = User::newFromName( $params['ip'], false );
00447         }
00448 
00449         $importSessionFunction = function( User $user, array $params ) {
00450             global $wgRequest, $wgUser;
00451 
00452             $context = RequestContext::getMain();
00453             // Commit and close any current session
00454             session_write_close(); // persist
00455             session_id( '' ); // detach
00456             $_SESSION = array(); // clear in-memory array
00457             // Remove any user IP or agent information
00458             $context->setRequest( new FauxRequest() );
00459             $wgRequest = $context->getRequest(); // b/c
00460             // Now that all private information is detached from the user, it should
00461             // be safe to load the new user. If errors occur or an exception is thrown
00462             // and caught (leaving the main context in a mixed state), there is no risk
00463             // of the User object being attached to the wrong IP, headers, or session.
00464             $context->setUser( $user );
00465             $wgUser = $context->getUser(); // b/c
00466             if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID
00467                 wfSetupSession( $params['sessionId'] ); // sets $_SESSION
00468             }
00469             $request = new FauxRequest( array(), false, $_SESSION );
00470             $request->setIP( $params['ip'] );
00471             foreach ( $params['headers'] as $name => $value ) {
00472                 $request->setHeader( $name, $value );
00473             }
00474             // Set the current context to use the new WebRequest
00475             $context->setRequest( $request );
00476             $wgRequest = $context->getRequest(); // b/c
00477         };
00478 
00479         // Stash the old session and load in the new one
00480         $oUser = self::getMain()->getUser();
00481         $oParams = self::getMain()->exportSession();
00482         $importSessionFunction( $user, $params );
00483 
00484         // Set callback to save and close the new session and reload the old one
00485         return new ScopedCallback( function() use ( $importSessionFunction, $oUser, $oParams ) {
00486             $importSessionFunction( $oUser, $oParams );
00487         } );
00488     }
00489 
00504     public static function newExtraneousContext( Title $title, $request = array() ) {
00505         $context = new self;
00506         $context->setTitle( $title );
00507         if ( $request instanceof WebRequest ) {
00508             $context->setRequest( $request );
00509         } else {
00510             $context->setRequest( new FauxRequest( $request ) );
00511         }
00512         $context->user = User::newFromName( '127.0.0.1', false );
00513         return $context;
00514     }
00515 }