MediaWiki  REL1_21
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( 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 }