Merge "Documentation: Remove paragraph about not creating a 2nd WebRequest"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 5 Jan 2016 22:23:55 +0000 (22:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 5 Jan 2016 22:23:55 +0000 (22:23 +0000)
1  2 
includes/WebRequest.php

diff --combined includes/WebRequest.php
   * URL or via a POSTed form stripping illegal input characters and
   * normalizing Unicode sequences.
   *
-  * Usually this is used via a global singleton, $wgRequest. You should
-  * not create a second WebRequest object; make a FauxRequest object if
-  * you want to pass arbitrary data to some function in place of the web
-  * input.
-  *
   * @ingroup HTTP
   */
  class WebRequest {
        protected $data, $headers = array();
  
 +      /**
 +       * Flag to make WebRequest::getHeader return an array of values.
 +       * @since 1.26
 +       */
 +      const GETHEADER_LIST = 1;
 +
        /**
         * Lazy-init response object
         * @var WebResponse
         */
        private $ip;
  
 +      /**
 +       * The timestamp of the start of the request, with microsecond precision.
 +       * @var float
 +       */
 +      protected $requestTime;
 +
        /**
         * Cached URL protocol
         * @var string
@@@ -69,8 -52,9 +64,8 @@@
        protected $protocol;
  
        public function __construct() {
 -              if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) {
 -                      throw new MWException( "MediaWiki does not function when magic quotes are enabled." );
 -              }
 +              $this->requestTime = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
 +                      ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
  
                // POST overrides GET data
                // We don't use $_REQUEST here to avoid interference from cookies...
                        if ( !preg_match( '!^https?://!', $url ) ) {
                                $url = 'http://unused' . $url;
                        }
 -                      wfSuppressWarnings();
 +                      MediaWiki\suppressWarnings();
                        $a = parse_url( $url );
 -                      wfRestoreWarnings();
 +                      MediaWiki\restoreWarnings();
                        if ( $a ) {
                                $path = isset( $a['path'] ) ? $a['path'] : '';
  
                                        );
                                }
  
 -                              wfRunHooks( 'WebRequestPathInfoRouter', array( $router ) );
 +                              Hooks::run( 'WebRequestPathInfoRouter', array( $router ) );
  
                                $matches = $router->parse( $path );
                        }
         * @return string
         */
        public static function detectServer() {
 +              global $wgAssumeProxiesUseDefaultProtocolPorts;
 +
                $proto = self::detectProtocol();
                $stdPort = $proto === 'https' ? 443 : 80;
  
                        if ( !isset( $_SERVER[$varName] ) ) {
                                continue;
                        }
 +
                        $parts = IP::splitHostAndPort( $_SERVER[$varName] );
                        if ( !$parts ) {
                                // Invalid, do not use
                                continue;
                        }
 +
                        $host = $parts[0];
 -                      if ( $parts[1] === false ) {
 +                      if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
 +                              // Bug 70021: Assume that upstream proxy is running on the default
 +                              // port based on the protocol. We have no reliable way to determine
 +                              // the actual port in use upstream.
 +                              $port = $stdPort;
 +                      } elseif ( $parts[1] === false ) {
                                if ( isset( $_SERVER['SERVER_PORT'] ) ) {
                                        $port = $_SERVER['SERVER_PORT'];
                                } // else leave it as $stdPort
         * @return array
         */
        public static function detectProtocol() {
 -              if ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) ||
 +              if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
                        ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
 -                      $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) ) {
 +                      $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
                        return 'https';
                } else {
                        return 'http';
                }
        }
  
 +      /**
 +       * Get the number of seconds to have elapsed since request start,
 +       * in fractional seconds, with microsecond resolution.
 +       *
 +       * @return float
 +       * @since 1.25
 +       */
 +      public function getElapsedTime() {
 +              return microtime( true ) - $this->requestTime;
 +      }
 +
        /**
         * Get the current URL protocol (http or https)
         * @return string
                        }
                } else {
                        global $wgContLang;
 -                      $data = isset( $wgContLang ) ? $wgContLang->normalize( $data ) : UtfNormal::cleanUp( $data );
 +                      $data = isset( $wgContLang ) ?
 +                              $wgContLang->normalize( $data ) :
 +                              UtfNormal\Validator::cleanUp( $data );
                }
                return $data;
        }
                        // This shouldn't happen!
                        throw new MWException( "Web server doesn't provide either " .
                                "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
 -                              "of your web server configuration to http://bugzilla.wikimedia.org/" );
 +                              "of your web server configuration to https://phabricator.wikimedia.org/" );
                }
                // User-agents should not send a fragment with the URI, but
                // if they do, and the web server passes it on to us, we
  
        /**
         * Take an arbitrary query and rewrite the present URL to include it
 +       * @deprecated Use appendQueryValue/appendQueryArray instead
         * @param string $query Query string fragment; do not include initial '?'
 -       *
         * @return string
         */
        public function appendQuery( $query ) {
 +              wfDeprecated( __METHOD__, '1.25' );
                return $this->appendQueryArray( wfCgiToArray( $query ) );
        }
  
        /**
         * @param string $key
         * @param string $value
 -       * @param bool $onlyquery
 +       * @param bool $onlyquery [deprecated]
         * @return string
         */
 -      public function appendQueryValue( $key, $value, $onlyquery = false ) {
 +      public function appendQueryValue( $key, $value, $onlyquery = true ) {
                return $this->appendQueryArray( array( $key => $value ), $onlyquery );
        }
  
         * Appends or replaces value of query variables.
         *
         * @param array $array Array of values to replace/add to query
 -       * @param bool $onlyquery Whether to only return the query string and not the complete URL
 +       * @param bool $onlyquery Whether to only return the query string
 +       *  and not the complete URL [deprecated]
         * @return string
         */
 -      public function appendQueryArray( $array, $onlyquery = false ) {
 +      public function appendQueryArray( $array, $onlyquery = true ) {
                global $wgTitle;
                $newquery = $this->getQueryValues();
                unset( $newquery['title'] );
                $newquery = array_merge( $newquery, $array );
                $query = wfArrayToCgi( $newquery );
 -              return $onlyquery ? $query : $wgTitle->getLocalURL( $query );
 +              if ( !$onlyquery ) {
 +                      wfDeprecated( __METHOD__, '1.25' );
 +                      return $wgTitle->getLocalURL( $query );
 +              }
 +
 +              return $query;
        }
  
        /**
         *
         * @param int $deflimit Limit to use if no input and the user hasn't set the option.
         * @param string $optionname To specify an option other than rclimit to pull from.
 -       * @return array First element is limit, second is offset
 +       * @return int[] First element is limit, second is offset
         */
        public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
                global $wgUser;
        /**
         * Initialise the header list
         */
 -      private function initHeaders() {
 +      protected function initHeaders() {
                if ( count( $this->headers ) ) {
                        return;
                }
        }
  
        /**
 -       * Get a request header, or false if it isn't set
 -       * @param string $name Case-insensitive header name
 +       * Get a request header, or false if it isn't set.
         *
 -       * @return string|bool False on failure
 -       */
 -      public function getHeader( $name ) {
 +       * @param string $name Case-insensitive header name
 +       * @param int $flags Bitwise combination of:
 +       *   WebRequest::GETHEADER_LIST  Treat the header as a comma-separated list
 +       *                               of values, as described in RFC 2616 ยง 4.2.
 +       *                               (since 1.26).
 +       * @return string|array|bool False if header is unset; otherwise the
 +       *  header value(s) as either a string (the default) or an array, if
 +       *  WebRequest::GETHEADER_LIST flag was set.
 +       */
 +      public function getHeader( $name, $flags = 0 ) {
                $this->initHeaders();
                $name = strtoupper( $name );
 -              if ( isset( $this->headers[$name] ) ) {
 -                      return $this->headers[$name];
 -              } else {
 +              if ( !isset( $this->headers[$name] ) ) {
                        return false;
                }
 +              $value = $this->headers[$name];
 +              if ( $flags & self::GETHEADER_LIST ) {
 +                      $value = array_map( 'trim', explode( ',', $value ) );
 +              }
 +              return $value;
        }
  
        /**
         * @return bool
         */
        public function checkUrlExtension( $extWhitelist = array() ) {
 -              global $wgScriptExtension;
 -              $extWhitelist[] = ltrim( $wgScriptExtension, '.' );
 +              $extWhitelist[] = 'php';
                if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
                        if ( !$this->wasPosted() ) {
                                $newUrl = IEUrlExtension::fixUrlForIE6(
@@@ -1155,7 -1102,7 +1150,7 @@@ HTML
                }
  
                # Allow extensions to improve our guess
 -              wfRunHooks( 'GetIP', array( &$ip ) );
 +              Hooks::run( 'GetIP', array( &$ip ) );
  
                if ( !$ip ) {
                        throw new MWException( "Unable to determine IP." );
                $this->ip = $ip;
        }
  }
 -
 -/**
 - * Object to access the $_FILES array
 - */
 -class WebRequestUpload {
 -      protected $request;
 -      protected $doesExist;
 -      protected $fileInfo;
 -
 -      /**
 -       * Constructor. Should only be called by WebRequest
 -       *
 -       * @param WebRequest $request The associated request
 -       * @param string $key Key in $_FILES array (name of form field)
 -       */
 -      public function __construct( $request, $key ) {
 -              $this->request = $request;
 -              $this->doesExist = isset( $_FILES[$key] );
 -              if ( $this->doesExist ) {
 -                      $this->fileInfo = $_FILES[$key];
 -              }
 -      }
 -
 -      /**
 -       * Return whether a file with this name was uploaded.
 -       *
 -       * @return bool
 -       */
 -      public function exists() {
 -              return $this->doesExist;
 -      }
 -
 -      /**
 -       * Return the original filename of the uploaded file
 -       *
 -       * @return string|null Filename or null if non-existent
 -       */
 -      public function getName() {
 -              if ( !$this->exists() ) {
 -                      return null;
 -              }
 -
 -              global $wgContLang;
 -              $name = $this->fileInfo['name'];
 -
 -              # Safari sends filenames in HTML-encoded Unicode form D...
 -              # Horrid and evil! Let's try to make some kind of sense of it.
 -              $name = Sanitizer::decodeCharReferences( $name );
 -              $name = $wgContLang->normalize( $name );
 -              wfDebug( __METHOD__ . ": {$this->fileInfo['name']} normalized to '$name'\n" );
 -              return $name;
 -      }
 -
 -      /**
 -       * Return the file size of the uploaded file
 -       *
 -       * @return int File size or zero if non-existent
 -       */
 -      public function getSize() {
 -              if ( !$this->exists() ) {
 -                      return 0;
 -              }
 -
 -              return $this->fileInfo['size'];
 -      }
 -
 -      /**
 -       * Return the path to the temporary file
 -       *
 -       * @return string|null Path or null if non-existent
 -       */
 -      public function getTempName() {
 -              if ( !$this->exists() ) {
 -                      return null;
 -              }
 -
 -              return $this->fileInfo['tmp_name'];
 -      }
 -
 -      /**
 -       * Return the upload error. See link for explanation
 -       * http://www.php.net/manual/en/features.file-upload.errors.php
 -       *
 -       * @return int One of the UPLOAD_ constants, 0 if non-existent
 -       */
 -      public function getError() {
 -              if ( !$this->exists() ) {
 -                      return 0; # UPLOAD_ERR_OK
 -              }
 -
 -              return $this->fileInfo['error'];
 -      }
 -
 -      /**
 -       * Returns whether this upload failed because of overflow of a maximum set
 -       * in php.ini
 -       *
 -       * @return bool
 -       */
 -      public function isIniSizeOverflow() {
 -              if ( $this->getError() == UPLOAD_ERR_INI_SIZE ) {
 -                      # PHP indicated that upload_max_filesize is exceeded
 -                      return true;
 -              }
 -
 -              $contentLength = $this->request->getHeader( 'CONTENT_LENGTH' );
 -              if ( $contentLength > wfShorthandToInteger( ini_get( 'post_max_size' ) ) ) {
 -                      # post_max_size is exceeded
 -                      return true;
 -              }
 -
 -              return false;
 -      }
 -}
 -
 -/**
 - * WebRequest clone which takes values from a provided array.
 - *
 - * @ingroup HTTP
 - */
 -class FauxRequest extends WebRequest {
 -      private $wasPosted = false;
 -      private $session = array();
 -
 -      /**
 -       * @param array $data Array of *non*-urlencoded key => value pairs, the
 -       *   fake GET/POST values
 -       * @param bool $wasPosted Whether to treat the data as POST
 -       * @param array|null $session Session array or null
 -       * @param string $protocol 'http' or 'https'
 -       * @throws MWException
 -       */
 -      public function __construct( $data = array(), $wasPosted = false,
 -              $session = null, $protocol = 'http'
 -      ) {
 -              if ( is_array( $data ) ) {
 -                      $this->data = $data;
 -              } else {
 -                      throw new MWException( "FauxRequest() got bogus data" );
 -              }
 -              $this->wasPosted = $wasPosted;
 -              if ( $session ) {
 -                      $this->session = $session;
 -              }
 -              $this->protocol = $protocol;
 -      }
 -
 -      /**
 -       * @param string $method
 -       * @throws MWException
 -       */
 -      private function notImplemented( $method ) {
 -              throw new MWException( "{$method}() not implemented" );
 -      }
 -
 -      /**
 -       * @param string $name
 -       * @param string $default
 -       * @return string
 -       */
 -      public function getText( $name, $default = '' ) {
 -              # Override; don't recode since we're using internal data
 -              return (string)$this->getVal( $name, $default );
 -      }
 -
 -      /**
 -       * @return array
 -       */
 -      public function getValues() {
 -              return $this->data;
 -      }
 -
 -      /**
 -       * @return array
 -       */
 -      public function getQueryValues() {
 -              if ( $this->wasPosted ) {
 -                      return array();
 -              } else {
 -                      return $this->data;
 -              }
 -      }
 -
 -      public function getMethod() {
 -              return $this->wasPosted ? 'POST' : 'GET';
 -      }
 -
 -      /**
 -       * @return bool
 -       */
 -      public function wasPosted() {
 -              return $this->wasPosted;
 -      }
 -
 -      public function getCookie( $key, $prefix = null, $default = null ) {
 -              return $default;
 -      }
 -
 -      public function checkSessionCookie() {
 -              return false;
 -      }
 -
 -      public function getRequestURL() {
 -              $this->notImplemented( __METHOD__ );
 -      }
 -
 -      public function getProtocol() {
 -              return $this->protocol;
 -      }
 -
 -      /**
 -       * @param string $name The name of the header to get (case insensitive).
 -       * @return bool|string
 -       */
 -      public function getHeader( $name ) {
 -              $name = strtoupper( $name );
 -              return isset( $this->headers[$name] ) ? $this->headers[$name] : false;
 -      }
 -
 -      /**
 -       * @param string $name
 -       * @param string $val
 -       */
 -      public function setHeader( $name, $val ) {
 -              $name = strtoupper( $name );
 -              $this->headers[$name] = $val;
 -      }
 -
 -      /**
 -       * @param string $key
 -       * @return array|null
 -       */
 -      public function getSessionData( $key ) {
 -              if ( isset( $this->session[$key] ) ) {
 -                      return $this->session[$key];
 -              }
 -              return null;
 -      }
 -
 -      /**
 -       * @param string $key
 -       * @param array $data
 -       */
 -      public function setSessionData( $key, $data ) {
 -              $this->session[$key] = $data;
 -      }
 -
 -      /**
 -       * @return array|mixed|null
 -       */
 -      public function getSessionArray() {
 -              return $this->session;
 -      }
 -
 -      /**
 -       * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
 -       * @return string
 -       */
 -      public function getRawQueryString() {
 -              return '';
 -      }
 -
 -      /**
 -       * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
 -       * @return string
 -       */
 -      public function getRawPostString() {
 -              return '';
 -      }
 -
 -      /**
 -       * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
 -       * @return string
 -       */
 -      public function getRawInput() {
 -              return '';
 -      }
 -
 -      /**
 -       * @param array $extWhitelist
 -       * @return bool
 -       */
 -      public function checkUrlExtension( $extWhitelist = array() ) {
 -              return true;
 -      }
 -
 -      /**
 -       * @return string
 -       */
 -      protected function getRawIP() {
 -              return '127.0.0.1';
 -      }
 -}
 -
 -/**
 - * Similar to FauxRequest, but only fakes URL parameters and method
 - * (POST or GET) and use the base request for the remaining stuff
 - * (cookies, session and headers).
 - *
 - * @ingroup HTTP
 - * @since 1.19
 - */
 -class DerivativeRequest extends FauxRequest {
 -      private $base;
 -
 -      /**
 -       * @param WebRequest $base
 -       * @param array $data Array of *non*-urlencoded key => value pairs, the
 -       *   fake GET/POST values
 -       * @param bool $wasPosted Whether to treat the data as POST
 -       */
 -      public function __construct( WebRequest $base, $data, $wasPosted = false ) {
 -              $this->base = $base;
 -              parent::__construct( $data, $wasPosted );
 -      }
 -
 -      public function getCookie( $key, $prefix = null, $default = null ) {
 -              return $this->base->getCookie( $key, $prefix, $default );
 -      }
 -
 -      public function checkSessionCookie() {
 -              return $this->base->checkSessionCookie();
 -      }
 -
 -      public function getHeader( $name ) {
 -              return $this->base->getHeader( $name );
 -      }
 -
 -      public function getAllHeaders() {
 -              return $this->base->getAllHeaders();
 -      }
 -
 -      public function getSessionData( $key ) {
 -              return $this->base->getSessionData( $key );
 -      }
 -
 -      public function setSessionData( $key, $data ) {
 -              $this->base->setSessionData( $key, $data );
 -      }
 -
 -      public function getAcceptLang() {
 -              return $this->base->getAcceptLang();
 -      }
 -
 -      public function getIP() {
 -              return $this->base->getIP();
 -      }
 -
 -      public function getProtocol() {
 -              return $this->base->getProtocol();
 -      }
 -}