MediaWiki
REL1_22
|
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 }