Go to the documentation of this file.
30 use Wikimedia\AtEase\AtEase;
117 $this->requestTime = $_SERVER[
'REQUEST_TIME_FLOAT'];
121 $this->data = $_POST + $_GET;
123 $this->queryAndPathParams = $this->queryParams = $_GET;
145 if ( isset( $_SERVER[
'REQUEST_URI'] ) ) {
147 $url = $_SERVER[
'REQUEST_URI'];
148 if ( !preg_match(
'!^https?://!', $url ) ) {
149 $url =
'http://unused' . $url;
151 AtEase::suppressWarnings();
152 $a = parse_url( $url );
153 AtEase::restoreWarnings();
157 $path = $a[
'path'] ??
'';
169 $router->
add(
"$wgScript/$1" );
171 if ( isset( $_SERVER[
'SCRIPT_NAME'] )
172 && strpos( $_SERVER[
'SCRIPT_NAME'],
'.php' ) !==
false
177 $router->add( $_SERVER[
'SCRIPT_NAME'] .
"/$1" );
187 if ( $articlePaths ) {
188 $router->add( $articlePaths, [
'action' =>
'$key' ] );
194 [
'variant' =>
'$2' ],
195 [
'$2' => MediaWikiServices::getInstance()->getContentLanguage()->
200 Hooks::run(
'WebRequestPathInfoRouter', [ $router ] );
207 if ( !empty( $_SERVER[
'ORIG_PATH_INFO'] ) ) {
211 $matches[
'title'] = substr( $_SERVER[
'ORIG_PATH_INFO'], 1 );
212 } elseif ( !empty( $_SERVER[
'PATH_INFO'] ) ) {
214 $matches[
'title'] = substr( $_SERVER[
'PATH_INFO'], 1 );
232 $stdPort = $proto ===
'https' ? 443 : 80;
234 $varNames = [
'HTTP_HOST',
'SERVER_NAME',
'HOSTNAME',
'SERVER_ADDR' ];
237 foreach ( $varNames as $varName ) {
238 if ( !isset( $_SERVER[$varName] ) ) {
254 } elseif ( $parts[1] ===
false ) {
255 if ( isset( $_SERVER[
'SERVER_PORT'] ) ) {
256 $port = $_SERVER[
'SERVER_PORT'];
275 if ( ( !empty( $_SERVER[
'HTTPS'] ) && $_SERVER[
'HTTPS'] !==
'off' ) ||
276 ( isset( $_SERVER[
'HTTP_X_FORWARDED_PROTO'] ) &&
277 $_SERVER[
'HTTP_X_FORWARDED_PROTO'] ===
'https' ) ) {
306 if ( self::$reqId ) {
339 if ( $this->protocol ===
null ) {
354 if ( defined(
'MW_API' ) ) {
359 foreach (
$matches as $key => $val ) {
360 $this->data[$key] = $this->queryAndPathParams[$key] = $val;
375 foreach ( (array)$bases as $keyValue =>
$base ) {
378 $baseLen = strlen(
$base );
379 if ( substr(
$path, 0, $baseLen ) ==
$base ) {
380 $raw = substr(
$path, $baseLen );
382 $matches = [
'title' => rawurldecode( $raw ) ];
401 if ( is_array(
$data ) ) {
402 foreach (
$data as $key => $val ) {
406 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
407 $data = $contLang ? $contLang->normalize(
$data ) :
408 UtfNormal\Validator::cleanUp(
$data );
422 # PHP is so nice to not touch input data, except sometimes:
423 # https://www.php.net/variables.external#language.variables.external.dot-in-names
424 # Work around PHP *feature* to avoid *bugs* elsewhere.
425 $name = strtr( $name,
'.',
'_' );
427 if ( !isset( $arr[$name] ) ) {
432 # Optimisation: Skip UTF-8 normalization and legacy transcoding for simple ASCII strings.
433 $isAsciiStr = ( is_string(
$data ) && preg_match(
'/[^\x20-\x7E]/',
$data ) === 0 );
434 if ( !$isAsciiStr ) {
435 if ( isset( $_GET[$name] ) && is_string(
$data ) ) {
436 # Check for alternate/legacy character encoding.
437 $data = MediaWikiServices::getInstance()
438 ->getContentLanguage()
439 ->checkTitleEncoding(
$data );
460 $name = strtr( $name,
'.',
'_' );
461 if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
462 $val = $this->data[$name];
466 if ( is_null( $val ) ) {
483 public function getVal( $name, $default =
null ) {
484 $val = $this->
getGPCVal( $this->data, $name, $default );
485 if ( is_array( $val ) ) {
488 if ( is_null( $val ) ) {
503 $ret = $this->data[$key] ??
null;
504 $this->data[$key] = $value;
515 if ( !isset( $this->data[$key] ) ) {
518 $ret = $this->data[$key];
519 unset( $this->data[$key] );
533 public function getArray( $name, $default =
null ) {
534 $val = $this->
getGPCVal( $this->data, $name, $default );
535 if ( is_null( $val ) ) {
553 $val = $this->
getArray( $name, $default );
554 if ( is_array( $val ) ) {
555 $val = array_map(
'intval', $val );
569 public function getInt( $name, $default = 0 ) {
570 return intval( $this->
getRawVal( $name, $default ) );
583 return is_numeric( $val )
598 public function getFloat( $name, $default = 0.0 ) {
599 return floatval( $this->
getRawVal( $name, $default ) );
611 public function getBool( $name, $default =
false ) {
612 return (
bool)$this->
getRawVal( $name, $default );
625 return $this->
getBool( $name, $default )
626 && strcasecmp( $this->
getRawVal( $name ),
'false' ) !== 0;
638 # Checkboxes and buttons are only present when clicked
639 # Presence connotes truth, absence false
640 return $this->
getRawVal( $name,
null ) !==
null;
653 public function getText( $name, $default =
'' ) {
654 $val = $this->
getVal( $name, $default );
655 return str_replace(
"\r\n",
"\n", $val );
666 $names = func_get_args();
667 if ( count( $names ) == 0 ) {
668 $names = array_keys( $this->data );
672 foreach ( $names as $name ) {
673 $value = $this->
getGPCVal( $this->data, $name,
null );
674 if ( !is_null( $value ) ) {
675 $retVal[$name] = $value;
688 return array_diff( array_keys( $this->
getValues() ), $exclude );
735 return $_SERVER[
'QUERY_STRING'];
759 static $input =
null;
760 if ( $input ===
null ) {
761 $input = file_get_contents(
'php://input' );
772 return $_SERVER[
'REQUEST_METHOD'] ??
'GET';
799 if ( $this->sessionId !==
null ) {
800 $session = SessionManager::singleton()->getSessionById( (
string)$this->sessionId,
true, $this );
806 $session = SessionManager::singleton()->getSessionForRequest( $this );
807 $this->sessionId = $session->getSessionId();
839 public function getCookie( $key, $prefix =
null, $default =
null ) {
840 if ( $prefix ===
null ) {
844 $name = $prefix . $key;
846 $name = strtr( $name,
'.',
'_' );
847 if ( isset( $_COOKIE[$name] ) ) {
848 return $_COOKIE[$name];
864 $name = $prefix . $key;
866 $name = strtr( $name,
'.',
'_' );
867 if ( isset( $_COOKIE[$name] ) ) {
868 return $_COOKIE[$name];
871 $legacyName = $prefix .
"ss0-" . $key;
872 $legacyName = strtr( $legacyName,
'.',
'_' );
873 if ( isset( $_COOKIE[$legacyName] ) ) {
874 return $_COOKIE[$legacyName];
890 if ( isset( $_SERVER[
'REQUEST_URI'] ) && strlen( $_SERVER[
'REQUEST_URI'] ) ) {
891 $base = $_SERVER[
'REQUEST_URI'];
892 } elseif ( isset( $_SERVER[
'HTTP_X_ORIGINAL_URL'] )
893 && strlen( $_SERVER[
'HTTP_X_ORIGINAL_URL'] )
896 $base = $_SERVER[
'HTTP_X_ORIGINAL_URL'];
897 } elseif ( isset( $_SERVER[
'SCRIPT_NAME'] ) ) {
898 $base = $_SERVER[
'SCRIPT_NAME'];
899 if ( isset( $_SERVER[
'QUERY_STRING'] ) && $_SERVER[
'QUERY_STRING'] !=
'' ) {
900 $base .=
'?' . $_SERVER[
'QUERY_STRING'];
904 throw new MWException(
"Web server doesn't provide either " .
905 "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
906 "of your web server configuration to https://phabricator.wikimedia.org/" );
912 $hash = strpos(
$base,
'#' );
913 if ( $hash !==
false ) {
917 if (
$base[0] ==
'/' ) {
919 return preg_replace(
'!^/+!',
'/',
$base );
922 return preg_replace(
'!^[^:]+://[^/]+/+!',
'/',
$base );
975 unset( $newquery[
'title'] );
976 $newquery = array_merge( $newquery, $array );
993 $limit = $this->
getInt(
'limit', 0 );
997 if ( ( $limit == 0 ) && ( $optionname !=
'' ) ) {
998 $limit = $wgUser->getIntOption( $optionname );
1000 if ( $limit <= 0 ) {
1003 if ( $limit > 5000 ) {
1004 $limit = 5000; # We have *some* limits...
1007 $offset = $this->
getInt(
'offset', 0 );
1008 if ( $offset < 0 ) {
1012 return [ $limit, $offset ];
1023 return $file->getTempName();
1034 return $file->getError();
1050 return $file->getName();
1071 if ( !is_object( $this->
response ) ) {
1072 $class = ( $this instanceof
FauxRequest ) ? FauxResponse::class : WebResponse::class;
1082 if ( count( $this->headers ) ) {
1086 $apacheHeaders = function_exists(
'apache_request_headers' ) ? apache_request_headers() :
false;
1087 if ( $apacheHeaders ) {
1088 foreach ( $apacheHeaders as $tempName => $tempValue ) {
1089 $this->headers[strtoupper( $tempName )] = $tempValue;
1092 foreach ( $_SERVER as $name => $value ) {
1093 if ( substr( $name, 0, 5 ) ===
'HTTP_' ) {
1094 $name = str_replace(
'_',
'-', substr( $name, 5 ) );
1095 $this->headers[$name] = $value;
1096 } elseif ( $name ===
'CONTENT_LENGTH' ) {
1097 $this->headers[
'CONTENT-LENGTH'] = $value;
1127 $name = strtoupper( $name );
1128 if ( !isset( $this->headers[$name] ) ) {
1131 $value = $this->headers[$name];
1132 if ( $flags & self::GETHEADER_LIST ) {
1133 $value = array_map(
'trim', explode(
',', $value ) );
1171 $extWhitelist[] =
'php';
1176 if ( $newUrl !==
false ) {
1182 'Invalid file extension found in the path info or query string.' );
1195 header(
'Location: ' . $url );
1196 header(
'Content-Type: text/html' );
1197 $encUrl = htmlspecialchars( $url );
1202 <title>Security redirect</title>
1205 <h1>Security redirect</h1>
1207 We can
't serve non-HTML content from the URL you have requested, because
1208 Internet Explorer would interpret it as an incorrect and potentially dangerous
1210 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1211 URL you have requested, except that "&*" is appended. This prevents Internet
1212 Explorer from seeing a bogus file extension.
1230 public function getAcceptLang() {
1231 // Modified version of code found at
1232 // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1233 $acceptLang = $this->getHeader( 'Accept-
Language' );
1234 if ( !$acceptLang ) {
1238 // Return the language codes in lower case
1239 $acceptLang = strtolower( $acceptLang );
1241 // Break up string into pieces (languages and q factors)
1244 '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/
',
1249 if ( !count( $lang_parse[1] ) ) {
1253 $langcodes = $lang_parse[1];
1254 $qvalues = $lang_parse[4];
1255 $indices = range( 0, count( $lang_parse[1] ) - 1 );
1257 // Set default q factor to 1
1258 foreach ( $indices as $index ) {
1259 if ( $qvalues[$index] === '' ) {
1260 $qvalues[$index] = 1;
1261 } elseif ( $qvalues[$index] == 0 ) {
1262 unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1266 // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1267 array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1269 // Create a list like "en" => 0.8
1270 $langs = array_combine( $langcodes, $qvalues );
1283 protected function getRawIP() {
1284 if ( !isset( $_SERVER['REMOTE_ADDR
'] ) ) {
1288 if ( is_array( $_SERVER['REMOTE_ADDR
'] ) || strpos( $_SERVER['REMOTE_ADDR
'], ',
' ) !== false ) {
1289 throw new MWException( __METHOD__
1290 . " : Could not determine the remote IP address due to multiple values." );
1292 $ipchain = $_SERVER['REMOTE_ADDR
'];
1295 return IP::canonicalize( $ipchain );
1307 public function getIP() {
1308 global $wgUsePrivateIPs;
1310 # Return cached result
1311 if ( $this->ip !== null ) {
1315 # collect the originating ips
1316 $ip = $this->getRawIP();
1318 throw new MWException( 'Unable to determine
IP.
' );
1322 $forwardedFor = $this->getHeader( 'X-Forwarded-For
' );
1323 if ( $forwardedFor !== false ) {
1324 $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1325 $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1326 $ipchain = array_map( 'trim
', explode( ',
', $forwardedFor ) );
1327 $ipchain = array_reverse( $ipchain );
1328 array_unshift( $ipchain, $ip );
1330 # Step through XFF list and find the last address in the list which is a
1331 # trusted server. Set $ip to the IP address given by that trusted server,
1332 # unless the address is not sensible (e.g. private). However, prefer private
1333 # IP addresses over proxy servers controlled by this site (more sensible).
1334 # Note that some XFF values might be "unknown" with Squid/Varnish.
1335 foreach ( $ipchain as $i => $curIP ) {
1336 $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1337 if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown
'
1338 || !$proxyLookup->isTrustedProxy( $curIP )
1340 break; // IP is not valid/trusted or does not point to anything
1343 IP::isPublic( $ipchain[$i + 1] ) ||
1345 $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1347 // Follow the next IP according to the proxy
1348 $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1349 if ( !$nextIP && $isConfigured ) {
1350 // We have not yet made it past CDN/proxy servers of this site,
1351 // so either they are misconfigured or there is some IP spoofing.
1352 throw new MWException( "Invalid IP given in XFF '$forwardedFor
'." );
1355 // keep traversing the chain
1362 # Allow extensions to improve our guess
1363 Hooks::run( 'GetIP
', [ &$ip ] );
1366 throw new MWException( "Unable to determine IP." );
1369 wfDebug( "IP: $ip\n" );
1379 public function setIP( $ip ) {
1395 public function hasSafeMethod() {
1396 if ( !isset( $_SERVER['REQUEST_METHOD
'] ) ) {
1397 return false; // CLI mode
1400 return in_array( $_SERVER['REQUEST_METHOD
'], [ 'GET
', 'HEAD
', 'OPTIONS
', 'TRACE
' ] );
1421 public function isSafeRequest() {
1422 if ( $this->markedAsSafe && $this->wasPosted() ) {
1423 return true; // marked as a "safe" POST
1426 return $this->hasSafeMethod();
1439 public function markAsSafeRequest() {
1440 $this->markedAsSafe = true;
add( $path, $params=[], $options=[])
Add a new path pattern to the path router.
initHeaders()
Initialise the header list.
SessionId null $sessionId
Session ID to use for this request.
WebRequest clone which takes values from a provided array.
getSessionData( $key)
Get data from the session.
array $headers
Lazy-initialized request headers indexed by upper-case header name.
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
static combineHostAndPort( $host, $port, $defaultPort=false)
Given a host name and a port, combine them into host/port string like you might find in a URL.
array $data
The parameters from $_GET, $_POST and the path router.
getSessionId()
Get the session id for this request, if any.
$wgScript
The URL path to index.php.
$queryParams
The parameters from $_GET only.
appendQueryValue( $key, $value)
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables.
setSessionId(SessionId $sessionId)
Set the session for this request.
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds,...
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
A collection of public static functions to play with IP address and IP ranges.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
getRawPostString()
Return the contents of the POST with no decoding.
static detectProtocol()
Detect the protocol from $_SERVER.
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it's not set.
bool $wgUseSameSiteLegacyCookies
If true, when a cross-site cookie with SameSite=None is sent, a legacy cookie with an "ss0" prefix wi...
Show an error that looks like an HTTP server error.
getRawQueryString()
Return the contents of the Query with no decoding.
appendQueryArray( $array)
Appends or replaces value of query variables.
$wgAllowExternalReqID
Whether to respect/honour the request ID provided by the incoming request via the X-Request-Id header...
static getActionPaths(array $actionPaths, $articlePath)
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
getText( $name, $default='')
Fetch a text string from the given array or return $default if it's not set.
string $protocol
Cached URL protocol.
getMethod()
Get the HTTP method used for this request.
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent.
getQueryValuesOnly()
Get the values passed in the query string only, not including the path router parameters.
setVal( $key, $value)
Set an arbitrary value into our get/post data.
static string $reqId
The unique request ID.
getRawInput()
Return the raw request body, with no processing.
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
getValues()
Extracts the given named values into an array.
static getPathInfo( $want='all')
Extract relevant query arguments from the http request uri's path to be merged with the normal php pr...
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
getArray( $name, $default=null)
Fetch an array from the input or return $default if it's not set.
WebResponse $response
Lazy-init response object.
getAllHeaders()
Get an array containing all request headers.
Object to access the $_FILES array.
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it's not set.
static areServerVarsBad( $vars, $extWhitelist=[])
Check a subset of $_SERVER (or the whole of $_SERVER if you like) to see if it indicates that the req...
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
getProtocol()
Get the current URL protocol (http or https)
getSession()
Return the session for this request.
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff,...
static splitHostAndPort( $both)
Given a host/port string, like one might find in the host part of a URL per RFC 2732,...
getCrossSiteCookie( $key, $prefix='', $default=null)
Get a cookie set with SameSite=None possibly with a legacy fallback cookie.
getLimitOffset( $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given.
checkUrlExtension( $extWhitelist=[])
Check if Internet Explorer will detect an incorrect cache extension in PATH_INFO or QUERY_STRING.
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it's not set.
float $requestTime
The timestamp of the start of the request, with microsecond precision.
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters,...
static getMain()
Get the RequestContext object associated with the main request.
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
getUploadError( $key)
Return the upload error or 0.
array $queryAndPathParams
The parameters from $_GET.
setSessionData( $key, $data)
Set session data.
doSecurityRedirect( $url)
Attempt to redirect to a URL with a QUERY_STRING that's not dangerous in IE 6.
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it's not set.
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
static getRequestId()
Get the unique request ID.
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
getHeader( $name, $flags=0)
Get a request header, or false if it isn't set.
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
string $ip
Cached client IP address.
getPostValues()
Get the values passed via POST.
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET,...
unsetVal( $key)
Unset an arbitrary value from our get/post data.
wfGetServerUrl( $proto)
Get the wiki's "server", i.e.
getRequestURL()
Return the path and query string portion of the request URI.
static overrideRequestId( $id)
Override the unique request ID.
static extractTitle( $path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
getQueryValues()
Get the values passed in the query string and the path router parameters.
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
Internationalisation code.
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode.
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.