MediaWiki
REL1_24
|
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 00074 private static $instance = null; 00075 00081 public function setConfig( Config $c ) { 00082 $this->config = $c; 00083 } 00084 00090 public function getConfig() { 00091 if ( $this->config === null ) { 00092 // @todo In the future, we could move this to WebStart.php so 00093 // the Config object is ready for when initialization happens 00094 $this->config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' ); 00095 } 00096 00097 return $this->config; 00098 } 00099 00105 public function setRequest( WebRequest $r ) { 00106 $this->request = $r; 00107 } 00108 00114 public function getRequest() { 00115 if ( $this->request === null ) { 00116 global $wgRequest; # fallback to $wg till we can improve this 00117 $this->request = $wgRequest; 00118 } 00119 00120 return $this->request; 00121 } 00122 00128 public function setTitle( Title $title ) { 00129 $this->title = $title; 00130 // Erase the WikiPage so a new one with the new title gets created. 00131 $this->wikipage = null; 00132 } 00133 00139 public function getTitle() { 00140 if ( $this->title === null ) { 00141 global $wgTitle; # fallback to $wg till we can improve this 00142 $this->title = $wgTitle; 00143 } 00144 00145 return $this->title; 00146 } 00147 00156 public function canUseWikiPage() { 00157 if ( $this->wikipage ) { 00158 // If there's a WikiPage object set, we can for sure get it 00159 return true; 00160 } 00161 // Only pages with legitimate titles can have WikiPages. 00162 // That usually means pages in non-virtual namespaces. 00163 $title = $this->getTitle(); 00164 return $title ? $title->canExist() : false; 00165 } 00166 00173 public function setWikiPage( WikiPage $p ) { 00174 $contextTitle = $this->getTitle(); 00175 $pageTitle = $p->getTitle(); 00176 if ( !$contextTitle || !$pageTitle->equals( $contextTitle ) ) { 00177 $this->setTitle( $pageTitle ); 00178 } 00179 // Defer this to the end since setTitle sets it to null. 00180 $this->wikipage = $p; 00181 } 00182 00193 public function getWikiPage() { 00194 if ( $this->wikipage === null ) { 00195 $title = $this->getTitle(); 00196 if ( $title === null ) { 00197 throw new MWException( __METHOD__ . ' called without Title object set' ); 00198 } 00199 $this->wikipage = WikiPage::factory( $title ); 00200 } 00201 00202 return $this->wikipage; 00203 } 00204 00208 public function setOutput( OutputPage $o ) { 00209 $this->output = $o; 00210 } 00211 00217 public function getOutput() { 00218 if ( $this->output === null ) { 00219 $this->output = new OutputPage( $this ); 00220 } 00221 00222 return $this->output; 00223 } 00224 00230 public function setUser( User $u ) { 00231 $this->user = $u; 00232 } 00233 00239 public function getUser() { 00240 if ( $this->user === null ) { 00241 $this->user = User::newFromSession( $this->getRequest() ); 00242 } 00243 00244 return $this->user; 00245 } 00246 00253 public static function sanitizeLangCode( $code ) { 00254 global $wgLanguageCode; 00255 00256 // BCP 47 - letter case MUST NOT carry meaning 00257 $code = strtolower( $code ); 00258 00259 # Validate $code 00260 if ( !$code || !Language::isValidCode( $code ) || $code === 'qqq' ) { 00261 wfDebug( "Invalid user language code\n" ); 00262 $code = $wgLanguageCode; 00263 } 00264 00265 return $code; 00266 } 00267 00275 public function setLanguage( $l ) { 00276 if ( $l instanceof Language ) { 00277 $this->lang = $l; 00278 } elseif ( is_string( $l ) ) { 00279 $l = self::sanitizeLangCode( $l ); 00280 $obj = Language::factory( $l ); 00281 $this->lang = $obj; 00282 } else { 00283 throw new MWException( __METHOD__ . " was passed an invalid type of data." ); 00284 } 00285 } 00286 00294 public function getLanguage() { 00295 if ( isset( $this->recursion ) ) { 00296 trigger_error( "Recursion detected in " . __METHOD__, E_USER_WARNING ); 00297 $e = new Exception; 00298 wfDebugLog( 'recursion-guard', "Recursion detected:\n" . $e->getTraceAsString() ); 00299 00300 $code = $this->getConfig()->get( 'LanguageCode' ) ?: 'en'; 00301 $this->lang = Language::factory( $code ); 00302 } elseif ( $this->lang === null ) { 00303 $this->recursion = true; 00304 00305 global $wgContLang; 00306 00307 try { 00308 $request = $this->getRequest(); 00309 $user = $this->getUser(); 00310 00311 $code = $request->getVal( 'uselang', $user->getOption( 'language' ) ); 00312 $code = self::sanitizeLangCode( $code ); 00313 00314 wfRunHooks( 'UserGetLanguageObject', array( $user, &$code, $this ) ); 00315 00316 if ( $code === $this->getConfig()->get( 'LanguageCode' ) ) { 00317 $this->lang = $wgContLang; 00318 } else { 00319 $obj = Language::factory( $code ); 00320 $this->lang = $obj; 00321 } 00322 00323 unset( $this->recursion ); 00324 } 00325 catch ( Exception $ex ) { 00326 unset( $this->recursion ); 00327 throw $ex; 00328 } 00329 } 00330 00331 return $this->lang; 00332 } 00333 00339 public function setSkin( Skin $s ) { 00340 $this->skin = clone $s; 00341 $this->skin->setContext( $this ); 00342 } 00343 00349 public function getSkin() { 00350 if ( $this->skin === null ) { 00351 wfProfileIn( __METHOD__ . '-createskin' ); 00352 00353 $skin = null; 00354 wfRunHooks( 'RequestContextCreateSkin', array( $this, &$skin ) ); 00355 $factory = SkinFactory::getDefaultInstance(); 00356 00357 // If the hook worked try to set a skin from it 00358 if ( $skin instanceof Skin ) { 00359 $this->skin = $skin; 00360 } elseif ( is_string( $skin ) ) { 00361 // Normalize the key, just in case the hook did something weird. 00362 $normalized = Skin::normalizeKey( $skin ); 00363 $this->skin = $factory->makeSkin( $normalized ); 00364 } 00365 00366 // If this is still null (the hook didn't run or didn't work) 00367 // then go through the normal processing to load a skin 00368 if ( $this->skin === null ) { 00369 if ( !in_array( 'skin', $this->getConfig()->get( 'HiddenPrefs' ) ) ) { 00370 # get the user skin 00371 $userSkin = $this->getUser()->getOption( 'skin' ); 00372 $userSkin = $this->getRequest()->getVal( 'useskin', $userSkin ); 00373 } else { 00374 # if we're not allowing users to override, then use the default 00375 $userSkin = $this->getConfig()->get( 'DefaultSkin' ); 00376 } 00377 00378 // Normalize the key in case the user is passing gibberish 00379 // or has old preferences (bug 69566). 00380 $normalized = Skin::normalizeKey( $userSkin ); 00381 00382 // Skin::normalizeKey will also validate it, so 00383 // this won't throw an exception 00384 $this->skin = $factory->makeSkin( $normalized ); 00385 } 00386 00387 // After all that set a context on whatever skin got created 00388 $this->skin->setContext( $this ); 00389 wfProfileOut( __METHOD__ . '-createskin' ); 00390 } 00391 00392 return $this->skin; 00393 } 00394 00403 public function msg() { 00404 $args = func_get_args(); 00405 00406 return call_user_func_array( 'wfMessage', $args )->setContext( $this ); 00407 } 00408 00416 public static function getMain() { 00417 if ( self::$instance === null ) { 00418 self::$instance = new self; 00419 } 00420 00421 return self::$instance; 00422 } 00423 00432 public static function getMainAndWarn( $func = __METHOD__ ) { 00433 wfDebug( $func . ' called without context. ' . 00434 "Using RequestContext::getMain() for sanity\n" ); 00435 00436 return self::getMain(); 00437 } 00438 00442 public static function resetMain() { 00443 if ( !defined( 'MW_PHPUNIT_TEST' ) ) { 00444 throw new MWException( __METHOD__ . '() should be called only from unit tests!' ); 00445 } 00446 self::$instance = null; 00447 } 00448 00456 public function exportSession() { 00457 return array( 00458 'ip' => $this->getRequest()->getIP(), 00459 'headers' => $this->getRequest()->getAllHeaders(), 00460 'sessionId' => session_id(), 00461 'userId' => $this->getUser()->getId() 00462 ); 00463 } 00464 00481 public static function importScopedSession( array $params ) { 00482 if ( PHP_SAPI !== 'cli' ) { 00483 // Don't send random private cookies or turn $wgRequest into FauxRequest 00484 throw new MWException( "Sessions can only be imported in cli mode." ); 00485 } elseif ( !strlen( $params['sessionId'] ) ) { 00486 throw new MWException( "No session ID was specified." ); 00487 } 00488 00489 if ( $params['userId'] ) { // logged-in user 00490 $user = User::newFromId( $params['userId'] ); 00491 $user->load(); 00492 if ( !$user->getId() ) { 00493 throw new MWException( "No user with ID '{$params['userId']}'." ); 00494 } 00495 } elseif ( !IP::isValid( $params['ip'] ) ) { 00496 throw new MWException( "Could not load user '{$params['ip']}'." ); 00497 } else { // anon user 00498 $user = User::newFromName( $params['ip'], false ); 00499 } 00500 00501 $importSessionFunction = function ( User $user, array $params ) { 00502 global $wgRequest, $wgUser; 00503 00504 $context = RequestContext::getMain(); 00505 // Commit and close any current session 00506 session_write_close(); // persist 00507 session_id( '' ); // detach 00508 $_SESSION = array(); // clear in-memory array 00509 // Remove any user IP or agent information 00510 $context->setRequest( new FauxRequest() ); 00511 $wgRequest = $context->getRequest(); // b/c 00512 // Now that all private information is detached from the user, it should 00513 // be safe to load the new user. If errors occur or an exception is thrown 00514 // and caught (leaving the main context in a mixed state), there is no risk 00515 // of the User object being attached to the wrong IP, headers, or session. 00516 $context->setUser( $user ); 00517 $wgUser = $context->getUser(); // b/c 00518 if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID 00519 wfSetupSession( $params['sessionId'] ); // sets $_SESSION 00520 } 00521 $request = new FauxRequest( array(), false, $_SESSION ); 00522 $request->setIP( $params['ip'] ); 00523 foreach ( $params['headers'] as $name => $value ) { 00524 $request->setHeader( $name, $value ); 00525 } 00526 // Set the current context to use the new WebRequest 00527 $context->setRequest( $request ); 00528 $wgRequest = $context->getRequest(); // b/c 00529 }; 00530 00531 // Stash the old session and load in the new one 00532 $oUser = self::getMain()->getUser(); 00533 $oParams = self::getMain()->exportSession(); 00534 $importSessionFunction( $user, $params ); 00535 00536 // Set callback to save and close the new session and reload the old one 00537 return new ScopedCallback( function () use ( $importSessionFunction, $oUser, $oParams ) { 00538 $importSessionFunction( $oUser, $oParams ); 00539 } ); 00540 } 00541 00556 public static function newExtraneousContext( Title $title, $request = array() ) { 00557 $context = new self; 00558 $context->setTitle( $title ); 00559 if ( $request instanceof WebRequest ) { 00560 $context->setRequest( $request ); 00561 } else { 00562 $context->setRequest( new FauxRequest( $request ) ); 00563 } 00564 $context->user = User::newFromName( '127.0.0.1', false ); 00565 00566 return $context; 00567 } 00568 }