Issues (4122)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/context/RequestContext.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This program is free software; you can redistribute it and/or modify
4
 * it under the terms of the GNU General Public License as published by
5
 * the Free Software Foundation; either version 2 of the License, or
6
 * (at your option) any later version.
7
 *
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
 * GNU General Public License for more details.
12
 *
13
 * You should have received a copy of the GNU General Public License along
14
 * with this program; if not, write to the Free Software Foundation, Inc.,
15
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
 * http://www.gnu.org/copyleft/gpl.html
17
 *
18
 * @since 1.18
19
 *
20
 * @author Alexandre Emsenhuber
21
 * @author Daniel Friesen
22
 * @file
23
 */
24
25
use Liuggio\StatsdClient\Factory\StatsdDataFactory;
26
use MediaWiki\Logger\LoggerFactory;
27
use MediaWiki\MediaWikiServices;
28
use Wikimedia\ScopedCallback;
0 ignored issues
show
This use statement conflicts with another class in this namespace, ScopedCallback.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
29
30
/**
31
 * Group all the pieces relevant to the context of a request into one instance
32
 */
33
class RequestContext implements IContextSource, MutableContext {
34
	/**
35
	 * @var WebRequest
36
	 */
37
	private $request;
38
39
	/**
40
	 * @var Title
41
	 */
42
	private $title;
43
44
	/**
45
	 * @var WikiPage
46
	 */
47
	private $wikipage;
48
49
	/**
50
	 * @var OutputPage
51
	 */
52
	private $output;
53
54
	/**
55
	 * @var User
56
	 */
57
	private $user;
58
59
	/**
60
	 * @var Language
61
	 */
62
	private $lang;
63
64
	/**
65
	 * @var Skin
66
	 */
67
	private $skin;
68
69
	/**
70
	 * @var Timing
71
	 */
72
	private $timing;
73
74
	/**
75
	 * @var Config
76
	 */
77
	private $config;
78
79
	/**
80
	 * @var RequestContext
81
	 */
82
	private static $instance = null;
83
84
	/**
85
	 * Set the Config object
86
	 *
87
	 * @param Config $c
88
	 */
89
	public function setConfig( Config $c ) {
90
		$this->config = $c;
91
	}
92
93
	/**
94
	 * Get the Config object
95
	 *
96
	 * @return Config
97
	 */
98
	public function getConfig() {
99
		if ( $this->config === null ) {
100
			// @todo In the future, we could move this to WebStart.php so
101
			// the Config object is ready for when initialization happens
102
			$this->config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
103
		}
104
105
		return $this->config;
106
	}
107
108
	/**
109
	 * Set the WebRequest object
110
	 *
111
	 * @param WebRequest $r
112
	 */
113
	public function setRequest( WebRequest $r ) {
114
		$this->request = $r;
115
	}
116
117
	/**
118
	 * Get the WebRequest object
119
	 *
120
	 * @return WebRequest
121
	 */
122
	public function getRequest() {
123
		if ( $this->request === null ) {
124
			global $wgCommandLineMode;
125
			// create the WebRequest object on the fly
126
			if ( $wgCommandLineMode ) {
127
				$this->request = new FauxRequest( [] );
128
			} else {
129
				$this->request = new WebRequest();
130
			}
131
		}
132
133
		return $this->request;
134
	}
135
136
	/**
137
	 * Get the Stats object
138
	 *
139
	 * @deprecated since 1.27 use a StatsdDataFactory from MediaWikiServices (preferably injected)
140
	 *
141
	 * @return StatsdDataFactory
142
	 */
143
	public function getStats() {
144
		return MediaWikiServices::getInstance()->getStatsdDataFactory();
145
	}
146
147
	/**
148
	 * Get the timing object
149
	 *
150
	 * @return Timing
151
	 */
152
	public function getTiming() {
153
		if ( $this->timing === null ) {
154
			$this->timing = new Timing( [
155
				'logger' => LoggerFactory::getInstance( 'Timing' )
156
			] );
157
		}
158
		return $this->timing;
159
	}
160
161
	/**
162
	 * Set the Title object
163
	 *
164
	 * @param Title|null $title
165
	 */
166
	public function setTitle( Title $title = null ) {
167
		$this->title = $title;
168
		// Erase the WikiPage so a new one with the new title gets created.
169
		$this->wikipage = null;
170
	}
171
172
	/**
173
	 * Get the Title object
174
	 *
175
	 * @return Title|null
176
	 */
177
	public function getTitle() {
178
		if ( $this->title === null ) {
179
			global $wgTitle; # fallback to $wg till we can improve this
180
			$this->title = $wgTitle;
181
			wfDebugLog(
182
				'GlobalTitleFail',
183
				__METHOD__ . ' called by ' . wfGetAllCallers( 5 ) . ' with no title set.'
184
			);
185
		}
186
187
		return $this->title;
188
	}
189
190
	/**
191
	 * Check, if a Title object is set
192
	 *
193
	 * @since 1.25
194
	 * @return bool
195
	 */
196
	public function hasTitle() {
197
		return $this->title !== null;
198
	}
199
200
	/**
201
	 * Check whether a WikiPage object can be get with getWikiPage().
202
	 * Callers should expect that an exception is thrown from getWikiPage()
203
	 * if this method returns false.
204
	 *
205
	 * @since 1.19
206
	 * @return bool
207
	 */
208
	public function canUseWikiPage() {
209
		if ( $this->wikipage ) {
210
			// If there's a WikiPage object set, we can for sure get it
211
			return true;
212
		}
213
		// Only pages with legitimate titles can have WikiPages.
214
		// That usually means pages in non-virtual namespaces.
215
		$title = $this->getTitle();
216
		return $title ? $title->canExist() : false;
217
	}
218
219
	/**
220
	 * Set the WikiPage object
221
	 *
222
	 * @since 1.19
223
	 * @param WikiPage $p
224
	 */
225
	public function setWikiPage( WikiPage $p ) {
226
		$pageTitle = $p->getTitle();
227
		if ( !$this->hasTitle() || !$pageTitle->equals( $this->getTitle() ) ) {
0 ignored issues
show
It seems like $this->getTitle() can be null; however, equals() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
228
			$this->setTitle( $pageTitle );
229
		}
230
		// Defer this to the end since setTitle sets it to null.
231
		$this->wikipage = $p;
232
	}
233
234
	/**
235
	 * Get the WikiPage object.
236
	 * May throw an exception if there's no Title object set or the Title object
237
	 * belongs to a special namespace that doesn't have WikiPage, so use first
238
	 * canUseWikiPage() to check whether this method can be called safely.
239
	 *
240
	 * @since 1.19
241
	 * @throws MWException
242
	 * @return WikiPage
243
	 */
244
	public function getWikiPage() {
245
		if ( $this->wikipage === null ) {
246
			$title = $this->getTitle();
247
			if ( $title === null ) {
248
				throw new MWException( __METHOD__ . ' called without Title object set' );
249
			}
250
			$this->wikipage = WikiPage::factory( $title );
251
		}
252
253
		return $this->wikipage;
254
	}
255
256
	/**
257
	 * @param OutputPage $o
258
	 */
259
	public function setOutput( OutputPage $o ) {
260
		$this->output = $o;
261
	}
262
263
	/**
264
	 * Get the OutputPage object
265
	 *
266
	 * @return OutputPage
267
	 */
268
	public function getOutput() {
269
		if ( $this->output === null ) {
270
			$this->output = new OutputPage( $this );
271
		}
272
273
		return $this->output;
274
	}
275
276
	/**
277
	 * Set the User object
278
	 *
279
	 * @param User $u
280
	 */
281
	public function setUser( User $u ) {
282
		$this->user = $u;
283
	}
284
285
	/**
286
	 * Get the User object
287
	 *
288
	 * @return User
289
	 */
290
	public function getUser() {
291
		if ( $this->user === null ) {
292
			$this->user = User::newFromSession( $this->getRequest() );
293
		}
294
295
		return $this->user;
296
	}
297
298
	/**
299
	 * Accepts a language code and ensures it's sane. Outputs a cleaned up language
300
	 * code and replaces with $wgLanguageCode if not sane.
301
	 * @param string $code Language code
302
	 * @return string
303
	 */
304
	public static function sanitizeLangCode( $code ) {
305
		global $wgLanguageCode;
306
307
		// BCP 47 - letter case MUST NOT carry meaning
308
		$code = strtolower( $code );
309
310
		# Validate $code
311
		if ( !$code || !Language::isValidCode( $code ) || $code === 'qqq' ) {
312
			wfDebug( "Invalid user language code\n" );
313
			$code = $wgLanguageCode;
314
		}
315
316
		return $code;
317
	}
318
319
	/**
320
	 * Set the Language object
321
	 *
322
	 * @param Language|string $l Language instance or language code
323
	 * @throws MWException
324
	 * @since 1.19
325
	 */
326 View Code Duplication
	public function setLanguage( $l ) {
327
		if ( $l instanceof Language ) {
328
			$this->lang = $l;
329
		} elseif ( is_string( $l ) ) {
330
			$l = self::sanitizeLangCode( $l );
331
			$obj = Language::factory( $l );
332
			$this->lang = $obj;
333
		} else {
334
			throw new MWException( __METHOD__ . " was passed an invalid type of data." );
335
		}
336
	}
337
338
	/**
339
	 * Get the Language object.
340
	 * Initialization of user or request objects can depend on this.
341
	 * @return Language
342
	 * @throws Exception
343
	 * @since 1.19
344
	 */
345
	public function getLanguage() {
346
		if ( isset( $this->recursion ) ) {
347
			trigger_error( "Recursion detected in " . __METHOD__, E_USER_WARNING );
348
			$e = new Exception;
349
			wfDebugLog( 'recursion-guard', "Recursion detected:\n" . $e->getTraceAsString() );
350
351
			$code = $this->getConfig()->get( 'LanguageCode' ) ?: 'en';
352
			$this->lang = Language::factory( $code );
353
		} elseif ( $this->lang === null ) {
354
			$this->recursion = true;
0 ignored issues
show
The property recursion does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
355
356
			global $wgContLang;
357
358
			try {
359
				$request = $this->getRequest();
360
				$user = $this->getUser();
361
362
				$code = $request->getVal( 'uselang', 'user' );
363
				if ( $code === 'user' ) {
364
					$code = $user->getOption( 'language' );
365
				}
366
				$code = self::sanitizeLangCode( $code );
367
368
				Hooks::run( 'UserGetLanguageObject', [ $user, &$code, $this ] );
369
370
				if ( $code === $this->getConfig()->get( 'LanguageCode' ) ) {
371
					$this->lang = $wgContLang;
372
				} else {
373
					$obj = Language::factory( $code );
374
					$this->lang = $obj;
375
				}
376
377
				unset( $this->recursion );
378
			}
379
			catch ( Exception $ex ) {
380
				unset( $this->recursion );
381
				throw $ex;
382
			}
383
		}
384
385
		return $this->lang;
386
	}
387
388
	/**
389
	 * Set the Skin object
390
	 *
391
	 * @param Skin $s
392
	 */
393
	public function setSkin( Skin $s ) {
394
		$this->skin = clone $s;
395
		$this->skin->setContext( $this );
396
	}
397
398
	/**
399
	 * Get the Skin object
400
	 *
401
	 * @return Skin
402
	 */
403
	public function getSkin() {
404
		if ( $this->skin === null ) {
405
			$skin = null;
406
			Hooks::run( 'RequestContextCreateSkin', [ $this, &$skin ] );
407
			$factory = SkinFactory::getDefaultInstance();
408
409
			// If the hook worked try to set a skin from it
410
			if ( $skin instanceof Skin ) {
411
				$this->skin = $skin;
412
			} elseif ( is_string( $skin ) ) {
413
				// Normalize the key, just in case the hook did something weird.
414
				$normalized = Skin::normalizeKey( $skin );
415
				$this->skin = $factory->makeSkin( $normalized );
416
			}
417
418
			// If this is still null (the hook didn't run or didn't work)
419
			// then go through the normal processing to load a skin
420
			if ( $this->skin === null ) {
421
				if ( !in_array( 'skin', $this->getConfig()->get( 'HiddenPrefs' ) ) ) {
422
					# get the user skin
423
					$userSkin = $this->getUser()->getOption( 'skin' );
424
					$userSkin = $this->getRequest()->getVal( 'useskin', $userSkin );
425
				} else {
426
					# if we're not allowing users to override, then use the default
427
					$userSkin = $this->getConfig()->get( 'DefaultSkin' );
428
				}
429
430
				// Normalize the key in case the user is passing gibberish
431
				// or has old preferences (bug 69566).
432
				$normalized = Skin::normalizeKey( $userSkin );
433
434
				// Skin::normalizeKey will also validate it, so
435
				// this won't throw an exception
436
				$this->skin = $factory->makeSkin( $normalized );
437
			}
438
439
			// After all that set a context on whatever skin got created
440
			$this->skin->setContext( $this );
441
		}
442
443
		return $this->skin;
444
	}
445
446
	/** Helpful methods **/
447
448
	/**
449
	 * Get a Message object with context set
450
	 * Parameters are the same as wfMessage()
451
	 *
452
	 * @param mixed ...
453
	 * @return Message
454
	 */
455
	public function msg() {
456
		$args = func_get_args();
457
458
		return call_user_func_array( 'wfMessage', $args )->setContext( $this );
459
	}
460
461
	/** Static methods **/
462
463
	/**
464
	 * Get the RequestContext object associated with the main request
465
	 *
466
	 * @return RequestContext
467
	 */
468
	public static function getMain() {
469
		if ( self::$instance === null ) {
470
			self::$instance = new self;
471
		}
472
473
		return self::$instance;
474
	}
475
476
	/**
477
	 * Get the RequestContext object associated with the main request
478
	 * and gives a warning to the log, to find places, where a context maybe is missing.
479
	 *
480
	 * @param string $func
481
	 * @return RequestContext
482
	 * @since 1.24
483
	 */
484
	public static function getMainAndWarn( $func = __METHOD__ ) {
485
		wfDebug( $func . ' called without context. ' .
486
			"Using RequestContext::getMain() for sanity\n" );
487
488
		return self::getMain();
489
	}
490
491
	/**
492
	 * Resets singleton returned by getMain(). Should be called only from unit tests.
493
	 */
494
	public static function resetMain() {
495
		if ( !( defined( 'MW_PHPUNIT_TEST' ) || defined( 'MW_PARSER_TEST' ) ) ) {
496
			throw new MWException( __METHOD__ . '() should be called only from unit tests!' );
497
		}
498
		self::$instance = null;
499
	}
500
501
	/**
502
	 * Export the resolved user IP, HTTP headers, user ID, and session ID.
503
	 * The result will be reasonably sized to allow for serialization.
504
	 *
505
	 * @return array
506
	 * @since 1.21
507
	 */
508
	public function exportSession() {
509
		$session = MediaWiki\Session\SessionManager::getGlobalSession();
510
		return [
511
			'ip' => $this->getRequest()->getIP(),
512
			'headers' => $this->getRequest()->getAllHeaders(),
513
			'sessionId' => $session->isPersistent() ? $session->getId() : '',
514
			'userId' => $this->getUser()->getId()
515
		];
516
	}
517
518
	/**
519
	 * Import an client IP address, HTTP headers, user ID, and session ID
520
	 *
521
	 * This sets the current session, $wgUser, and $wgRequest from $params.
522
	 * Once the return value falls out of scope, the old context is restored.
523
	 * This method should only be called in contexts where there is no session
524
	 * ID or end user receiving the response (CLI or HTTP job runners). This
525
	 * is partly enforced, and is done so to avoid leaking cookies if certain
526
	 * error conditions arise.
527
	 *
528
	 * This is useful when background scripts inherit context when acting on
529
	 * behalf of a user. In general the 'sessionId' parameter should be set
530
	 * to an empty string unless session importing is *truly* needed. This
531
	 * feature is somewhat deprecated.
532
	 *
533
	 * @note suhosin.session.encrypt may interfere with this method.
534
	 *
535
	 * @param array $params Result of RequestContext::exportSession()
536
	 * @return ScopedCallback
537
	 * @throws MWException
538
	 * @since 1.21
539
	 */
540
	public static function importScopedSession( array $params ) {
0 ignored issues
show
importScopedSession uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
541
		if ( strlen( $params['sessionId'] ) &&
542
			MediaWiki\Session\SessionManager::getGlobalSession()->isPersistent()
543
		) {
544
			// Sanity check to avoid sending random cookies for the wrong users.
545
			// This method should only called by CLI scripts or by HTTP job runners.
546
			throw new MWException( "Sessions can only be imported when none is active." );
547
		} elseif ( !IP::isValid( $params['ip'] ) ) {
548
			throw new MWException( "Invalid client IP address '{$params['ip']}'." );
549
		}
550
551
		if ( $params['userId'] ) { // logged-in user
552
			$user = User::newFromId( $params['userId'] );
553
			$user->load();
554
			if ( !$user->getId() ) {
555
				throw new MWException( "No user with ID '{$params['userId']}'." );
556
			}
557
		} else { // anon user
558
			$user = User::newFromName( $params['ip'], false );
559
		}
560
561
		$importSessionFunc = function ( User $user, array $params ) {
562
			global $wgRequest, $wgUser;
563
564
			$context = RequestContext::getMain();
565
566
			// Commit and close any current session
567
			if ( MediaWiki\Session\PHPSessionHandler::isEnabled() ) {
568
				session_write_close(); // persist
569
				session_id( '' ); // detach
570
				$_SESSION = []; // clear in-memory array
571
			}
572
573
			// Get new session, if applicable
574
			$session = null;
575
			if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID
576
				$manager = MediaWiki\Session\SessionManager::singleton();
577
				$session = $manager->getSessionById( $params['sessionId'], true )
578
					?: $manager->getEmptySession();
579
			}
580
581
			// Remove any user IP or agent information, and attach the request
582
			// with the new session.
583
			$context->setRequest( new FauxRequest( [], false, $session ) );
584
			$wgRequest = $context->getRequest(); // b/c
585
586
			// Now that all private information is detached from the user, it should
587
			// be safe to load the new user. If errors occur or an exception is thrown
588
			// and caught (leaving the main context in a mixed state), there is no risk
589
			// of the User object being attached to the wrong IP, headers, or session.
590
			$context->setUser( $user );
591
			$wgUser = $context->getUser(); // b/c
592
			if ( $session && MediaWiki\Session\PHPSessionHandler::isEnabled() ) {
593
				session_id( $session->getId() );
594
				MediaWiki\quietCall( 'session_start' );
595
			}
596
			$request = new FauxRequest( [], false, $session );
597
			$request->setIP( $params['ip'] );
598
			foreach ( $params['headers'] as $name => $value ) {
599
				$request->setHeader( $name, $value );
600
			}
601
			// Set the current context to use the new WebRequest
602
			$context->setRequest( $request );
603
			$wgRequest = $context->getRequest(); // b/c
604
		};
605
606
		// Stash the old session and load in the new one
607
		$oUser = self::getMain()->getUser();
608
		$oParams = self::getMain()->exportSession();
609
		$oRequest = self::getMain()->getRequest();
610
		$importSessionFunc( $user, $params );
611
612
		// Set callback to save and close the new session and reload the old one
613
		return new ScopedCallback(
614
			function () use ( $importSessionFunc, $oUser, $oParams, $oRequest ) {
615
				global $wgRequest;
616
				$importSessionFunc( $oUser, $oParams );
617
				// Restore the exact previous Request object (instead of leaving FauxRequest)
618
				RequestContext::getMain()->setRequest( $oRequest );
619
				$wgRequest = RequestContext::getMain()->getRequest(); // b/c
620
			}
621
		);
622
	}
623
624
	/**
625
	 * Create a new extraneous context. The context is filled with information
626
	 * external to the current session.
627
	 * - Title is specified by argument
628
	 * - Request is a FauxRequest, or a FauxRequest can be specified by argument
629
	 * - User is an anonymous user, for separation IPv4 localhost is used
630
	 * - Language will be based on the anonymous user and request, may be content
631
	 *   language or a uselang param in the fauxrequest data may change the lang
632
	 * - Skin will be based on the anonymous user, should be the wiki's default skin
633
	 *
634
	 * @param Title $title Title to use for the extraneous request
635
	 * @param WebRequest|array $request A WebRequest or data to use for a FauxRequest
636
	 * @return RequestContext
637
	 */
638
	public static function newExtraneousContext( Title $title, $request = [] ) {
639
		$context = new self;
640
		$context->setTitle( $title );
641
		if ( $request instanceof WebRequest ) {
642
			$context->setRequest( $request );
643
		} else {
644
			$context->setRequest( new FauxRequest( $request ) );
645
		}
646
		$context->user = User::newFromName( '127.0.0.1', false );
0 ignored issues
show
Documentation Bug introduced by
It seems like \User::newFromName('127.0.0.1', false) can also be of type false. However, the property $user is declared as type object<User>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
647
648
		return $context;
649
	}
650
}
651