Index: includes/WebRequest.php =================================================================== --- includes/WebRequest.php (revision 44250) +++ includes/WebRequest.php (working copy) @@ -3,7 +3,8 @@ * Deal with importing all those nasssty globals and things */ -# Copyright (C) 2003 Brion Vibber +# Copyright (C) 2003 Brion Vibber , +# 2008 Chad Horohoe # http://www.mediawiki.org/ # # This program is free software; you can redistribute it and/or modify @@ -43,19 +44,30 @@ * @ingroup HTTP */ class WebRequest { - var $data = array(); - var $headers; + + // Constants referring to superglobals + // SG_REQUEST is only GET+POST, we remove + // cookies to keep them from conflicting + const SG_REQUEST = 'request'; + const SG_COOKIE = 'cookie'; + const SG_SERVER = 'server'; + const SG_ENV = 'env'; + const SG_FILES = 'files'; + const SG_SESSION = 'session'; + + // Private copies of the superglobals + protected $_request, $_cookie, $_server, $_env, $_files, $_session = array(); + protected $headers = array(); + + // Other privates + private $mStripSlashes; private $_response; - - function __construct() { - /// @fixme This preemptive de-quoting can interfere with other web libraries - /// and increases our memory footprint. It would be cleaner to do on - /// demand; but currently we have no wrapper for $_SERVER etc. - $this->checkMagicQuotes(); - - // POST overrides GET data - // We don't use $_REQUEST here to avoid interference from cookies... - $this->data = $_POST + $_GET; + + /** + * Constructor + */ + public function __construct() { + $this->mStripSlashes = $this->checkMagicQuotes(); } /** @@ -72,9 +84,9 @@ // And also by Apache 2.x, double slashes are converted to single slashes. // So we will use REQUEST_URI if possible. $matches = array(); - if ( !empty( $_SERVER['REQUEST_URI'] ) ) { + if ( !is_null( $this->getServerValue( 'REQUEST_URI' ) ) ) { // Slurp out the path portion to examine... - $url = $_SERVER['REQUEST_URI']; + $url = $this->getServerValue( 'REQUEST_URI' ); if ( !preg_match( '!^https?://!', $url ) ) { $url = 'http://unused' . $url; } @@ -111,22 +123,23 @@ $matches = $this->extractTitle( $path, $variantPaths, 'variant' ); } } - } elseif ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) { + } elseif ( !is_null( $this->getServerValue( 'ORIG_PATH_INFO' ) ) && $this->getServerValue( 'ORIG_PATH_INFO' ) != '' ) { // Mangled PATH_INFO // http://bugs.php.net/bug.php?id=31892 // Also reported when ini_get('cgi.fix_pathinfo')==false - $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 ); + $matches['title'] = substr( $this->getServerValue( 'ORIG_PATH_INFO' ), 1 ); - } elseif ( isset( $_SERVER['PATH_INFO'] ) && ($_SERVER['PATH_INFO'] != '') ) { + } elseif ( !is_null( $this->getServerValue( 'PATH_INFO' ) ) && ($this->getServerValue( 'PATH_INFO' ) != '') ) { // Regular old PATH_INFO yay - $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 ); + $matches['title'] = substr( $this->getServerValue( 'PATH_INFO' ), 1 ); } foreach( $matches as $key => $val) { - $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val; + $this->_request[ $key ] = $_GET[$key] = $_REQUEST[$key] = $val; } } } + /** * Internal URL rewriting function; tries to extract page title and, * optionally, one other fixed parameter value from a URL path. @@ -157,48 +170,39 @@ } /** - * Recursively strips slashes from the given array; + * Recursively strips slashes from the given array or string; * used for undoing the evil that is magic_quotes_gpc. - * @param $arr array: will be modified + * @param mixed $arr array * @return array the original array - * @private */ - function &fix_magic_quotes( &$arr ) { + private function &fix_magic_quotes( &$arr ) { + if ( !is_array( $arr ) ) { + return stripslashes( $arr ); + } foreach( $arr as $key => $val ) { if( is_array( $val ) ) { $this->fix_magic_quotes( $arr[$key] ); } else { - $arr[$key] = stripslashes( $val ); + $arr[ stripslashes( $key ) ] = stripslashes( $val ); } } return $arr; } /** - * If magic_quotes_gpc option is on, run the global arrays - * through fix_magic_quotes to strip out the stupid slashes. - * WARNING: This should only be done once! Running a second - * time could damage the values. - * @private + * Shall we strip slashes because magic_quotes added them? + * @return bool */ - function checkMagicQuotes() { - if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) { - $this->fix_magic_quotes( $_COOKIE ); - $this->fix_magic_quotes( $_ENV ); - $this->fix_magic_quotes( $_GET ); - $this->fix_magic_quotes( $_POST ); - $this->fix_magic_quotes( $_REQUEST ); - $this->fix_magic_quotes( $_SERVER ); - } + private function checkMagicQuotes() { + return ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ); } /** * Recursively normalizes UTF-8 strings in the given array. * @param $data string or array * @return cleaned-up version of the given - * @private */ - function normalizeUnicode( $data ) { + private function normalizeUnicode( $data ) { if( is_array( $data ) ) { foreach( $data as $key => $val ) { $data[$key] = $this->normalizeUnicode( $val ); @@ -210,32 +214,102 @@ } /** - * Fetch a value from the given array or return $default if it's not set. - * - * @param $arr array - * @param $name string - * @param $default mixed - * @return mixed - * @private + * Accessors for all of the different superglobals. getRequestValue() + * and getCookieValue() have wrappers that handle things nicer for + * us, so don't use them directly. */ - function getGPCVal( $arr, $name, $default ) { - if( isset( $arr[$name] ) ) { + protected function getRequestValue( $key ) { + if ( isset( $this->_request[ $key ] ) ) { + return $this->_request[ $key ]; + } + $data = $this->getCleanValue( WebRequest::SG_REQUEST, $key ); + if ( is_null( $data ) ) { + return null; + } + if ( !is_array( $data ) ) { global $wgContLang; - $data = $arr[$name]; - if( isset( $_GET[$name] ) && !is_array( $data ) ) { - # Check for alternate/legacy character encoding. - if( isset( $wgContLang ) ) { - $data = $wgContLang->checkTitleEncoding( $data ); - } + if ( isset( $wgContLang ) ) { + $data = $wgContLang->checkTitleEncoding( $data ); } - $data = $this->normalizeUnicode( $data ); - return $data; - } else { - return $default; + } + $this->_request[ $key ] = $this->normalizeUnicode( $data ); + return $this->_request[ $key ]; } + protected function getCookieValue( $key ) { + if ( isset( $this->_cookie[ $key ] ) ) { + return $this->_cookie[ $key ]; + } + $this->_cookie[ $key ] = $this->getCleanValue( WebRequest::SG_COOKIE, $key ); + return $this->_cookie[ $key ]; + } + public function getServerValue( $key ) { + if ( isset( $this->_server[ $key ] ) ) { + return $this->_server[ $key ]; + } + $this->_server[ $key ] = $this->getCleanValue( WebRequest::SG_SERVER, $key ); + return $this->_server[ $key ]; + } + public function getFilesValue( $key ) { + if ( isset( $this->_files[ $key ] ) ) { + return $this->_files[ $key ]; + } + $this->_files[ $key ] = $this->getCleanValue( WebRequest::SG_FILES, $key ); + return $this->_files[ $key ]; + } + public function getEnvValue( $key ) { + if ( isset( $this->_env[ $key ] ) ) { + return $this->_env[ $key ]; + } + $this->_env[ $key ] = $this->getCleanValue( WebRequest::SG_ENV, $key ); + return $this->_env[ $key ]; + } + public function getSessionValue( $key ) { + if ( isset( $this->_session[ $key ] ) ) { + return $this->_session[ $key ]; + } + $this->_session[ $key ] = $this->getCleanValue( WebRequest::SG_SESSION, $key ); + return $this->_session[ $key ]; + } /** + * Get a raw value from a global, null otherwise + */ + private function getRawValue( $global, $key ) { + return isset( $global[ $key ] ) ? $global[ $key ] : null; + } + private function getCleanValue( $type, $key ) { + switch ( $type ) { + case WebRequest::SG_REQUEST: + $val = $this->getRawValue( $_POST + $_GET, $key ); + break; + case WebRequest::SG_COOKIE: + $val = $this->getRawValue( $_COOKIE, $key ); + break; + case WebRequest::SG_SERVER: + $val = $this->getRawValue( $_SERVER, $key ); + break; + case WebRequest::SG_ENV: + $val = $this->getRawValue( $_ENV, $key ); + break; + case WebRequest::SG_FILES: + $val = $this->getRawValue( $_FILES, $key ); + break; + case WebRequest::SG_SESSION: + $val = $this->getRawValue( $_SESSION, $key ); + break; + default: + return null; + } + if ( !is_null( $val ) && $this->mStripSlashes === true ) { + $val = $this->fix_magic_quotes( $val ); + } + + return $val; + } + + + /** * Fetch a scalar from the input or return $default if it's not set. * Returns a string. Arrays are discarded. Useful for * non-freeform text inputs (e.g. predefined internal text keys @@ -245,27 +319,29 @@ * @param $default string: optional default (or NULL) * @return string */ - function getVal( $name, $default = NULL ) { - $val = $this->getGPCVal( $this->data, $name, $default ); + public function getVal( $name, $default = null ) { + $val = $this->getRequestValue( $name ); if( is_array( $val ) ) { $val = $default; } if( is_null( $val ) ) { - return null; + return $default; } else { return (string)$val; } } /** - * Set an aribtrary value into our get/post data. + * Set an aribtrary value * @param $key string Key name to use * @param $value mixed Value to set + * @param $type string What type to set * @return mixed old value if one was present, null otherwise */ - function setVal( $key, $value ) { - $ret = isset( $this->data[$key] ) ? $this->data[$key] : null; - $this->data[$key] = $value; + public function setVal( $key, $value, $type = WebRequest::SG_REQUEST ) { + $varname = '_' . $type; + $ret = isset( $this->$varame[ $key ] ) ? $this->$varname[ $key ] : null; + $this->$varname[ $key ] = $value; return $ret; } @@ -278,8 +354,8 @@ * @param $default array: optional default (or NULL) * @return array */ - function getArray( $name, $default = NULL ) { - $val = $this->getGPCVal( $this->data, $name, $default ); + public function getArray( $name, $default = null ) { + $val = $this->getRequestValue( $name ); if( is_null( $val ) ) { return null; } else { @@ -297,7 +373,7 @@ * @param $default array: option default (or NULL) * @return array of ints */ - function getIntArray( $name, $default = NULL ) { + public function getIntArray( $name, $default = null ) { $val = $this->getArray( $name, $default ); if( is_array( $val ) ) { $val = array_map( 'intval', $val ); @@ -313,7 +389,7 @@ * @param $default int * @return int */ - function getInt( $name, $default = 0 ) { + public function getInt( $name, $default = 0 ) { return intval( $this->getVal( $name, $default ) ); } @@ -324,7 +400,7 @@ * @param $name string * @return int */ - function getIntOrNull( $name ) { + public function getIntOrNull( $name ) { $val = $this->getVal( $name ); return is_numeric( $val ) ? intval( $val ) @@ -339,7 +415,7 @@ * @param $default bool * @return bool */ - function getBool( $name, $default = false ) { + public function getBool( $name, $default = false ) { return $this->getVal( $name, $default ) ? true : false; } @@ -350,7 +426,7 @@ * @param $name string * @return bool */ - function getCheck( $name ) { + public function getCheck( $name ) { # Checkboxes and buttons are only present when clicked # Presence connotes truth, abscense false $val = $this->getVal( $name, NULL ); @@ -369,7 +445,7 @@ * @param $default string: optional * @return string */ - function getText( $name, $default = '' ) { + public function getText( $name, $default = '' ) { global $wgContLang; $val = $this->getVal( $name, $default ); return str_replace( "\r\n", "\n", @@ -381,10 +457,10 @@ * If no arguments are given, returns all input values. * No transformation is performed on the values. */ - function getValues() { + public function getValues() { $names = func_get_args(); if ( count( $names ) == 0 ) { - $names = array_keys( $this->data ); + $names = array_keys( $this->_request ); } $retVal = array(); @@ -406,8 +482,8 @@ * * @return bool */ - function wasPosted() { - return $_SERVER['REQUEST_METHOD'] == 'POST'; + public function wasPosted() { + return $this->getServerValue( 'REQUEST_METHOD' ) == 'POST'; } /** @@ -421,22 +497,22 @@ * * @return bool */ - function checkSessionCookie() { - return isset( $_COOKIE[session_name()] ); + public function checkSessionCookie() { + return !is_null( $this->getCookieValue( session_name() ) ); } /** * Return the path portion of the request URI. * @return string */ - function getRequestURL() { - if( isset( $_SERVER['REQUEST_URI'] ) ) { - $base = $_SERVER['REQUEST_URI']; - } elseif( isset( $_SERVER['SCRIPT_NAME'] ) ) { + public function getRequestURL() { + if( !is_null( $this->getServerValue( 'REQUEST_URI' ) ) ) { + $base = $this->getServerValue( 'REQUEST_URI' ); + } elseif( !is_null( $this->getServerValue( 'SCRIPT_NAME' ) ) ) { // Probably IIS; doesn't set REQUEST_URI - $base = $_SERVER['SCRIPT_NAME']; - if( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) { - $base .= '?' . $_SERVER['QUERY_STRING']; + $base = $this->getServerValue( 'SCRIPT_NAME' ); + if( !is_null( $this->getServerValue( 'QUERY_STRING' ) ) && $this->getServerValue( 'QUERY_STRING' ) != '' ) { + $base .= '?' . $this->getServerValue( 'QUERY_STRING' ); } } else { // This shouldn't happen! @@ -464,7 +540,7 @@ * Return the request URI with the canonical service and hostname. * @return string */ - function getFullRequestURL() { + public function getFullRequestURL() { global $wgServer; return $wgServer . $this->getRequestURL(); } @@ -473,8 +549,10 @@ * Take an arbitrary query and rewrite the present URL to include it * @param $query String: query string fragment; do not include initial '?' * @return string + * + * @TODO FIXME $_GET USED HERE! */ - function appendQuery( $query ) { + public function appendQuery( $query ) { global $wgTitle; $basequery = ''; foreach( $_GET as $var => $val ) { @@ -499,11 +577,11 @@ * @param $query String: query string fragment; do not include initial '?' * @return string */ - function escapeAppendQuery( $query ) { + public function escapeAppendQuery( $query ) { return htmlspecialchars( $this->appendQuery( $query ) ); } - function appendQueryValue( $key, $value, $onlyquery = false ) { + public function appendQueryValue( $key, $value, $onlyquery = false ) { return $this->appendQueryArray( array( $key => $value ), $onlyquery ); } @@ -513,8 +591,10 @@ * @param $onlyquery Bool: whether to only return the query string and not * the complete URL * @return string + * + * @TODO FIXME $_GET USED HERE */ - function appendQueryArray( $array, $onlyquery = false ) { + public function appendQueryArray( $array, $onlyquery = false ) { global $wgTitle; $newquery = $_GET; unset( $newquery['title'] ); @@ -532,7 +612,7 @@ * @param $optionname String: to specify an option other than rclimit to pull from. * @return array first element is limit, second is offset */ - function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) { + public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) { global $wgUser; $limit = $this->getInt( 'limit', 0 ); @@ -554,11 +634,9 @@ * @param $key String: * @return string or NULL if no such file. */ - function getFileTempname( $key ) { - if( !isset( $_FILES[$key] ) ) { - return NULL; - } - return $_FILES[$key]['tmp_name']; + public function getFileTempname( $key ) { + $ret = $this->getFileValue( $key ); + return is_null( $ret ) ? null : $ret['tmp_name']; } /** @@ -566,11 +644,9 @@ * @param $key String: * @return integer */ - function getFileSize( $key ) { - if( !isset( $_FILES[$key] ) ) { - return 0; - } - return $_FILES[$key]['size']; + public function getFileSize( $key ) { + $ret = $this->getFileValue( $key ); + return is_null( $ret ) ? 0 : $ret['size']; } /** @@ -578,11 +654,12 @@ * @param $key String: * @return integer */ - function getUploadError( $key ) { - if( !isset( $_FILES[$key] ) || !isset( $_FILES[$key]['error'] ) ) { + public function getUploadError( $key ) { + $ret = $this->getFileValue( $key ); + if( is_null( $ret ) || !isset( $ret['error'] ) ) { return 0/*UPLOAD_ERR_OK*/; } - return $_FILES[$key]['error']; + return $ret['error']; } /** @@ -596,17 +673,18 @@ * @param $key String: * @return string or NULL if no such file. */ - function getFileName( $key ) { - if( !isset( $_FILES[$key] ) ) { - return NULL; + public function getFileName( $key ) { + $ret = $this->getFileValue( $key ); + if ( is_null( $ret ) ) { + return null; } - $name = $_FILES[$key]['name']; + $name = $ret['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 = UtfNormal::cleanUp( $name ); - wfDebug( "WebRequest::getFileName() '" . $_FILES[$key]['name'] . "' normalized to '$name'\n" ); + wfDebug( "WebRequest::getFileName() '" . $ret['name'] . "' normalized to '$name'\n" ); return $name; } @@ -614,7 +692,7 @@ * Return a handle to WebResponse style object, for setting cookies, * headers and other stuff, for Request being worked on. */ - function response() { + public function response() { /* Lazy initialization of response object for this request */ if (!is_object($this->_response)) { $this->_response = new WebResponse; @@ -626,7 +704,7 @@ * Get a request header, or false if it isn't set * @param $name String: case-insensitive header name */ - function getHeader( $name ) { + public function getHeader( $name ) { $name = strtoupper( $name ); if ( function_exists( 'apache_request_headers' ) ) { if ( !isset( $this->headers ) ) { @@ -642,24 +720,30 @@ } } else { $name = 'HTTP_' . str_replace( '-', '_', $name ); - if ( isset( $_SERVER[$name] ) ) { - return $_SERVER[$name]; + if ( !is_null( $this->getServerValue( $name ) ) ) { + return $this->getServerValue( $name ); } else { return false; } } } + public function getCookie( $key, $usePrefix = true ) { + if ( $usePrefix ) { + global $wgCookiePrefix; + $key = $wgCookiePrefix . $key; + } + return $this->getCookieValue( $key ); + } + /* * Get data from $_SESSION */ - function getSessionData( $key ) { - if( !isset( $_SESSION[$key] ) ) - return null; - return $_SESSION[$key]; + public function getSessionData( $key ) { + return $this->getSessionValue( $key ); } - function setSessionData( $key, $data ) { - $_SESSION[$key] = $data; + public function setSessionData( $key, $data ) { + $this->setVal( $key, $data, WebRequest::SG_SESSION ); } } @@ -677,8 +761,8 @@ * @param $wasPosted Bool: whether to treat the data as POST */ function FauxRequest( $data, $wasPosted = false, $session = null ) { - if( is_array( $data ) ) { - $this->data = $data; + if( !is_array( $data ) ) { + $this->_request = $data; } else { throw new MWException( "FauxRequest() got bogus data" ); } @@ -697,7 +781,7 @@ } function getValues() { - return $this->data; + return $this->_request; } function wasPosted() { @@ -707,23 +791,19 @@ function checkSessionCookie() { return false; } - function getRequestURL() { $this->notImplemented( __METHOD__ ); } - function appendQuery( $query ) { $this->notImplemented( __METHOD__ ); } - function getHeader( $name ) { return isset( $this->headers[$name] ) ? $this->headers[$name] : false; } - function getSessionData( $key ) { - if( !isset( $this->session[$key] ) ) + if( !isset( $this->_session[$key] ) ) return null; - return $this->session[$key]; + return $this->_session[$key]; } function setSessionData( $key, $data ) { $this->notImplemented( __METHOD__ );