MediaWiki
REL1_21
|
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( Title $t ) { 00094 $this->title = $t; 00095 // Erase the WikiPage so a new one with the new title gets created. 00096 $this->wikipage = null; 00097 } 00098 00104 public function getTitle() { 00105 if ( $this->title === null ) { 00106 global $wgTitle; # fallback to $wg till we can improve this 00107 $this->title = $wgTitle; 00108 } 00109 return $this->title; 00110 } 00111 00120 public function canUseWikiPage() { 00121 if ( $this->wikipage !== null ) { 00122 # If there's a WikiPage object set, we can for sure get it 00123 return true; 00124 } 00125 $title = $this->getTitle(); 00126 if ( $title === null ) { 00127 # No Title, no WikiPage 00128 return false; 00129 } else { 00130 # Only namespaces whose pages are stored in the database can have WikiPage 00131 return $title->canExist(); 00132 } 00133 } 00134 00141 public function setWikiPage( WikiPage $p ) { 00142 $contextTitle = $this->getTitle(); 00143 $pageTitle = $p->getTitle(); 00144 if ( !$contextTitle || !$pageTitle->equals( $contextTitle ) ) { 00145 $this->setTitle( $pageTitle ); 00146 } 00147 // Defer this to the end since setTitle sets it to null. 00148 $this->wikipage = $p; 00149 } 00150 00161 public function getWikiPage() { 00162 if ( $this->wikipage === null ) { 00163 $title = $this->getTitle(); 00164 if ( $title === null ) { 00165 throw new MWException( __METHOD__ . ' called without Title object set' ); 00166 } 00167 $this->wikipage = WikiPage::factory( $title ); 00168 } 00169 return $this->wikipage; 00170 } 00171 00175 public function setOutput( OutputPage $o ) { 00176 $this->output = $o; 00177 } 00178 00184 public function getOutput() { 00185 if ( $this->output === null ) { 00186 $this->output = new OutputPage( $this ); 00187 } 00188 return $this->output; 00189 } 00190 00196 public function setUser( User $u ) { 00197 $this->user = $u; 00198 } 00199 00205 public function getUser() { 00206 if ( $this->user === null ) { 00207 $this->user = User::newFromSession( $this->getRequest() ); 00208 } 00209 return $this->user; 00210 } 00211 00218 public static function sanitizeLangCode( $code ) { 00219 global $wgLanguageCode; 00220 00221 // BCP 47 - letter case MUST NOT carry meaning 00222 $code = strtolower( $code ); 00223 00224 # Validate $code 00225 if ( empty( $code ) || !Language::isValidCode( $code ) || ( $code === 'qqq' ) ) { 00226 wfDebug( "Invalid user language code\n" ); 00227 $code = $wgLanguageCode; 00228 } 00229 00230 return $code; 00231 } 00232 00239 public function setLang( $l ) { 00240 wfDeprecated( __METHOD__, '1.19' ); 00241 $this->setLanguage( $l ); 00242 } 00243 00251 public function setLanguage( $l ) { 00252 if ( $l instanceof Language ) { 00253 $this->lang = $l; 00254 } elseif ( is_string( $l ) ) { 00255 $l = self::sanitizeLangCode( $l ); 00256 $obj = Language::factory( $l ); 00257 $this->lang = $obj; 00258 } else { 00259 throw new MWException( __METHOD__ . " was passed an invalid type of data." ); 00260 } 00261 } 00262 00267 public function getLang() { 00268 wfDeprecated( __METHOD__, '1.19' ); 00269 return $this->getLanguage(); 00270 } 00271 00279 public function getLanguage() { 00280 if ( isset( $this->recursion ) ) { 00281 trigger_error( "Recursion detected in " . __METHOD__, E_USER_WARNING ); 00282 $e = new Exception; 00283 wfDebugLog( 'recursion-guard', "Recursion detected:\n" . $e->getTraceAsString() ); 00284 00285 global $wgLanguageCode; 00286 $code = ( $wgLanguageCode ) ? $wgLanguageCode : 'en'; 00287 $this->lang = Language::factory( $code ); 00288 } elseif ( $this->lang === null ) { 00289 $this->recursion = true; 00290 00291 global $wgLanguageCode, $wgContLang; 00292 00293 $request = $this->getRequest(); 00294 $user = $this->getUser(); 00295 00296 $code = $request->getVal( 'uselang', $user->getOption( 'language' ) ); 00297 $code = self::sanitizeLangCode( $code ); 00298 00299 wfRunHooks( 'UserGetLanguageObject', array( $user, &$code, $this ) ); 00300 00301 if ( $code === $wgLanguageCode ) { 00302 $this->lang = $wgContLang; 00303 } else { 00304 $obj = Language::factory( $code ); 00305 $this->lang = $obj; 00306 } 00307 00308 unset( $this->recursion ); 00309 } 00310 00311 return $this->lang; 00312 } 00313 00319 public function setSkin( Skin $s ) { 00320 $this->skin = clone $s; 00321 $this->skin->setContext( $this ); 00322 } 00323 00329 public function getSkin() { 00330 if ( $this->skin === null ) { 00331 wfProfileIn( __METHOD__ . '-createskin' ); 00332 00333 $skin = null; 00334 wfRunHooks( 'RequestContextCreateSkin', array( $this, &$skin ) ); 00335 00336 // If the hook worked try to set a skin from it 00337 if ( $skin instanceof Skin ) { 00338 $this->skin = $skin; 00339 } elseif ( is_string( $skin ) ) { 00340 $this->skin = Skin::newFromKey( $skin ); 00341 } 00342 00343 // If this is still null (the hook didn't run or didn't work) 00344 // then go through the normal processing to load a skin 00345 if ( $this->skin === null ) { 00346 global $wgHiddenPrefs; 00347 if ( !in_array( 'skin', $wgHiddenPrefs ) ) { 00348 # get the user skin 00349 $userSkin = $this->getUser()->getOption( 'skin' ); 00350 $userSkin = $this->getRequest()->getVal( 'useskin', $userSkin ); 00351 } else { 00352 # if we're not allowing users to override, then use the default 00353 global $wgDefaultSkin; 00354 $userSkin = $wgDefaultSkin; 00355 } 00356 00357 $this->skin = Skin::newFromKey( $userSkin ); 00358 } 00359 00360 // After all that set a context on whatever skin got created 00361 $this->skin->setContext( $this ); 00362 wfProfileOut( __METHOD__ . '-createskin' ); 00363 } 00364 return $this->skin; 00365 } 00366 00375 public function msg() { 00376 $args = func_get_args(); 00377 return call_user_func_array( 'wfMessage', $args )->setContext( $this ); 00378 } 00379 00387 public static function getMain() { 00388 static $instance = null; 00389 if ( $instance === null ) { 00390 $instance = new self; 00391 } 00392 return $instance; 00393 } 00394 00402 public function exportSession() { 00403 return array( 00404 'ip' => $this->getRequest()->getIP(), 00405 'headers' => $this->getRequest()->getAllHeaders(), 00406 'sessionId' => session_id(), 00407 'userId' => $this->getUser()->getId() 00408 ); 00409 } 00410 00425 public static function importScopedSession( array $params ) { 00426 if ( PHP_SAPI !== 'cli' ) { 00427 // Don't send random private cookies or turn $wgRequest into FauxRequest 00428 throw new MWException( "Sessions can only be imported in cli mode." ); 00429 } elseif ( !strlen( $params['sessionId'] ) ) { 00430 throw new MWException( "No session ID was specified." ); 00431 } 00432 00433 if ( $params['userId'] ) { // logged-in user 00434 $user = User::newFromId( $params['userId'] ); 00435 if ( !$user ) { 00436 throw new MWException( "No user with ID '{$params['userId']}'." ); 00437 } 00438 } elseif ( !IP::isValid( $params['ip'] ) ) { 00439 throw new MWException( "Could not load user '{$params['ip']}'." ); 00440 } else { // anon user 00441 $user = User::newFromName( $params['ip'], false ); 00442 } 00443 00444 $importSessionFunction = function( User $user, array $params ) { 00445 global $wgRequest, $wgUser; 00446 00447 $context = RequestContext::getMain(); 00448 // Commit and close any current session 00449 session_write_close(); // persist 00450 session_id( '' ); // detach 00451 $_SESSION = array(); // clear in-memory array 00452 // Remove any user IP or agent information 00453 $context->setRequest( new FauxRequest() ); 00454 $wgRequest = $context->getRequest(); // b/c 00455 // Now that all private information is detached from the user, it should 00456 // be safe to load the new user. If errors occur or an exception is thrown 00457 // and caught (leaving the main context in a mixed state), there is no risk 00458 // of the User object being attached to the wrong IP, headers, or session. 00459 $context->setUser( $user ); 00460 $wgUser = $context->getUser(); // b/c 00461 if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID 00462 wfSetupSession( $params['sessionId'] ); // sets $_SESSION 00463 } 00464 $request = new FauxRequest( array(), false, $_SESSION ); 00465 $request->setIP( $params['ip'] ); 00466 foreach ( $params['headers'] as $name => $value ) { 00467 $request->setHeader( $name, $value ); 00468 } 00469 // Set the current context to use the new WebRequest 00470 $context->setRequest( $request ); 00471 $wgRequest = $context->getRequest(); // b/c 00472 }; 00473 00474 // Stash the old session and load in the new one 00475 $oUser = self::getMain()->getUser(); 00476 $oParams = self::getMain()->exportSession(); 00477 $importSessionFunction( $user, $params ); 00478 00479 // Set callback to save and close the new session and reload the old one 00480 return new ScopedCallback( function() use ( $importSessionFunction, $oUser, $oParams ) { 00481 $importSessionFunction( $oUser, $oParams ); 00482 } ); 00483 } 00484 00499 public static function newExtraneousContext( Title $title, $request = array() ) { 00500 $context = new self; 00501 $context->setTitle( $title ); 00502 if ( $request instanceof WebRequest ) { 00503 $context->setRequest( $request ); 00504 } else { 00505 $context->setRequest( new FauxRequest( $request ) ); 00506 } 00507 $context->user = User::newFromName( '127.0.0.1', false ); 00508 return $context; 00509 } 00510 }