MediaWiki  REL1_23
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 
00069     private $config;
00070 
00076     public function setConfig( Config $c ) {
00077         $this->config = $c;
00078     }
00079 
00085     public function getConfig() {
00086         if ( $this->config === null ) {
00087             // @todo In the future, we could move this to WebStart.php so
00088             // the Config object is ready for when initialization happens
00089             $this->config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
00090         }
00091 
00092         return $this->config;
00093     }
00094 
00100     public function setRequest( WebRequest $r ) {
00101         $this->request = $r;
00102     }
00103 
00109     public function getRequest() {
00110         if ( $this->request === null ) {
00111             global $wgRequest; # fallback to $wg till we can improve this
00112             $this->request = $wgRequest;
00113         }
00114 
00115         return $this->request;
00116     }
00117 
00124     public function setTitle( $t ) {
00125         if ( $t !== null && !$t instanceof Title ) {
00126             throw new MWException( __METHOD__ . " expects an instance of Title" );
00127         }
00128         $this->title = $t;
00129         // Erase the WikiPage so a new one with the new title gets created.
00130         $this->wikipage = null;
00131     }
00132 
00138     public function getTitle() {
00139         if ( $this->title === null ) {
00140             global $wgTitle; # fallback to $wg till we can improve this
00141             $this->title = $wgTitle;
00142         }
00143 
00144         return $this->title;
00145     }
00146 
00155     public function canUseWikiPage() {
00156         if ( $this->wikipage !== null ) {
00157             # If there's a WikiPage object set, we can for sure get it
00158             return true;
00159         }
00160         $title = $this->getTitle();
00161         if ( $title === null ) {
00162             # No Title, no WikiPage
00163             return false;
00164         } else {
00165             # Only namespaces whose pages are stored in the database can have WikiPage
00166             return $title->canExist();
00167         }
00168     }
00169 
00176     public function setWikiPage( WikiPage $p ) {
00177         $contextTitle = $this->getTitle();
00178         $pageTitle = $p->getTitle();
00179         if ( !$contextTitle || !$pageTitle->equals( $contextTitle ) ) {
00180             $this->setTitle( $pageTitle );
00181         }
00182         // Defer this to the end since setTitle sets it to null.
00183         $this->wikipage = $p;
00184     }
00185 
00196     public function getWikiPage() {
00197         if ( $this->wikipage === null ) {
00198             $title = $this->getTitle();
00199             if ( $title === null ) {
00200                 throw new MWException( __METHOD__ . ' called without Title object set' );
00201             }
00202             $this->wikipage = WikiPage::factory( $title );
00203         }
00204 
00205         return $this->wikipage;
00206     }
00207 
00211     public function setOutput( OutputPage $o ) {
00212         $this->output = $o;
00213     }
00214 
00220     public function getOutput() {
00221         if ( $this->output === null ) {
00222             $this->output = new OutputPage( $this );
00223         }
00224 
00225         return $this->output;
00226     }
00227 
00233     public function setUser( User $u ) {
00234         $this->user = $u;
00235     }
00236 
00242     public function getUser() {
00243         if ( $this->user === null ) {
00244             $this->user = User::newFromSession( $this->getRequest() );
00245         }
00246 
00247         return $this->user;
00248     }
00249 
00256     public static function sanitizeLangCode( $code ) {
00257         global $wgLanguageCode;
00258 
00259         // BCP 47 - letter case MUST NOT carry meaning
00260         $code = strtolower( $code );
00261 
00262         # Validate $code
00263         if ( empty( $code ) || !Language::isValidCode( $code ) || ( $code === 'qqq' ) ) {
00264             wfDebug( "Invalid user language code\n" );
00265             $code = $wgLanguageCode;
00266         }
00267 
00268         return $code;
00269     }
00270 
00277     public function setLang( $l ) {
00278         wfDeprecated( __METHOD__, '1.19' );
00279         $this->setLanguage( $l );
00280     }
00281 
00289     public function setLanguage( $l ) {
00290         if ( $l instanceof Language ) {
00291             $this->lang = $l;
00292         } elseif ( is_string( $l ) ) {
00293             $l = self::sanitizeLangCode( $l );
00294             $obj = Language::factory( $l );
00295             $this->lang = $obj;
00296         } else {
00297             throw new MWException( __METHOD__ . " was passed an invalid type of data." );
00298         }
00299     }
00300 
00305     public function getLang() {
00306         wfDeprecated( __METHOD__, '1.19' );
00307 
00308         return $this->getLanguage();
00309     }
00310 
00318     public function getLanguage() {
00319         if ( isset( $this->recursion ) ) {
00320             trigger_error( "Recursion detected in " . __METHOD__, E_USER_WARNING );
00321             $e = new Exception;
00322             wfDebugLog( 'recursion-guard', "Recursion detected:\n" . $e->getTraceAsString() );
00323 
00324             global $wgLanguageCode;
00325             $code = ( $wgLanguageCode ) ? $wgLanguageCode : 'en';
00326             $this->lang = Language::factory( $code );
00327         } elseif ( $this->lang === null ) {
00328             $this->recursion = true;
00329 
00330             global $wgLanguageCode, $wgContLang;
00331 
00332             $request = $this->getRequest();
00333             $user = $this->getUser();
00334 
00335             $code = $request->getVal( 'uselang', $user->getOption( 'language' ) );
00336             $code = self::sanitizeLangCode( $code );
00337 
00338             wfRunHooks( 'UserGetLanguageObject', array( $user, &$code, $this ) );
00339 
00340             if ( $code === $wgLanguageCode ) {
00341                 $this->lang = $wgContLang;
00342             } else {
00343                 $obj = Language::factory( $code );
00344                 $this->lang = $obj;
00345             }
00346 
00347             unset( $this->recursion );
00348         }
00349 
00350         return $this->lang;
00351     }
00352 
00358     public function setSkin( Skin $s ) {
00359         $this->skin = clone $s;
00360         $this->skin->setContext( $this );
00361     }
00362 
00368     public function getSkin() {
00369         if ( $this->skin === null ) {
00370             wfProfileIn( __METHOD__ . '-createskin' );
00371 
00372             $skin = null;
00373             wfRunHooks( 'RequestContextCreateSkin', array( $this, &$skin ) );
00374 
00375             // If the hook worked try to set a skin from it
00376             if ( $skin instanceof Skin ) {
00377                 $this->skin = $skin;
00378             } elseif ( is_string( $skin ) ) {
00379                 $this->skin = Skin::newFromKey( $skin );
00380             }
00381 
00382             // If this is still null (the hook didn't run or didn't work)
00383             // then go through the normal processing to load a skin
00384             if ( $this->skin === null ) {
00385                 global $wgHiddenPrefs;
00386                 if ( !in_array( 'skin', $wgHiddenPrefs ) ) {
00387                     # get the user skin
00388                     $userSkin = $this->getUser()->getOption( 'skin' );
00389                     $userSkin = $this->getRequest()->getVal( 'useskin', $userSkin );
00390                 } else {
00391                     # if we're not allowing users to override, then use the default
00392                     global $wgDefaultSkin;
00393                     $userSkin = $wgDefaultSkin;
00394                 }
00395 
00396                 $this->skin = Skin::newFromKey( $userSkin );
00397             }
00398 
00399             // After all that set a context on whatever skin got created
00400             $this->skin->setContext( $this );
00401             wfProfileOut( __METHOD__ . '-createskin' );
00402         }
00403 
00404         return $this->skin;
00405     }
00406 
00415     public function msg() {
00416         $args = func_get_args();
00417 
00418         return call_user_func_array( 'wfMessage', $args )->setContext( $this );
00419     }
00420 
00428     public static function getMain() {
00429         static $instance = null;
00430         if ( $instance === null ) {
00431             $instance = new self;
00432         }
00433 
00434         return $instance;
00435     }
00436 
00444     public function exportSession() {
00445         return array(
00446             'ip' => $this->getRequest()->getIP(),
00447             'headers' => $this->getRequest()->getAllHeaders(),
00448             'sessionId' => session_id(),
00449             'userId' => $this->getUser()->getId()
00450         );
00451     }
00452 
00469     public static function importScopedSession( array $params ) {
00470         if ( PHP_SAPI !== 'cli' ) {
00471             // Don't send random private cookies or turn $wgRequest into FauxRequest
00472             throw new MWException( "Sessions can only be imported in cli mode." );
00473         } elseif ( !strlen( $params['sessionId'] ) ) {
00474             throw new MWException( "No session ID was specified." );
00475         }
00476 
00477         if ( $params['userId'] ) { // logged-in user
00478             $user = User::newFromId( $params['userId'] );
00479             if ( !$user ) {
00480                 throw new MWException( "No user with ID '{$params['userId']}'." );
00481             }
00482         } elseif ( !IP::isValid( $params['ip'] ) ) {
00483             throw new MWException( "Could not load user '{$params['ip']}'." );
00484         } else { // anon user
00485             $user = User::newFromName( $params['ip'], false );
00486         }
00487 
00488         $importSessionFunction = function ( User $user, array $params ) {
00489             global $wgRequest, $wgUser;
00490 
00491             $context = RequestContext::getMain();
00492             // Commit and close any current session
00493             session_write_close(); // persist
00494             session_id( '' ); // detach
00495             $_SESSION = array(); // clear in-memory array
00496             // Remove any user IP or agent information
00497             $context->setRequest( new FauxRequest() );
00498             $wgRequest = $context->getRequest(); // b/c
00499             // Now that all private information is detached from the user, it should
00500             // be safe to load the new user. If errors occur or an exception is thrown
00501             // and caught (leaving the main context in a mixed state), there is no risk
00502             // of the User object being attached to the wrong IP, headers, or session.
00503             $context->setUser( $user );
00504             $wgUser = $context->getUser(); // b/c
00505             if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID
00506                 wfSetupSession( $params['sessionId'] ); // sets $_SESSION
00507             }
00508             $request = new FauxRequest( array(), false, $_SESSION );
00509             $request->setIP( $params['ip'] );
00510             foreach ( $params['headers'] as $name => $value ) {
00511                 $request->setHeader( $name, $value );
00512             }
00513             // Set the current context to use the new WebRequest
00514             $context->setRequest( $request );
00515             $wgRequest = $context->getRequest(); // b/c
00516         };
00517 
00518         // Stash the old session and load in the new one
00519         $oUser = self::getMain()->getUser();
00520         $oParams = self::getMain()->exportSession();
00521         $importSessionFunction( $user, $params );
00522 
00523         // Set callback to save and close the new session and reload the old one
00524         return new ScopedCallback( function () use ( $importSessionFunction, $oUser, $oParams ) {
00525             $importSessionFunction( $oUser, $oParams );
00526         } );
00527     }
00528 
00543     public static function newExtraneousContext( Title $title, $request = array() ) {
00544         $context = new self;
00545         $context->setTitle( $title );
00546         if ( $request instanceof WebRequest ) {
00547             $context->setRequest( $request );
00548         } else {
00549             $context->setRequest( new FauxRequest( $request ) );
00550         }
00551         $context->user = User::newFromName( '127.0.0.1', false );
00552 
00553         return $context;
00554     }
00555 }