MediaWiki REL1_35
WebRequest.php
Go to the documentation of this file.
1<?php
30use Wikimedia\IPUtils;
31
32// The point of this class is to be a wrapper around super globals
33// phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
34
47 protected $data;
48
55
59 protected $queryParams;
60
65 protected $headers = [];
66
71 public const GETHEADER_LIST = 1;
72
77 private static $reqId;
78
83 private $response;
84
89 private $ip;
90
95 protected $requestTime;
96
101 protected $protocol;
102
111 protected $sessionId = null;
112
114 protected $markedAsSafe = false;
115
119 public function __construct() {
120 $this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
121
122 // POST overrides GET data
123 // We don't use $_REQUEST here to avoid interference from cookies...
124 $this->data = $_POST + $_GET;
125
126 $this->queryAndPathParams = $this->queryParams = $_GET;
127 }
128
148 public static function getPathInfo( $want = 'all' ) {
149 // PATH_INFO is mangled due to https://bugs.php.net/bug.php?id=31892
150 // And also by Apache 2.x, double slashes are converted to single slashes.
151 // So we will use REQUEST_URI if possible.
152 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
153 // Slurp out the path portion to examine...
154 $url = $_SERVER['REQUEST_URI'];
155 if ( !preg_match( '!^https?://!', $url ) ) {
156 $url = 'http://unused' . $url;
157 }
158 $a = parse_url( $url );
159 if ( !$a ) {
160 return [];
161 }
162 $path = $a['path'] ?? '';
163
164 global $wgScript;
165 if ( $path == $wgScript && $want !== 'all' ) {
166 // Script inside a rewrite path?
167 // Abort to keep from breaking...
168 return [];
169 }
170
171 $router = new PathRouter;
172
173 // Raw PATH_INFO style
174 $router->add( "$wgScript/$1" );
175
176 global $wgArticlePath;
177 if ( $wgArticlePath ) {
178 $router->validateRoute( $wgArticlePath, 'wgArticlePath' );
179 $router->add( $wgArticlePath );
180 }
181
182 global $wgActionPaths;
183 $articlePaths = PathRouter::getActionPaths( $wgActionPaths, $wgArticlePath );
184 if ( $articlePaths ) {
185 $router->add( $articlePaths, [ 'action' => '$key' ] );
186 }
187
189 if ( $wgVariantArticlePath ) {
190 $router->validateRoute( $wgVariantArticlePath, 'wgVariantArticlePath' );
191 $router->add( $wgVariantArticlePath,
192 [ 'variant' => '$2' ],
193 [ '$2' => MediaWikiServices::getInstance()->getContentLanguage()->
194 getVariants() ]
195 );
196 }
197
198 Hooks::runner()->onWebRequestPathInfoRouter( $router );
199
200 $matches = $router->parse( $path );
201 } else {
202 global $wgUsePathInfo;
203 $matches = [];
204 if ( $wgUsePathInfo ) {
205 if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) ) {
206 // Mangled PATH_INFO
207 // https://bugs.php.net/bug.php?id=31892
208 // Also reported when ini_get('cgi.fix_pathinfo')==false
209 $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
210 } elseif ( !empty( $_SERVER['PATH_INFO'] ) ) {
211 // Regular old PATH_INFO yay
212 $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
213 }
214 }
215 }
216
217 return $matches;
218 }
219
231 public static function getRequestPathSuffix( $basePath ) {
232 $basePath = rtrim( $basePath, '/' ) . '/';
233 $requestUrl = self::getGlobalRequestURL();
234 $qpos = strpos( $requestUrl, '?' );
235 if ( $qpos !== false ) {
236 $requestPath = substr( $requestUrl, 0, $qpos );
237 } else {
238 $requestPath = $requestUrl;
239 }
240 if ( substr( $requestPath, 0, strlen( $basePath ) ) !== $basePath ) {
241 return false;
242 }
243 return rawurldecode( substr( $requestPath, strlen( $basePath ) ) );
244 }
245
252 public static function detectServer() {
254
255 $proto = self::detectProtocol();
256 $stdPort = $proto === 'https' ? 443 : 80;
257
258 $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
259 $host = 'localhost';
260 $port = $stdPort;
261 foreach ( $varNames as $varName ) {
262 if ( !isset( $_SERVER[$varName] ) ) {
263 continue;
264 }
265
266 $parts = IPUtils::splitHostAndPort( $_SERVER[$varName] );
267 if ( !$parts ) {
268 // Invalid, do not use
269 continue;
270 }
271
272 $host = $parts[0];
273 if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
274 // T72021: Assume that upstream proxy is running on the default
275 // port based on the protocol. We have no reliable way to determine
276 // the actual port in use upstream.
277 $port = $stdPort;
278 } elseif ( $parts[1] === false ) {
279 if ( isset( $_SERVER['SERVER_PORT'] ) ) {
280 $port = $_SERVER['SERVER_PORT'];
281 } // else leave it as $stdPort
282 } else {
283 $port = $parts[1];
284 }
285 break;
286 }
287
288 return $proto . '://' . IPUtils::combineHostAndPort( $host, $port, $stdPort );
289 }
290
298 public static function detectProtocol() {
299 if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
300 ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
301 $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
302 return 'https';
303 } else {
304 return 'http';
305 }
306 }
307
315 public function getElapsedTime() {
316 return microtime( true ) - $this->requestTime;
317 }
318
327 public static function getRequestId() {
328 // This method is called from various error handlers and should be kept simple.
329
330 if ( !self::$reqId ) {
333 ? RequestContext::getMain()->getRequest()->getHeader( 'X-Request-Id' )
334 : null;
335 if ( !$id ) {
336 $id = $_SERVER['UNIQUE_ID'] ?? wfRandomString( 24 );
337 }
338 self::$reqId = $id;
339 }
340
341 return self::$reqId;
342 }
343
351 public static function overrideRequestId( $id ) {
352 self::$reqId = $id;
353 }
354
359 public function getProtocol() {
360 if ( $this->protocol === null ) {
361 $this->protocol = self::detectProtocol();
362 }
363 return $this->protocol;
364 }
365
373 public function interpolateTitle() {
374 // T18019: title interpolation on API queries is useless and sometimes harmful
375 if ( defined( 'MW_API' ) ) {
376 return;
377 }
378
379 $matches = self::getPathInfo( 'title' );
380 foreach ( $matches as $key => $val ) {
381 $this->data[$key] = $this->queryAndPathParams[$key] = $val;
382 }
383 }
384
395 public static function extractTitle( $path, $bases, $key = false ) {
396 foreach ( (array)$bases as $keyValue => $base ) {
397 // Find the part after $wgArticlePath
398 $base = str_replace( '$1', '', $base );
399 $baseLen = strlen( $base );
400 if ( substr( $path, 0, $baseLen ) == $base ) {
401 $raw = substr( $path, $baseLen );
402 if ( $raw !== '' ) {
403 $matches = [ 'title' => rawurldecode( $raw ) ];
404 if ( $key ) {
405 $matches[$key] = $keyValue;
406 }
407 return $matches;
408 }
409 }
410 }
411 return [];
412 }
413
421 public function normalizeUnicode( $data ) {
422 if ( is_array( $data ) ) {
423 foreach ( $data as $key => $val ) {
424 $data[$key] = $this->normalizeUnicode( $val );
425 }
426 } else {
427 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
428 $data = $contLang->normalize( $data );
429 }
430 return $data;
431 }
432
441 private function getGPCVal( $arr, $name, $default ) {
442 # PHP is so nice to not touch input data, except sometimes:
443 # https://www.php.net/variables.external#language.variables.external.dot-in-names
444 # Work around PHP *feature* to avoid *bugs* elsewhere.
445 $name = strtr( $name, '.', '_' );
446
447 if ( !isset( $arr[$name] ) ) {
448 return $default;
449 }
450
451 $data = $arr[$name];
452 # Optimisation: Skip UTF-8 normalization and legacy transcoding for simple ASCII strings.
453 $isAsciiStr = ( is_string( $data ) && preg_match( '/[^\x20-\x7E]/', $data ) === 0 );
454 if ( !$isAsciiStr ) {
455 if ( isset( $_GET[$name] ) && is_string( $data ) ) {
456 # Check for alternate/legacy character encoding.
457 $data = MediaWikiServices::getInstance()
458 ->getContentLanguage()
459 ->checkTitleEncoding( $data );
460 }
461 $data = $this->normalizeUnicode( $data );
462 }
463
464 return $data;
465 }
466
479 public function getRawVal( $name, $default = null ) {
480 $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
481 if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
482 $val = $this->data[$name];
483 } else {
484 $val = $default;
485 }
486 if ( $val === null ) {
487 return $val;
488 } else {
489 return (string)$val;
490 }
491 }
492
503 public function getVal( $name, $default = null ) {
504 $val = $this->getGPCVal( $this->data, $name, $default );
505 if ( is_array( $val ) ) {
506 $val = $default;
507 }
508 if ( $val === null ) {
509 return $val;
510 } else {
511 return (string)$val;
512 }
513 }
514
522 public function setVal( $key, $value ) {
523 $ret = $this->data[$key] ?? null;
524 $this->data[$key] = $value;
525 return $ret;
526 }
527
534 public function unsetVal( $key ) {
535 if ( !isset( $this->data[$key] ) ) {
536 $ret = null;
537 } else {
538 $ret = $this->data[$key];
539 unset( $this->data[$key] );
540 }
541 return $ret;
542 }
543
553 public function getArray( $name, $default = null ) {
554 $val = $this->getGPCVal( $this->data, $name, $default );
555 if ( $val === null ) {
556 return null;
557 } else {
558 return (array)$val;
559 }
560 }
561
572 public function getIntArray( $name, $default = null ) {
573 $val = $this->getArray( $name, $default );
574 if ( is_array( $val ) ) {
575 $val = array_map( 'intval', $val );
576 }
577 return $val;
578 }
579
589 public function getInt( $name, $default = 0 ) {
590 return intval( $this->getRawVal( $name, $default ) );
591 }
592
601 public function getIntOrNull( $name ) {
602 $val = $this->getRawVal( $name );
603 return is_numeric( $val )
604 ? intval( $val )
605 : null;
606 }
607
618 public function getFloat( $name, $default = 0.0 ) {
619 return floatval( $this->getRawVal( $name, $default ) );
620 }
621
631 public function getBool( $name, $default = false ) {
632 return (bool)$this->getRawVal( $name, $default );
633 }
634
644 public function getFuzzyBool( $name, $default = false ) {
645 return $this->getBool( $name, $default )
646 && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
647 }
648
657 public function getCheck( $name ) {
658 # Checkboxes and buttons are only present when clicked
659 # Presence connotes truth, absence false
660 return $this->getRawVal( $name, null ) !== null;
661 }
662
673 public function getText( $name, $default = '' ) {
674 $val = $this->getVal( $name, $default );
675 return str_replace( "\r\n", "\n", $val );
676 }
677
685 public function getValues() {
686 $names = func_get_args();
687 if ( count( $names ) == 0 ) {
688 $names = array_keys( $this->data );
689 }
690
691 $retVal = [];
692 foreach ( $names as $name ) {
693 $value = $this->getGPCVal( $this->data, $name, null );
694 if ( $value !== null ) {
695 $retVal[$name] = $value;
696 }
697 }
698 return $retVal;
699 }
700
707 public function getValueNames( $exclude = [] ) {
708 return array_diff( array_keys( $this->getValues() ), $exclude );
709 }
710
718 public function getQueryValues() {
719 return $this->queryAndPathParams;
720 }
721
731 public function getQueryValuesOnly() {
732 return $this->queryParams;
733 }
734
743 public function getPostValues() {
744 return $_POST;
745 }
746
754 public function getRawQueryString() {
755 return $_SERVER['QUERY_STRING'];
756 }
757
764 public function getRawPostString() {
765 if ( !$this->wasPosted() ) {
766 return '';
767 }
768 return $this->getRawInput();
769 }
770
778 public function getRawInput() {
779 static $input = null;
780 if ( $input === null ) {
781 $input = file_get_contents( 'php://input' );
782 }
783 return $input;
784 }
785
791 public function getMethod() {
792 return $_SERVER['REQUEST_METHOD'] ?? 'GET';
793 }
794
804 public function wasPosted() {
805 return $this->getMethod() == 'POST';
806 }
807
818 public function getSession() {
819 if ( $this->sessionId !== null ) {
820 $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
821 if ( $session ) {
822 return $session;
823 }
824 }
825
826 $session = SessionManager::singleton()->getSessionForRequest( $this );
827 $this->sessionId = $session->getSessionId();
828 return $session;
829 }
830
837 public function setSessionId( SessionId $sessionId ) {
838 $this->sessionId = $sessionId;
839 }
840
847 public function getSessionId() {
848 return $this->sessionId;
849 }
850
859 public function getCookie( $key, $prefix = null, $default = null ) {
860 if ( $prefix === null ) {
861 global $wgCookiePrefix;
862 $prefix = $wgCookiePrefix;
863 }
864 $name = $prefix . $key;
865 // Work around mangling of $_COOKIE
866 $name = strtr( $name, '.', '_' );
867 if ( isset( $_COOKIE[$name] ) ) {
868 return $_COOKIE[$name];
869 } else {
870 return $default;
871 }
872 }
873
882 public function getCrossSiteCookie( $key, $prefix = '', $default = null ) {
884 $name = $prefix . $key;
885 // Work around mangling of $_COOKIE
886 $name = strtr( $name, '.', '_' );
887 if ( isset( $_COOKIE[$name] ) ) {
888 return $_COOKIE[$name];
889 }
891 $legacyName = $prefix . "ss0-" . $key;
892 $legacyName = strtr( $legacyName, '.', '_' );
893 if ( isset( $_COOKIE[$legacyName] ) ) {
894 return $_COOKIE[$legacyName];
895 }
896 }
897 return $default;
898 }
899
907 public static function getGlobalRequestURL() {
908 // This method is called on fatal errors; it should not depend on anything complex.
909
910 if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
911 $base = $_SERVER['REQUEST_URI'];
912 } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
913 && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
914 ) {
915 // Probably IIS; doesn't set REQUEST_URI
916 $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
917 } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
918 $base = $_SERVER['SCRIPT_NAME'];
919 if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
920 $base .= '?' . $_SERVER['QUERY_STRING'];
921 }
922 } else {
923 // This shouldn't happen!
924 throw new MWException( "Web server doesn't provide either " .
925 "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
926 "of your web server configuration to https://phabricator.wikimedia.org/" );
927 }
928 // User-agents should not send a fragment with the URI, but
929 // if they do, and the web server passes it on to us, we
930 // need to strip it or we get false-positive redirect loops
931 // or weird output URLs
932 $hash = strpos( $base, '#' );
933 if ( $hash !== false ) {
934 $base = substr( $base, 0, $hash );
935 }
936
937 if ( $base[0] == '/' ) {
938 // More than one slash will look like it is protocol relative
939 return preg_replace( '!^/+!', '/', $base );
940 } else {
941 // We may get paths with a host prepended; strip it.
942 return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
943 }
944 }
945
953 public function getRequestURL() {
954 return self::getGlobalRequestURL();
955 }
956
967 public function getFullRequestURL() {
968 // Pass an explicit PROTO constant instead of PROTO_CURRENT so that we
969 // do not rely on state from the global $wgRequest object (which it would,
970 // via wfGetServerUrl/wfExpandUrl/$wgRequest->protocol).
971 if ( $this->getProtocol() === 'http' ) {
972 return wfGetServerUrl( PROTO_HTTP ) . $this->getRequestURL();
973 } else {
974 return wfGetServerUrl( PROTO_HTTPS ) . $this->getRequestURL();
975 }
976 }
977
983 public function appendQueryValue( $key, $value ) {
984 return $this->appendQueryArray( [ $key => $value ] );
985 }
986
993 public function appendQueryArray( $array ) {
994 $newquery = $this->getQueryValues();
995 unset( $newquery['title'] );
996 $newquery = array_merge( $newquery, $array );
997
998 return wfArrayToCgi( $newquery );
999 }
1000
1010 public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
1011 wfDeprecated( __METHOD__, '1.35' );
1012
1013 global $wgUser;
1014 return $this->getLimitOffsetForUser( $wgUser, $deflimit, $optionname );
1015 }
1016
1027 public function getLimitOffsetForUser( User $user, $deflimit = 50, $optionname = 'rclimit' ) {
1028 $limit = $this->getInt( 'limit', 0 );
1029 if ( $limit < 0 ) {
1030 $limit = 0;
1031 }
1032 if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
1033 $limit = $user->getIntOption( $optionname );
1034 }
1035 if ( $limit <= 0 ) {
1036 $limit = $deflimit;
1037 }
1038 if ( $limit > 5000 ) {
1039 $limit = 5000; # We have *some* limits...
1040 }
1041
1042 $offset = $this->getInt( 'offset', 0 );
1043 if ( $offset < 0 ) {
1044 $offset = 0;
1045 }
1046
1047 return [ $limit, $offset ];
1048 }
1049
1056 public function getFileTempname( $key ) {
1057 $file = new WebRequestUpload( $this, $key );
1058 return $file->getTempName();
1059 }
1060
1067 public function getUploadError( $key ) {
1068 $file = new WebRequestUpload( $this, $key );
1069 return $file->getError();
1070 }
1071
1083 public function getFileName( $key ) {
1084 $file = new WebRequestUpload( $this, $key );
1085 return $file->getName();
1086 }
1087
1094 public function getUpload( $key ) {
1095 return new WebRequestUpload( $this, $key );
1096 }
1097
1104 public function response() {
1105 /* Lazy initialization of response object for this request */
1106 if ( !is_object( $this->response ) ) {
1107 $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
1108 $this->response = new $class();
1109 }
1110 return $this->response;
1111 }
1112
1116 protected function initHeaders() {
1117 if ( count( $this->headers ) ) {
1118 return;
1119 }
1120
1121 $this->headers = array_change_key_case( getallheaders(), CASE_UPPER );
1122 }
1123
1129 public function getAllHeaders() {
1130 $this->initHeaders();
1131 return $this->headers;
1132 }
1133
1146 public function getHeader( $name, $flags = 0 ) {
1147 $this->initHeaders();
1148 $name = strtoupper( $name );
1149 if ( !isset( $this->headers[$name] ) ) {
1150 return false;
1151 }
1152 $value = $this->headers[$name];
1153 if ( $flags & self::GETHEADER_LIST ) {
1154 $value = array_map( 'trim', explode( ',', $value ) );
1155 }
1156 return $value;
1157 }
1158
1166 public function getSessionData( $key ) {
1167 return $this->getSession()->get( $key );
1168 }
1169
1177 public function setSessionData( $key, $data ) {
1178 $this->getSession()->set( $key, $data );
1179 }
1180
1190 public function checkUrlExtension( $extWhitelist = [] ) {
1191 wfDeprecated( __METHOD__, '1.35' );
1192 return true;
1193 }
1194
1208 public function getAcceptLang() {
1209 // Modified version of code found at
1210 // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1211 $acceptLang = $this->getHeader( 'Accept-Language' );
1212 if ( !$acceptLang ) {
1213 return [];
1214 }
1215
1216 // Return the language codes in lower case
1217 $acceptLang = strtolower( $acceptLang );
1218
1219 // Break up string into pieces (languages and q factors)
1220 if ( !preg_match_all(
1221 '/([a-z]{1,8}(?:-[a-z]{1,8})*|\*)\s*(?:;\s*q\s*=\s*(1(?:\.0{0,3})?|0(?:\.[0-9]{0,3})?)?)?/',
1222 $acceptLang,
1223 $matches,
1224 PREG_SET_ORDER
1225 ) ) {
1226 return [];
1227 }
1228
1229 // Create a list like "en" => 0.8
1230 $langs = [];
1231 foreach ( $matches as $match ) {
1232 $languageCode = $match[1];
1233 // When not present, the default value is 1
1234 $qValue = $match[2] ?? 1;
1235 if ( $qValue > 0 ) {
1236 $langs[$languageCode] = $qValue;
1237 }
1238 }
1239
1240 // Sort list by qValue
1241 arsort( $langs, SORT_NUMERIC );
1242 return $langs;
1243 }
1244
1253 protected function getRawIP() {
1254 if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1255 return null;
1256 }
1257
1258 if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1259 throw new MWException( __METHOD__
1260 . " : Could not determine the remote IP address due to multiple values." );
1261 } else {
1262 $ipchain = $_SERVER['REMOTE_ADDR'];
1263 }
1264
1265 return IPUtils::canonicalize( $ipchain );
1266 }
1267
1277 public function getIP() {
1278 global $wgUsePrivateIPs;
1279
1280 # Return cached result
1281 if ( $this->ip !== null ) {
1282 return $this->ip;
1283 }
1284
1285 # collect the originating ips
1286 $ip = $this->getRawIP();
1287 if ( !$ip ) {
1288 throw new MWException( 'Unable to determine IP.' );
1289 }
1290
1291 # Append XFF
1292 $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1293 if ( $forwardedFor !== false ) {
1294 $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1295 $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1296 $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1297 $ipchain = array_reverse( $ipchain );
1298 array_unshift( $ipchain, $ip );
1299
1300 # Step through XFF list and find the last address in the list which is a
1301 # trusted server. Set $ip to the IP address given by that trusted server,
1302 # unless the address is not sensible (e.g. private). However, prefer private
1303 # IP addresses over proxy servers controlled by this site (more sensible).
1304 # Note that some XFF values might be "unknown" with Squid/Varnish.
1305 foreach ( $ipchain as $i => $curIP ) {
1306 $curIP = IPUtils::sanitizeIP( IPUtils::canonicalize( $curIP ) );
1307 if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1308 || !$proxyLookup->isTrustedProxy( $curIP )
1309 ) {
1310 break; // IP is not valid/trusted or does not point to anything
1311 }
1312 if (
1313 IPUtils::isPublic( $ipchain[$i + 1] ) ||
1315 $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1316 ) {
1317 // Follow the next IP according to the proxy
1318 $nextIP = IPUtils::canonicalize( $ipchain[$i + 1] );
1319 if ( !$nextIP && $isConfigured ) {
1320 // We have not yet made it past CDN/proxy servers of this site,
1321 // so either they are misconfigured or there is some IP spoofing.
1322 throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1323 }
1324 $ip = $nextIP;
1325 // keep traversing the chain
1326 continue;
1327 }
1328 break;
1329 }
1330 }
1331
1332 # Allow extensions to improve our guess
1333 Hooks::runner()->onGetIP( $ip );
1334
1335 if ( !$ip ) {
1336 throw new MWException( "Unable to determine IP." );
1337 }
1338
1339 $this->ip = $ip;
1340 return $ip;
1341 }
1342
1348 public function setIP( $ip ) {
1349 $this->ip = $ip;
1350 }
1351
1364 public function hasSafeMethod() {
1365 if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1366 return false; // CLI mode
1367 }
1368
1369 return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1370 }
1371
1390 public function isSafeRequest() {
1391 if ( $this->markedAsSafe && $this->wasPosted() ) {
1392 return true; // marked as a "safe" POST
1393 }
1394
1395 return $this->hasSafeMethod();
1396 }
1397
1408 public function markAsSafeRequest() {
1409 $this->markedAsSafe = true;
1410 }
1411}
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode.
$wgScript
The URL path to index.php.
$wgActionPaths
To set 'pretty' URL paths for actions other than plain page views, add to this array.
bool $wgUseSameSiteLegacyCookies
If true, when a cross-site cookie with SameSite=None is sent, a legacy cookie with an "ss0" prefix wi...
$wgArticlePath
The URL path for primary article page views.
$wgAllowExternalReqID
Whether to respect/honour the request ID provided by the incoming request via the X-Request-Id header...
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
$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.
$wgUsePrivateIPs
Should forwarded Private IPs be accepted?
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
wfGetServerUrl( $proto)
Get the wiki's "server", i.e.
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
$basePath
Definition addSite.php:5
WebRequest clone which takes values from a provided array.
MediaWiki exception.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Value object holding the session ID in a manner that can be globally updated.
Definition SessionId.php:40
This serves as the entry point to the MediaWiki session handling system.
Manages data for an an authenticated session.
Definition Session.php:48
PathRouter class.
add( $path, $params=[], $options=[])
Add a new path pattern to the path router.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
Definition User.php:2712
Object to access the $_FILES array.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
markAsSafeRequest()
Mark this request as identified as being nullipotent even if it is a POST request.
array $headers
Lazy-initialized request headers indexed by upper-case header name.
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
string $protocol
Cached URL protocol.
getArray( $name, $default=null)
Fetch an array from the input or return $default if it's not set.
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables.
getPostValues()
Get the values passed via POST.
static detectProtocol()
Detect the protocol from $_SERVER.
isSafeRequest()
Whether this request should be identified as being "safe".
getSession()
Return the session for this request.
getRawInput()
Return the raw request body, with no processing.
getRawQueryString()
Return the contents of the Query with no decoding.
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it's not set.
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
WebResponse $response
Lazy-init response object.
getUploadError( $key)
Return the upload error or 0.
getAllHeaders()
Get an array containing all request headers.
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it's not set.
static getRequestId()
Get the unique request ID.
getProtocol()
Get the current URL protocol (http or https)
getMethod()
Get the HTTP method used for this request.
initHeaders()
Initialise the header list.
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
static getRequestPathSuffix( $basePath)
If the request URL matches a given base path, extract the path part of the request URL after that bas...
string $ip
Cached client IP address.
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
setVal( $key, $value)
Set an arbitrary value into our get/post data.
static string $reqId
The unique request ID.
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
checkUrlExtension( $extWhitelist=[])
This function formerly did a security check to prevent an XSS vulnerability in IE6,...
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds,...
float $requestTime
The timestamp of the start of the request, with microsecond precision.
getCrossSiteCookie( $key, $prefix='', $default=null)
Get a cookie set with SameSite=None possibly with a legacy fallback cookie.
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
appendQueryArray( $array)
Appends or replaces value of query variables.
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
getSessionId()
Get the session id for this request, if any.
getAcceptLang()
Parse the Accept-Language header sent by the client into an array.
getRawPostString()
Return the contents of the POST with no decoding.
getQueryValues()
Get the values passed in the query string and the path router parameters.
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff,...
getIP()
Work out the IP address based on various globals For trusted proxies, use the XFF client IP (first of...
$queryParams
The parameters from $_GET only.
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET,...
setSessionData( $key, $data)
Set session data.
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent.
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
hasSafeMethod()
Check if this request uses a "safe" HTTP method.
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it's not set.
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it's not set.
appendQueryValue( $key, $value)
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
static overrideRequestId( $id)
Override the unique request ID.
unsetVal( $key)
Unset an arbitrary value from our get/post data.
static getPathInfo( $want='all')
Extract relevant query arguments from the http request uri's path to be merged with the normal php pr...
SessionId null $sessionId
Session ID to use for this request.
array $queryAndPathParams
The parameters from $_GET.
getRawIP()
Fetch the raw IP from the request.
setSessionId(SessionId $sessionId)
Set the session for this request.
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
array $data
The parameters from $_GET, $_POST and the path router.
getLimitOffset( $deflimit=50, $optionname='rclimit')
Same as ::getLimitOffsetForUser, but without a user parameter, instead using $wgUser.
static extractTitle( $path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
getText( $name, $default='')
Fetch a text string from the given array or return $default if it's not set.
getLimitOffsetForUser(User $user, $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given.
getRequestURL()
Return the path and query string portion of the request URI.
getHeader( $name, $flags=0)
Get a request header, or false if it isn't set.
getSessionData( $key)
Get data from the session.
getValues()
Extracts the given named values into an array.
getQueryValuesOnly()
Get the values passed in the query string only, not including the path router parameters.
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
const PROTO_HTTPS
Definition Defines.php:210
const PROTO_HTTP
Definition Defines.php:209
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42