MediaWiki REL1_32
WebRequest.php
Go to the documentation of this file.
1<?php
30
31// The point of this class is to be a wrapper around super globals
32// phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
33
42 protected $data, $headers = [];
43
48 const GETHEADER_LIST = 1;
49
54 private static $reqId;
55
60 private $response;
61
66 private $ip;
67
72 protected $requestTime;
73
78 protected $protocol;
79
85 protected $sessionId = null;
86
88 protected $markedAsSafe = false;
89
93 public function __construct() {
94 $this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
95
96 // POST overrides GET data
97 // We don't use $_REQUEST here to avoid interference from cookies...
98 $this->data = $_POST + $_GET;
99 }
100
116 public static function getPathInfo( $want = 'all' ) {
117 global $wgUsePathInfo;
118 // PATH_INFO is mangled due to https://bugs.php.net/bug.php?id=31892
119 // And also by Apache 2.x, double slashes are converted to single slashes.
120 // So we will use REQUEST_URI if possible.
121 $matches = [];
122 if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
123 // Slurp out the path portion to examine...
124 $url = $_SERVER['REQUEST_URI'];
125 if ( !preg_match( '!^https?://!', $url ) ) {
126 $url = 'http://unused' . $url;
127 }
128 Wikimedia\suppressWarnings();
129 $a = parse_url( $url );
130 Wikimedia\restoreWarnings();
131 if ( $a ) {
132 $path = $a['path'] ?? '';
133
134 global $wgScript;
135 if ( $path == $wgScript && $want !== 'all' ) {
136 // Script inside a rewrite path?
137 // Abort to keep from breaking...
138 return $matches;
139 }
140
141 $router = new PathRouter;
142
143 // Raw PATH_INFO style
144 $router->add( "$wgScript/$1" );
145
146 if ( isset( $_SERVER['SCRIPT_NAME'] )
147 && preg_match( '/\.php/', $_SERVER['SCRIPT_NAME'] )
148 ) {
149 # Check for SCRIPT_NAME, we handle index.php explicitly
150 # But we do have some other .php files such as img_auth.php
151 # Don't let root article paths clober the parsing for them
152 $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" );
153 }
154
155 global $wgArticlePath;
156 if ( $wgArticlePath ) {
157 $router->add( $wgArticlePath );
158 }
159
160 global $wgActionPaths;
161 if ( $wgActionPaths ) {
162 $router->add( $wgActionPaths, [ 'action' => '$key' ] );
163 }
164
166 if ( $wgVariantArticlePath ) {
167 $router->add( $wgVariantArticlePath,
168 [ 'variant' => '$2' ],
169 [ '$2' => MediaWikiServices::getInstance()->getContentLanguage()->
170 getVariants() ]
171 );
172 }
173
174 Hooks::run( 'WebRequestPathInfoRouter', [ $router ] );
175
176 $matches = $router->parse( $path );
177 }
178 } elseif ( $wgUsePathInfo ) {
179 if ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) {
180 // Mangled PATH_INFO
181 // https://bugs.php.net/bug.php?id=31892
182 // Also reported when ini_get('cgi.fix_pathinfo')==false
183 $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
184
185 } elseif ( isset( $_SERVER['PATH_INFO'] ) && $_SERVER['PATH_INFO'] != '' ) {
186 // Regular old PATH_INFO yay
187 $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
188 }
189 }
190
191 return $matches;
192 }
193
200 public static function detectServer() {
202
203 $proto = self::detectProtocol();
204 $stdPort = $proto === 'https' ? 443 : 80;
205
206 $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
207 $host = 'localhost';
208 $port = $stdPort;
209 foreach ( $varNames as $varName ) {
210 if ( !isset( $_SERVER[$varName] ) ) {
211 continue;
212 }
213
214 $parts = IP::splitHostAndPort( $_SERVER[$varName] );
215 if ( !$parts ) {
216 // Invalid, do not use
217 continue;
218 }
219
220 $host = $parts[0];
221 if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
222 // T72021: Assume that upstream proxy is running on the default
223 // port based on the protocol. We have no reliable way to determine
224 // the actual port in use upstream.
225 $port = $stdPort;
226 } elseif ( $parts[1] === false ) {
227 if ( isset( $_SERVER['SERVER_PORT'] ) ) {
228 $port = $_SERVER['SERVER_PORT'];
229 } // else leave it as $stdPort
230 } else {
231 $port = $parts[1];
232 }
233 break;
234 }
235
236 return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort );
237 }
238
246 public static function detectProtocol() {
247 if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
248 ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
249 $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
250 return 'https';
251 } else {
252 return 'http';
253 }
254 }
255
263 public function getElapsedTime() {
264 return microtime( true ) - $this->requestTime;
265 }
266
275 public static function getRequestId() {
276 // This method is called from various error handlers and should be kept simple.
277
278 if ( !self::$reqId ) {
279 self::$reqId = $_SERVER['UNIQUE_ID'] ?? wfRandomString( 24 );
280 }
281
282 return self::$reqId;
283 }
284
292 public static function overrideRequestId( $id ) {
293 self::$reqId = $id;
294 }
295
300 public function getProtocol() {
301 if ( $this->protocol === null ) {
302 $this->protocol = self::detectProtocol();
303 }
304 return $this->protocol;
305 }
306
314 public function interpolateTitle() {
315 // T18019: title interpolation on API queries is useless and sometimes harmful
316 if ( defined( 'MW_API' ) ) {
317 return;
318 }
319
320 $matches = self::getPathInfo( 'title' );
321 foreach ( $matches as $key => $val ) {
322 $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
323 }
324 }
325
336 static function extractTitle( $path, $bases, $key = false ) {
337 foreach ( (array)$bases as $keyValue => $base ) {
338 // Find the part after $wgArticlePath
339 $base = str_replace( '$1', '', $base );
340 $baseLen = strlen( $base );
341 if ( substr( $path, 0, $baseLen ) == $base ) {
342 $raw = substr( $path, $baseLen );
343 if ( $raw !== '' ) {
344 $matches = [ 'title' => rawurldecode( $raw ) ];
345 if ( $key ) {
346 $matches[$key] = $keyValue;
347 }
348 return $matches;
349 }
350 }
351 }
352 return [];
353 }
354
362 public function normalizeUnicode( $data ) {
363 if ( is_array( $data ) ) {
364 foreach ( $data as $key => $val ) {
365 $data[$key] = $this->normalizeUnicode( $val );
366 }
367 } else {
368 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
369 $data = $contLang ? $contLang->normalize( $data ) :
370 UtfNormal\Validator::cleanUp( $data );
371 }
372 return $data;
373 }
374
383 private function getGPCVal( $arr, $name, $default ) {
384 # PHP is so nice to not touch input data, except sometimes:
385 # https://secure.php.net/variables.external#language.variables.external.dot-in-names
386 # Work around PHP *feature* to avoid *bugs* elsewhere.
387 $name = strtr( $name, '.', '_' );
388 if ( isset( $arr[$name] ) ) {
389 $data = $arr[$name];
390 if ( isset( $_GET[$name] ) && !is_array( $data ) ) {
391 # Check for alternate/legacy character encoding.
392 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
393 if ( $contLang ) {
394 $data = $contLang->checkTitleEncoding( $data );
395 }
396 }
397 $data = $this->normalizeUnicode( $data );
398 return $data;
399 } else {
400 return $default;
401 }
402 }
403
416 public function getRawVal( $name, $default = null ) {
417 $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
418 if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
419 $val = $this->data[$name];
420 } else {
421 $val = $default;
422 }
423 if ( is_null( $val ) ) {
424 return $val;
425 } else {
426 return (string)$val;
427 }
428 }
429
440 public function getVal( $name, $default = null ) {
441 $val = $this->getGPCVal( $this->data, $name, $default );
442 if ( is_array( $val ) ) {
443 $val = $default;
444 }
445 if ( is_null( $val ) ) {
446 return $val;
447 } else {
448 return (string)$val;
449 }
450 }
451
459 public function setVal( $key, $value ) {
460 $ret = $this->data[$key] ?? null;
461 $this->data[$key] = $value;
462 return $ret;
463 }
464
471 public function unsetVal( $key ) {
472 if ( !isset( $this->data[$key] ) ) {
473 $ret = null;
474 } else {
475 $ret = $this->data[$key];
476 unset( $this->data[$key] );
477 }
478 return $ret;
479 }
480
490 public function getArray( $name, $default = null ) {
491 $val = $this->getGPCVal( $this->data, $name, $default );
492 if ( is_null( $val ) ) {
493 return null;
494 } else {
495 return (array)$val;
496 }
497 }
498
509 public function getIntArray( $name, $default = null ) {
510 $val = $this->getArray( $name, $default );
511 if ( is_array( $val ) ) {
512 $val = array_map( 'intval', $val );
513 }
514 return $val;
515 }
516
526 public function getInt( $name, $default = 0 ) {
527 return intval( $this->getRawVal( $name, $default ) );
528 }
529
538 public function getIntOrNull( $name ) {
539 $val = $this->getRawVal( $name );
540 return is_numeric( $val )
541 ? intval( $val )
542 : null;
543 }
544
555 public function getFloat( $name, $default = 0.0 ) {
556 return floatval( $this->getRawVal( $name, $default ) );
557 }
558
568 public function getBool( $name, $default = false ) {
569 return (bool)$this->getRawVal( $name, $default );
570 }
571
581 public function getFuzzyBool( $name, $default = false ) {
582 return $this->getBool( $name, $default )
583 && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
584 }
585
594 public function getCheck( $name ) {
595 # Checkboxes and buttons are only present when clicked
596 # Presence connotes truth, absence false
597 return $this->getRawVal( $name, null ) !== null;
598 }
599
610 public function getText( $name, $default = '' ) {
611 $val = $this->getVal( $name, $default );
612 return str_replace( "\r\n", "\n", $val );
613 }
614
622 public function getValues() {
623 $names = func_get_args();
624 if ( count( $names ) == 0 ) {
625 $names = array_keys( $this->data );
626 }
627
628 $retVal = [];
629 foreach ( $names as $name ) {
630 $value = $this->getGPCVal( $this->data, $name, null );
631 if ( !is_null( $value ) ) {
632 $retVal[$name] = $value;
633 }
634 }
635 return $retVal;
636 }
637
644 public function getValueNames( $exclude = [] ) {
645 return array_diff( array_keys( $this->getValues() ), $exclude );
646 }
647
655 public function getQueryValues() {
656 return $_GET;
657 }
658
667 public function getPostValues() {
668 return $_POST;
669 }
670
678 public function getRawQueryString() {
679 return $_SERVER['QUERY_STRING'];
680 }
681
688 public function getRawPostString() {
689 if ( !$this->wasPosted() ) {
690 return '';
691 }
692 return $this->getRawInput();
693 }
694
702 public function getRawInput() {
703 static $input = null;
704 if ( $input === null ) {
705 $input = file_get_contents( 'php://input' );
706 }
707 return $input;
708 }
709
715 public function getMethod() {
716 return $_SERVER['REQUEST_METHOD'] ?? 'GET';
717 }
718
728 public function wasPosted() {
729 return $this->getMethod() == 'POST';
730 }
731
742 public function getSession() {
743 if ( $this->sessionId !== null ) {
744 $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
745 if ( $session ) {
746 return $session;
747 }
748 }
749
750 $session = SessionManager::singleton()->getSessionForRequest( $this );
751 $this->sessionId = $session->getSessionId();
752 return $session;
753 }
754
761 public function setSessionId( SessionId $sessionId ) {
762 $this->sessionId = $sessionId;
763 }
764
771 public function getSessionId() {
772 return $this->sessionId;
773 }
774
783 public function getCookie( $key, $prefix = null, $default = null ) {
784 if ( $prefix === null ) {
785 global $wgCookiePrefix;
786 $prefix = $wgCookiePrefix;
787 }
788 return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
789 }
790
798 public static function getGlobalRequestURL() {
799 // This method is called on fatal errors; it should not depend on anything complex.
800
801 if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
802 $base = $_SERVER['REQUEST_URI'];
803 } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
804 && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
805 ) {
806 // Probably IIS; doesn't set REQUEST_URI
807 $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
808 } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
809 $base = $_SERVER['SCRIPT_NAME'];
810 if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
811 $base .= '?' . $_SERVER['QUERY_STRING'];
812 }
813 } else {
814 // This shouldn't happen!
815 throw new MWException( "Web server doesn't provide either " .
816 "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
817 "of your web server configuration to https://phabricator.wikimedia.org/" );
818 }
819 // User-agents should not send a fragment with the URI, but
820 // if they do, and the web server passes it on to us, we
821 // need to strip it or we get false-positive redirect loops
822 // or weird output URLs
823 $hash = strpos( $base, '#' );
824 if ( $hash !== false ) {
825 $base = substr( $base, 0, $hash );
826 }
827
828 if ( $base[0] == '/' ) {
829 // More than one slash will look like it is protocol relative
830 return preg_replace( '!^/+!', '/', $base );
831 } else {
832 // We may get paths with a host prepended; strip it.
833 return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
834 }
835 }
836
844 public function getRequestURL() {
845 return self::getGlobalRequestURL();
846 }
847
858 public function getFullRequestURL() {
859 return wfGetServerUrl( PROTO_CURRENT ) . $this->getRequestURL();
860 }
861
867 public function appendQueryValue( $key, $value ) {
868 return $this->appendQueryArray( [ $key => $value ] );
869 }
870
877 public function appendQueryArray( $array ) {
878 $newquery = $this->getQueryValues();
879 unset( $newquery['title'] );
880 $newquery = array_merge( $newquery, $array );
881
882 return wfArrayToCgi( $newquery );
883 }
884
894 public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
895 global $wgUser;
896
897 $limit = $this->getInt( 'limit', 0 );
898 if ( $limit < 0 ) {
899 $limit = 0;
900 }
901 if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
902 $limit = $wgUser->getIntOption( $optionname );
903 }
904 if ( $limit <= 0 ) {
905 $limit = $deflimit;
906 }
907 if ( $limit > 5000 ) {
908 $limit = 5000; # We have *some* limits...
909 }
910
911 $offset = $this->getInt( 'offset', 0 );
912 if ( $offset < 0 ) {
913 $offset = 0;
914 }
915
916 return [ $limit, $offset ];
917 }
918
925 public function getFileTempname( $key ) {
926 $file = new WebRequestUpload( $this, $key );
927 return $file->getTempName();
928 }
929
936 public function getUploadError( $key ) {
937 $file = new WebRequestUpload( $this, $key );
938 return $file->getError();
939 }
940
952 public function getFileName( $key ) {
953 $file = new WebRequestUpload( $this, $key );
954 return $file->getName();
955 }
956
963 public function getUpload( $key ) {
964 return new WebRequestUpload( $this, $key );
965 }
966
973 public function response() {
974 /* Lazy initialization of response object for this request */
975 if ( !is_object( $this->response ) ) {
976 $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
977 $this->response = new $class();
978 }
979 return $this->response;
980 }
981
985 protected function initHeaders() {
986 if ( count( $this->headers ) ) {
987 return;
988 }
989
990 $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
991 if ( $apacheHeaders ) {
992 foreach ( $apacheHeaders as $tempName => $tempValue ) {
993 $this->headers[strtoupper( $tempName )] = $tempValue;
994 }
995 } else {
996 foreach ( $_SERVER as $name => $value ) {
997 if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
998 $name = str_replace( '_', '-', substr( $name, 5 ) );
999 $this->headers[$name] = $value;
1000 } elseif ( $name === 'CONTENT_LENGTH' ) {
1001 $this->headers['CONTENT-LENGTH'] = $value;
1002 }
1003 }
1004 }
1005 }
1006
1012 public function getAllHeaders() {
1013 $this->initHeaders();
1014 return $this->headers;
1015 }
1016
1029 public function getHeader( $name, $flags = 0 ) {
1030 $this->initHeaders();
1031 $name = strtoupper( $name );
1032 if ( !isset( $this->headers[$name] ) ) {
1033 return false;
1034 }
1035 $value = $this->headers[$name];
1036 if ( $flags & self::GETHEADER_LIST ) {
1037 $value = array_map( 'trim', explode( ',', $value ) );
1038 }
1039 return $value;
1040 }
1041
1049 public function getSessionData( $key ) {
1050 return $this->getSession()->get( $key );
1051 }
1052
1060 public function setSessionData( $key, $data ) {
1061 $this->getSession()->set( $key, $data );
1062 }
1063
1074 public function checkUrlExtension( $extWhitelist = [] ) {
1075 $extWhitelist[] = 'php';
1076 if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
1077 if ( !$this->wasPosted() ) {
1079 $this->getFullRequestURL(), $extWhitelist );
1080 if ( $newUrl !== false ) {
1081 $this->doSecurityRedirect( $newUrl );
1082 return false;
1083 }
1084 }
1085 throw new HttpError( 403,
1086 'Invalid file extension found in the path info or query string.' );
1087 }
1088 return true;
1089 }
1090
1098 protected function doSecurityRedirect( $url ) {
1099 header( 'Location: ' . $url );
1100 header( 'Content-Type: text/html' );
1101 $encUrl = htmlspecialchars( $url );
1102 echo <<<HTML
1103<!DOCTYPE html>
1104<html>
1105<head>
1106<title>Security redirect</title>
1107</head>
1108<body>
1109<h1>Security redirect</h1>
1110<p>
1111We can't serve non-HTML content from the URL you have requested, because
1112Internet Explorer would interpret it as an incorrect and potentially dangerous
1113content type.</p>
1114<p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1115URL you have requested, except that "&amp;*" is appended. This prevents Internet
1116Explorer from seeing a bogus file extension.
1117</p>
1118</body>
1119</html>
1120HTML;
1121 echo "\n";
1122 return true;
1123 }
1124
1134 public function getAcceptLang() {
1135 // Modified version of code found at
1136 // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1137 $acceptLang = $this->getHeader( 'Accept-Language' );
1138 if ( !$acceptLang ) {
1139 return [];
1140 }
1141
1142 // Return the language codes in lower case
1143 $acceptLang = strtolower( $acceptLang );
1144
1145 // Break up string into pieces (languages and q factors)
1146 $lang_parse = null;
1147 preg_match_all(
1148 '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1149 $acceptLang,
1150 $lang_parse
1151 );
1152
1153 if ( !count( $lang_parse[1] ) ) {
1154 return [];
1155 }
1156
1157 $langcodes = $lang_parse[1];
1158 $qvalues = $lang_parse[4];
1159 $indices = range( 0, count( $lang_parse[1] ) - 1 );
1160
1161 // Set default q factor to 1
1162 foreach ( $indices as $index ) {
1163 if ( $qvalues[$index] === '' ) {
1164 $qvalues[$index] = 1;
1165 } elseif ( $qvalues[$index] == 0 ) {
1166 unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1167 }
1168 }
1169
1170 // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1171 array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1172
1173 // Create a list like "en" => 0.8
1174 $langs = array_combine( $langcodes, $qvalues );
1175
1176 return $langs;
1177 }
1178
1187 protected function getRawIP() {
1188 if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1189 return null;
1190 }
1191
1192 if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1193 throw new MWException( __METHOD__
1194 . " : Could not determine the remote IP address due to multiple values." );
1195 } else {
1196 $ipchain = $_SERVER['REMOTE_ADDR'];
1197 }
1198
1199 return IP::canonicalize( $ipchain );
1200 }
1201
1211 public function getIP() {
1212 global $wgUsePrivateIPs;
1213
1214 # Return cached result
1215 if ( $this->ip !== null ) {
1216 return $this->ip;
1217 }
1218
1219 # collect the originating ips
1220 $ip = $this->getRawIP();
1221 if ( !$ip ) {
1222 throw new MWException( 'Unable to determine IP.' );
1223 }
1224
1225 # Append XFF
1226 $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1227 if ( $forwardedFor !== false ) {
1228 $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1229 $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1230 $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1231 $ipchain = array_reverse( $ipchain );
1232 array_unshift( $ipchain, $ip );
1233
1234 # Step through XFF list and find the last address in the list which is a
1235 # trusted server. Set $ip to the IP address given by that trusted server,
1236 # unless the address is not sensible (e.g. private). However, prefer private
1237 # IP addresses over proxy servers controlled by this site (more sensible).
1238 # Note that some XFF values might be "unknown" with Squid/Varnish.
1239 foreach ( $ipchain as $i => $curIP ) {
1240 $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1241 if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1242 || !$proxyLookup->isTrustedProxy( $curIP )
1243 ) {
1244 break; // IP is not valid/trusted or does not point to anything
1245 }
1246 if (
1247 IP::isPublic( $ipchain[$i + 1] ) ||
1248 $wgUsePrivateIPs ||
1249 $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1250 ) {
1251 // Follow the next IP according to the proxy
1252 $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1253 if ( !$nextIP && $isConfigured ) {
1254 // We have not yet made it past CDN/proxy servers of this site,
1255 // so either they are misconfigured or there is some IP spoofing.
1256 throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1257 }
1258 $ip = $nextIP;
1259 // keep traversing the chain
1260 continue;
1261 }
1262 break;
1263 }
1264 }
1265
1266 # Allow extensions to improve our guess
1267 Hooks::run( 'GetIP', [ &$ip ] );
1268
1269 if ( !$ip ) {
1270 throw new MWException( "Unable to determine IP." );
1271 }
1272
1273 wfDebug( "IP: $ip\n" );
1274 $this->ip = $ip;
1275 return $ip;
1276 }
1277
1283 public function setIP( $ip ) {
1284 $this->ip = $ip;
1285 }
1286
1299 public function hasSafeMethod() {
1300 if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1301 return false; // CLI mode
1302 }
1303
1304 return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1305 }
1306
1325 public function isSafeRequest() {
1326 if ( $this->markedAsSafe && $this->wasPosted() ) {
1327 return true; // marked as a "safe" POST
1328 }
1329
1330 return $this->hasSafeMethod();
1331 }
1332
1343 public function markAsSafeRequest() {
1344 $this->markedAsSafe = true;
1345 }
1346}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second redirect
$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.
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.
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....
WebRequest clone which takes values from a provided array.
Show an error that looks like an HTTP server error.
Definition HttpError.php:30
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...
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters,...
A collection of public static functions to play with IP address and IP ranges.
Definition IP.php:67
Internationalisation code.
Definition Language.php:35
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:38
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.
Object to access the $_FILES array.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
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.
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.
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=[])
Check if Internet Explorer will detect an incorrect cache extension in PATH_INFO or QUERY_STRING.
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.
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.
getRawPostString()
Return the contents of the POST with no decoding.
getQueryValues()
Get the values passed in the query string.
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff,...
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.
doSecurityRedirect( $url)
Attempt to redirect to a URL with a QUERY_STRING that's not dangerous in IE 6.
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.
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.
setSessionId(SessionId $sessionId)
Set the session for this request.
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
getLimitOffset( $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given.
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.
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.
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
const PROTO_CURRENT
Definition Defines.php:222
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition hooks.txt:2054
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
this hook is for auditing only $response
Definition hooks.txt:813
$wgActionPaths
Definition img_auth.php:47
$wgArticlePath
Definition img_auth.php:46
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
title
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN boolean columns are always mapped to as the code does not always treat the column as a and VARBINARY columns should simply be TEXT The only exception is when VARBINARY is used to store true binary data
Definition postgres.txt:43
if(is_array($mode)) switch( $mode) $input