MediaWiki  master
WebRequest.php
Go to the documentation of this file.
1 <?php
29 
37 class WebRequest {
38  protected $data, $headers = [];
39 
44  const GETHEADER_LIST = 1;
45 
50  private static $reqId;
51 
56  private $response;
57 
62  private $ip;
63 
68  protected $requestTime;
69 
74  protected $protocol;
75 
81  protected $sessionId = null;
82 
84  protected $markedAsSafe = false;
85 
86  public function __construct() {
87  $this->requestTime = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
88  ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
89 
90  // POST overrides GET data
91  // We don't use $_REQUEST here to avoid interference from cookies...
92  $this->data = $_POST + $_GET;
93  }
94 
110  public static function getPathInfo( $want = 'all' ) {
112  // PATH_INFO is mangled due to http://bugs.php.net/bug.php?id=31892
113  // And also by Apache 2.x, double slashes are converted to single slashes.
114  // So we will use REQUEST_URI if possible.
115  $matches = [];
116  if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
117  // Slurp out the path portion to examine...
118  $url = $_SERVER['REQUEST_URI'];
119  if ( !preg_match( '!^https?://!', $url ) ) {
120  $url = 'http://unused' . $url;
121  }
122  MediaWiki\suppressWarnings();
123  $a = parse_url( $url );
124  MediaWiki\restoreWarnings();
125  if ( $a ) {
126  $path = isset( $a['path'] ) ? $a['path'] : '';
127 
129  if ( $path == $wgScript && $want !== 'all' ) {
130  // Script inside a rewrite path?
131  // Abort to keep from breaking...
132  return $matches;
133  }
134 
135  $router = new PathRouter;
136 
137  // Raw PATH_INFO style
138  $router->add( "$wgScript/$1" );
139 
140  if ( isset( $_SERVER['SCRIPT_NAME'] )
141  && preg_match( '/\.php5?/', $_SERVER['SCRIPT_NAME'] )
142  ) {
143  # Check for SCRIPT_NAME, we handle index.php explicitly
144  # But we do have some other .php files such as img_auth.php
145  # Don't let root article paths clober the parsing for them
146  $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" );
147  }
148 
150  if ( $wgArticlePath ) {
151  $router->add( $wgArticlePath );
152  }
153 
155  if ( $wgActionPaths ) {
156  $router->add( $wgActionPaths, [ 'action' => '$key' ] );
157  }
158 
160  if ( $wgVariantArticlePath ) {
161  $router->add( $wgVariantArticlePath,
162  [ 'variant' => '$2' ],
163  [ '$2' => $wgContLang->getVariants() ]
164  );
165  }
166 
167  Hooks::run( 'WebRequestPathInfoRouter', [ $router ] );
168 
169  $matches = $router->parse( $path );
170  }
171  } elseif ( $wgUsePathInfo ) {
172  if ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) {
173  // Mangled PATH_INFO
174  // http://bugs.php.net/bug.php?id=31892
175  // Also reported when ini_get('cgi.fix_pathinfo')==false
176  $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
177 
178  } elseif ( isset( $_SERVER['PATH_INFO'] ) && $_SERVER['PATH_INFO'] != '' ) {
179  // Regular old PATH_INFO yay
180  $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
181  }
182  }
183 
184  return $matches;
185  }
186 
193  public static function detectServer() {
195 
196  $proto = self::detectProtocol();
197  $stdPort = $proto === 'https' ? 443 : 80;
198 
199  $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
200  $host = 'localhost';
201  $port = $stdPort;
202  foreach ( $varNames as $varName ) {
203  if ( !isset( $_SERVER[$varName] ) ) {
204  continue;
205  }
206 
207  $parts = IP::splitHostAndPort( $_SERVER[$varName] );
208  if ( !$parts ) {
209  // Invalid, do not use
210  continue;
211  }
212 
213  $host = $parts[0];
214  if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
215  // Bug 70021: Assume that upstream proxy is running on the default
216  // port based on the protocol. We have no reliable way to determine
217  // the actual port in use upstream.
218  $port = $stdPort;
219  } elseif ( $parts[1] === false ) {
220  if ( isset( $_SERVER['SERVER_PORT'] ) ) {
221  $port = $_SERVER['SERVER_PORT'];
222  } // else leave it as $stdPort
223  } else {
224  $port = $parts[1];
225  }
226  break;
227  }
228 
229  return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort );
230  }
231 
239  public static function detectProtocol() {
240  if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
241  ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
242  $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
243  return 'https';
244  } else {
245  return 'http';
246  }
247  }
248 
256  public function getElapsedTime() {
257  return microtime( true ) - $this->requestTime;
258  }
259 
268  public static function getRequestId() {
269  if ( !self::$reqId ) {
270  self::$reqId = isset( $_SERVER['UNIQUE_ID'] )
271  ? $_SERVER['UNIQUE_ID'] : wfRandomString( 24 );
272  }
273 
274  return self::$reqId;
275  }
276 
284  public static function overrideRequestId( $id ) {
285  self::$reqId = $id;
286  }
287 
292  public function getProtocol() {
293  if ( $this->protocol === null ) {
294  $this->protocol = self::detectProtocol();
295  }
296  return $this->protocol;
297  }
298 
306  public function interpolateTitle() {
307  // bug 16019: title interpolation on API queries is useless and sometimes harmful
308  if ( defined( 'MW_API' ) ) {
309  return;
310  }
311 
312  $matches = self::getPathInfo( 'title' );
313  foreach ( $matches as $key => $val ) {
314  $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
315  }
316  }
317 
328  static function extractTitle( $path, $bases, $key = false ) {
329  foreach ( (array)$bases as $keyValue => $base ) {
330  // Find the part after $wgArticlePath
331  $base = str_replace( '$1', '', $base );
332  $baseLen = strlen( $base );
333  if ( substr( $path, 0, $baseLen ) == $base ) {
334  $raw = substr( $path, $baseLen );
335  if ( $raw !== '' ) {
336  $matches = [ 'title' => rawurldecode( $raw ) ];
337  if ( $key ) {
338  $matches[$key] = $keyValue;
339  }
340  return $matches;
341  }
342  }
343  }
344  return [];
345  }
346 
354  function normalizeUnicode( $data ) {
355  if ( is_array( $data ) ) {
356  foreach ( $data as $key => $val ) {
357  $data[$key] = $this->normalizeUnicode( $val );
358  }
359  } else {
361  $data = isset( $wgContLang ) ?
362  $wgContLang->normalize( $data ) :
363  UtfNormal\Validator::cleanUp( $data );
364  }
365  return $data;
366  }
367 
376  private function getGPCVal( $arr, $name, $default ) {
377  # PHP is so nice to not touch input data, except sometimes:
378  # http://us2.php.net/variables.external#language.variables.external.dot-in-names
379  # Work around PHP *feature* to avoid *bugs* elsewhere.
380  $name = strtr( $name, '.', '_' );
381  if ( isset( $arr[$name] ) ) {
383  $data = $arr[$name];
384  if ( isset( $_GET[$name] ) && !is_array( $data ) ) {
385  # Check for alternate/legacy character encoding.
386  if ( isset( $wgContLang ) ) {
387  $data = $wgContLang->checkTitleEncoding( $data );
388  }
389  }
390  $data = $this->normalizeUnicode( $data );
391  return $data;
392  } else {
393  return $default;
394  }
395  }
396 
407  public function getVal( $name, $default = null ) {
408  $val = $this->getGPCVal( $this->data, $name, $default );
409  if ( is_array( $val ) ) {
410  $val = $default;
411  }
412  if ( is_null( $val ) ) {
413  return $val;
414  } else {
415  return (string)$val;
416  }
417  }
418 
426  public function setVal( $key, $value ) {
427  $ret = isset( $this->data[$key] ) ? $this->data[$key] : null;
428  $this->data[$key] = $value;
429  return $ret;
430  }
431 
438  public function unsetVal( $key ) {
439  if ( !isset( $this->data[$key] ) ) {
440  $ret = null;
441  } else {
442  $ret = $this->data[$key];
443  unset( $this->data[$key] );
444  }
445  return $ret;
446  }
447 
457  public function getArray( $name, $default = null ) {
458  $val = $this->getGPCVal( $this->data, $name, $default );
459  if ( is_null( $val ) ) {
460  return null;
461  } else {
462  return (array)$val;
463  }
464  }
465 
476  public function getIntArray( $name, $default = null ) {
477  $val = $this->getArray( $name, $default );
478  if ( is_array( $val ) ) {
479  $val = array_map( 'intval', $val );
480  }
481  return $val;
482  }
483 
493  public function getInt( $name, $default = 0 ) {
494  return intval( $this->getVal( $name, $default ) );
495  }
496 
505  public function getIntOrNull( $name ) {
506  $val = $this->getVal( $name );
507  return is_numeric( $val )
508  ? intval( $val )
509  : null;
510  }
511 
522  public function getFloat( $name, $default = 0.0 ) {
523  return floatval( $this->getVal( $name, $default ) );
524  }
525 
535  public function getBool( $name, $default = false ) {
536  return (bool)$this->getVal( $name, $default );
537  }
538 
548  public function getFuzzyBool( $name, $default = false ) {
549  return $this->getBool( $name, $default ) && strcasecmp( $this->getVal( $name ), 'false' ) !== 0;
550  }
551 
560  public function getCheck( $name ) {
561  # Checkboxes and buttons are only present when clicked
562  # Presence connotes truth, absence false
563  return $this->getVal( $name, null ) !== null;
564  }
565 
576  public function getText( $name, $default = '' ) {
577  $val = $this->getVal( $name, $default );
578  return str_replace( "\r\n", "\n", $val );
579  }
580 
588  public function getValues() {
589  $names = func_get_args();
590  if ( count( $names ) == 0 ) {
591  $names = array_keys( $this->data );
592  }
593 
594  $retVal = [];
595  foreach ( $names as $name ) {
596  $value = $this->getGPCVal( $this->data, $name, null );
597  if ( !is_null( $value ) ) {
598  $retVal[$name] = $value;
599  }
600  }
601  return $retVal;
602  }
603 
610  public function getValueNames( $exclude = [] ) {
611  return array_diff( array_keys( $this->getValues() ), $exclude );
612  }
613 
620  public function getQueryValues() {
621  return $_GET;
622  }
623 
630  public function getRawQueryString() {
631  return $_SERVER['QUERY_STRING'];
632  }
633 
640  public function getRawPostString() {
641  if ( !$this->wasPosted() ) {
642  return '';
643  }
644  return $this->getRawInput();
645  }
646 
654  public function getRawInput() {
655  static $input = null;
656  if ( $input === null ) {
657  $input = file_get_contents( 'php://input' );
658  }
659  return $input;
660  }
661 
667  public function getMethod() {
668  return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'GET';
669  }
670 
680  public function wasPosted() {
681  return $this->getMethod() == 'POST';
682  }
683 
691  public function getSession() {
692  if ( $this->sessionId !== null ) {
693  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
694  if ( $session ) {
695  return $session;
696  }
697  }
698 
699  $session = SessionManager::singleton()->getSessionForRequest( $this );
700  $this->sessionId = $session->getSessionId();
701  return $session;
702  }
703 
710  public function setSessionId( SessionId $sessionId ) {
711  $this->sessionId = $sessionId;
712  }
713 
720  public function getSessionId() {
721  return $this->sessionId;
722  }
723 
733  public function checkSessionCookie() {
735  wfDeprecated( __METHOD__, '1.27' );
736  return $wgInitialSessionId !== null &&
737  $this->getSession()->getId() === (string)$wgInitialSessionId;
738  }
739 
748  public function getCookie( $key, $prefix = null, $default = null ) {
749  if ( $prefix === null ) {
751  $prefix = $wgCookiePrefix;
752  }
753  return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
754  }
755 
763  public static function getGlobalRequestURL() {
764  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
765  $base = $_SERVER['REQUEST_URI'];
766  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
767  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
768  ) {
769  // Probably IIS; doesn't set REQUEST_URI
770  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
771  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
772  $base = $_SERVER['SCRIPT_NAME'];
773  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
774  $base .= '?' . $_SERVER['QUERY_STRING'];
775  }
776  } else {
777  // This shouldn't happen!
778  throw new MWException( "Web server doesn't provide either " .
779  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
780  "of your web server configuration to https://phabricator.wikimedia.org/" );
781  }
782  // User-agents should not send a fragment with the URI, but
783  // if they do, and the web server passes it on to us, we
784  // need to strip it or we get false-positive redirect loops
785  // or weird output URLs
786  $hash = strpos( $base, '#' );
787  if ( $hash !== false ) {
788  $base = substr( $base, 0, $hash );
789  }
790 
791  if ( $base[0] == '/' ) {
792  // More than one slash will look like it is protocol relative
793  return preg_replace( '!^/+!', '/', $base );
794  } else {
795  // We may get paths with a host prepended; strip it.
796  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
797  }
798  }
799 
807  public function getRequestURL() {
808  return self::getGlobalRequestURL();
809  }
810 
821  public function getFullRequestURL() {
822  return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT );
823  }
824 
830  public function appendQueryValue( $key, $value ) {
831  return $this->appendQueryArray( [ $key => $value ] );
832  }
833 
840  public function appendQueryArray( $array ) {
841  $newquery = $this->getQueryValues();
842  unset( $newquery['title'] );
843  $newquery = array_merge( $newquery, $array );
844 
845  return wfArrayToCgi( $newquery );
846  }
847 
857  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
858  global $wgUser;
859 
860  $limit = $this->getInt( 'limit', 0 );
861  if ( $limit < 0 ) {
862  $limit = 0;
863  }
864  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
865  $limit = $wgUser->getIntOption( $optionname );
866  }
867  if ( $limit <= 0 ) {
868  $limit = $deflimit;
869  }
870  if ( $limit > 5000 ) {
871  $limit = 5000; # We have *some* limits...
872  }
873 
874  $offset = $this->getInt( 'offset', 0 );
875  if ( $offset < 0 ) {
876  $offset = 0;
877  }
878 
879  return [ $limit, $offset ];
880  }
881 
888  public function getFileTempname( $key ) {
889  $file = new WebRequestUpload( $this, $key );
890  return $file->getTempName();
891  }
892 
899  public function getUploadError( $key ) {
900  $file = new WebRequestUpload( $this, $key );
901  return $file->getError();
902  }
903 
915  public function getFileName( $key ) {
916  $file = new WebRequestUpload( $this, $key );
917  return $file->getName();
918  }
919 
926  public function getUpload( $key ) {
927  return new WebRequestUpload( $this, $key );
928  }
929 
936  public function response() {
937  /* Lazy initialization of response object for this request */
938  if ( !is_object( $this->response ) ) {
939  $class = ( $this instanceof FauxRequest ) ? 'FauxResponse' : 'WebResponse';
940  $this->response = new $class();
941  }
942  return $this->response;
943  }
944 
948  protected function initHeaders() {
949  if ( count( $this->headers ) ) {
950  return;
951  }
952 
953  $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
954  if ( $apacheHeaders ) {
955  foreach ( $apacheHeaders as $tempName => $tempValue ) {
956  $this->headers[strtoupper( $tempName )] = $tempValue;
957  }
958  } else {
959  foreach ( $_SERVER as $name => $value ) {
960  if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
961  $name = str_replace( '_', '-', substr( $name, 5 ) );
962  $this->headers[$name] = $value;
963  } elseif ( $name === 'CONTENT_LENGTH' ) {
964  $this->headers['CONTENT-LENGTH'] = $value;
965  }
966  }
967  }
968  }
969 
975  public function getAllHeaders() {
976  $this->initHeaders();
977  return $this->headers;
978  }
979 
992  public function getHeader( $name, $flags = 0 ) {
993  $this->initHeaders();
994  $name = strtoupper( $name );
995  if ( !isset( $this->headers[$name] ) ) {
996  return false;
997  }
998  $value = $this->headers[$name];
999  if ( $flags & self::GETHEADER_LIST ) {
1000  $value = array_map( 'trim', explode( ',', $value ) );
1001  }
1002  return $value;
1003  }
1004 
1012  public function getSessionData( $key ) {
1013  return $this->getSession()->get( $key );
1014  }
1015 
1023  public function setSessionData( $key, $data ) {
1024  $this->getSession()->set( $key, $data );
1025  }
1026 
1037  public function checkUrlExtension( $extWhitelist = [] ) {
1038  $extWhitelist[] = 'php';
1039  if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
1040  if ( !$this->wasPosted() ) {
1041  $newUrl = IEUrlExtension::fixUrlForIE6(
1042  $this->getFullRequestURL(), $extWhitelist );
1043  if ( $newUrl !== false ) {
1044  $this->doSecurityRedirect( $newUrl );
1045  return false;
1046  }
1047  }
1048  throw new HttpError( 403,
1049  'Invalid file extension found in the path info or query string.' );
1050  }
1051  return true;
1052  }
1053 
1061  protected function doSecurityRedirect( $url ) {
1062  header( 'Location: ' . $url );
1063  header( 'Content-Type: text/html' );
1064  $encUrl = htmlspecialchars( $url );
1065  echo <<<HTML
1066 <html>
1067 <head>
1068 <title>Security redirect</title>
1069 </head>
1070 <body>
1071 <h1>Security redirect</h1>
1072 <p>
1073 We can't serve non-HTML content from the URL you have requested, because
1074 Internet Explorer would interpret it as an incorrect and potentially dangerous
1075 content type.</p>
1076 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1077 URL you have requested, except that "&amp;*" is appended. This prevents Internet
1078 Explorer from seeing a bogus file extension.
1079 </p>
1080 </body>
1081 </html>
1082 HTML;
1083  echo "\n";
1084  return true;
1085  }
1086 
1096  public function getAcceptLang() {
1097  // Modified version of code found at
1098  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1099  $acceptLang = $this->getHeader( 'Accept-Language' );
1100  if ( !$acceptLang ) {
1101  return [];
1102  }
1103 
1104  // Return the language codes in lower case
1105  $acceptLang = strtolower( $acceptLang );
1106 
1107  // Break up string into pieces (languages and q factors)
1108  $lang_parse = null;
1109  preg_match_all(
1110  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1111  $acceptLang,
1112  $lang_parse
1113  );
1114 
1115  if ( !count( $lang_parse[1] ) ) {
1116  return [];
1117  }
1118 
1119  $langcodes = $lang_parse[1];
1120  $qvalues = $lang_parse[4];
1121  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1122 
1123  // Set default q factor to 1
1124  foreach ( $indices as $index ) {
1125  if ( $qvalues[$index] === '' ) {
1126  $qvalues[$index] = 1;
1127  } elseif ( $qvalues[$index] == 0 ) {
1128  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1129  }
1130  }
1131 
1132  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1133  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1134 
1135  // Create a list like "en" => 0.8
1136  $langs = array_combine( $langcodes, $qvalues );
1137 
1138  return $langs;
1139  }
1140 
1149  protected function getRawIP() {
1150  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1151  return null;
1152  }
1153 
1154  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1155  throw new MWException( __METHOD__
1156  . " : Could not determine the remote IP address due to multiple values." );
1157  } else {
1158  $ipchain = $_SERVER['REMOTE_ADDR'];
1159  }
1160 
1161  return IP::canonicalize( $ipchain );
1162  }
1163 
1173  public function getIP() {
1174  global $wgUsePrivateIPs;
1175 
1176  # Return cached result
1177  if ( $this->ip !== null ) {
1178  return $this->ip;
1179  }
1180 
1181  # collect the originating ips
1182  $ip = $this->getRawIP();
1183  if ( !$ip ) {
1184  throw new MWException( 'Unable to determine IP.' );
1185  }
1186 
1187  # Append XFF
1188  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1189  if ( $forwardedFor !== false ) {
1190  $isConfigured = IP::isConfiguredProxy( $ip );
1191  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1192  $ipchain = array_reverse( $ipchain );
1193  array_unshift( $ipchain, $ip );
1194 
1195  # Step through XFF list and find the last address in the list which is a
1196  # trusted server. Set $ip to the IP address given by that trusted server,
1197  # unless the address is not sensible (e.g. private). However, prefer private
1198  # IP addresses over proxy servers controlled by this site (more sensible).
1199  # Note that some XFF values might be "unknown" with Squid/Varnish.
1200  foreach ( $ipchain as $i => $curIP ) {
1201  $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1202  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1203  || !IP::isTrustedProxy( $curIP )
1204  ) {
1205  break; // IP is not valid/trusted or does not point to anything
1206  }
1207  if (
1208  IP::isPublic( $ipchain[$i + 1] ) ||
1209  $wgUsePrivateIPs ||
1210  IP::isConfiguredProxy( $curIP ) // bug 48919; treat IP as sane
1211  ) {
1212  // Follow the next IP according to the proxy
1213  $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1214  if ( !$nextIP && $isConfigured ) {
1215  // We have not yet made it past CDN/proxy servers of this site,
1216  // so either they are misconfigured or there is some IP spoofing.
1217  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1218  }
1219  $ip = $nextIP;
1220  // keep traversing the chain
1221  continue;
1222  }
1223  break;
1224  }
1225  }
1226 
1227  # Allow extensions to improve our guess
1228  Hooks::run( 'GetIP', [ &$ip ] );
1229 
1230  if ( !$ip ) {
1231  throw new MWException( "Unable to determine IP." );
1232  }
1233 
1234  wfDebug( "IP: $ip\n" );
1235  $this->ip = $ip;
1236  return $ip;
1237  }
1238 
1244  public function setIP( $ip ) {
1245  $this->ip = $ip;
1246  }
1247 
1260  public function hasSafeMethod() {
1261  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1262  return false; // CLI mode
1263  }
1264 
1265  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1266  }
1267 
1286  public function isSafeRequest() {
1287  if ( $this->markedAsSafe && $this->wasPosted() ) {
1288  return true; // marked as a "safe" POST
1289  }
1290 
1291  return $this->hasSafeMethod();
1292  }
1293 
1304  public function markAsSafeRequest() {
1305  $this->markedAsSafe = true;
1306  }
1307 }
$wgInitialSessionId
Definition: Setup.php:737
float $requestTime
The timestamp of the start of the request, with microsecond precision.
Definition: WebRequest.php:68
the array() calling protocol came about after MediaWiki 1.4rc1.
getUploadError($key)
Return the upload error or 0.
Definition: WebRequest.php:899
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:640
$wgScript
The URL path to index.php.
I won t presume to tell you how to I m just describing the methods I chose to use for myself If you do choose to follow these it will probably be easier for you to collaborate with others on the but if you want to contribute without by all means do which work well I also use K &R brace matching style I know that s a religious issue for some
Definition: design.txt:79
static getRequestId()
Get the unique request ID.
Definition: WebRequest.php:268
getFileTempname($key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:888
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
Definition: WebRequest.php:821
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
$wgActionPaths
Definition: img_auth.php:46
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...
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:37
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff...
Definition: WebRequest.php:936
getUpload($key)
Return a WebRequestUpload object corresponding to the key.
Definition: WebRequest.php:926
getIntOrNull($name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:505
string $protocol
Cached URL protocol.
Definition: WebRequest.php:74
getFuzzyBool($name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:548
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:177
$value
getBool($name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:535
getGPCVal($arr, $name, $default)
Fetch a value from the given array or return $default if it's not set.
Definition: WebRequest.php:376
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
Definition: hooks.txt:6
const PROTO_CURRENT
Definition: Defines.php:265
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2588
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
Definition: WebRequest.php:44
getSession()
Return the session for this request.
Definition: WebRequest.php:691
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
wfExpandUrl($url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
$wgArticlePath
Definition: img_auth.php:45
add($path, $params=[], $options=[])
Add a new path pattern to the path router.
Definition: PathRouter.php:160
initHeaders()
Initialise the header list.
Definition: WebRequest.php:948
string $ip
Cached client IP address.
Definition: WebRequest.php:62
wfRandomString($length=32)
Get a random string containing a number of pseudo-random hex characters.
doSecurityRedirect($url)
Attempt to redirect to a URL with a QUERY_STRING that's not dangerous in IE 6.
getArray($name, $default=null)
Fetch an array from the input or return $default if it's not set.
Definition: WebRequest.php:457
Show an error that looks like an HTTP server error.
Definition: HttpError.php:30
getAllHeaders()
Get an array containing all request headers.
Definition: WebRequest.php:975
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
Definition: WebRequest.php:193
setVal($key, $value)
Set an arbitrary value into our get/post data.
Definition: WebRequest.php:426
getHeader($name, $flags=0)
Get a request header, or false if it isn't set.
Definition: WebRequest.php:992
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add headers
Definition: design.txt:12
getFloat($name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:522
getProtocol()
Get the current URL protocol (http or https)
Definition: WebRequest.php:292
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET...
Definition: WebRequest.php:680
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:807
PathRouter class.
Definition: PathRouter.php:73
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
Definition: WebRequest.php:84
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...
Definition: IP.php:303
getSessionData($key)
Get data from the session.
getQueryValues()
Get the values passed in the query string.
Definition: WebRequest.php:620
MediaWiki exception.
Definition: MWException.php:26
unsetVal($key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:438
getCheck($name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:560
Internationalisation code.
Definition: Language.php:39
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode...
getFileName($key)
Return the original filename of the uploaded file, as reported by the submitting user agent...
Definition: WebRequest.php:915
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Manages data for an an authenticated session.
Definition: Session.php:48
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
static overrideRequestId($id)
Override the unique request ID.
Definition: WebRequest.php:284
getValueNames($exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:610
static detectProtocol()
Detect the protocol from $_SERVER.
Definition: WebRequest.php:239
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:1816
getSessionId()
Get the session id for this request, if any.
Definition: WebRequest.php:720
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
Definition: distributors.txt:9
getIntArray($name, $default=null)
Fetch an array of integers, or return $default if it's not set.
Definition: WebRequest.php:476
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:588
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:710
WebResponse $response
Lazy-init response object.
Definition: WebRequest.php:56
getVal($name, $default=null)
Fetch a scalar from the input or return $default if it's not set.
Definition: WebRequest.php:407
static splitHostAndPort($both)
Given a host/port string, like one might find in the host part of a URL per RFC 2732, split the hostname part and the port part and return an array with an element for each.
Definition: IP.php:254
SessionId null $sessionId
Session ID to use for this request.
Definition: WebRequest.php:81
A collection of public static functions to play with IP address and IP blocks.
Definition: IP.php:67
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
Definition: WebRequest.php:763
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:35
getMethod()
Get the HTTP method used for this request.
Definition: WebRequest.php:667
appendQueryValue($key, $value)
Definition: WebRequest.php:830
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:33
Value object holding the session ID in a manner that can be globally updated.
Definition: SessionId.php:38
wfArrayToCgi($array1, $array2=null, $prefix= '')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
static extractTitle($path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
Definition: WebRequest.php:328
to move a page</td >< td > &*You are moving the page across *A non empty talk page already exists under the new or *You uncheck the box below In those you will have to move or merge the page manually if desired</td >< td > be sure to &You are responsible for making sure that links continue to point where they are supposed to go Note that the page will &a page at the new title
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1020
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
Definition: design.txt:56
getRawInput()
Return the raw request body, with no processing.
Definition: WebRequest.php:654
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables...
Definition: WebRequest.php:306
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
checkSessionCookie()
Returns true if the request has a persistent session.
Definition: WebRequest.php:733
getCookie($key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:748
This serves as the entry point to the MediaWiki session handling system.
static getPathInfo($want= 'all')
Extract relevant query arguments from the http request uri's path to be merged with the normal php pr...
Definition: WebRequest.php:110
checkUrlExtension($extWhitelist=[])
Check if Internet Explorer will detect an incorrect cache extension in PATH_INFO or QUERY_STRING...
setSessionData($key, $data)
Set session data.
static string $reqId
The unique request ID.
Definition: WebRequest.php:50
getInt($name, $default=0)
Fetch an integer value from the input or return $default if not set.
Definition: WebRequest.php:493
appendQueryArray($array)
Appends or replaces value of query variables.
Definition: WebRequest.php:840
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds, with microsecond resolution.
Definition: WebRequest.php:256
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
getLimitOffset($deflimit=50, $optionname= 'rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given...
Definition: WebRequest.php:857
normalizeUnicode($data)
Recursively normalizes UTF-8 strings in the given array.
Definition: WebRequest.php:354
Object to access the $_FILES array.
getText($name, $default= '')
Fetch a text string from the given array or return $default if it's not set.
Definition: WebRequest.php:576
getRawQueryString()
Return the contents of the Query with no decoding.
Definition: WebRequest.php:630
$wgUser
Definition: Setup.php:801
$matches
static fixUrlForIE6($url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters...
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:310