summaryrefslogtreecommitdiff
path: root/lib/prado/framework/Web/THttpRequest.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/prado/framework/Web/THttpRequest.php')
-rw-r--r--lib/prado/framework/Web/THttpRequest.php1406
1 files changed, 1406 insertions, 0 deletions
diff --git a/lib/prado/framework/Web/THttpRequest.php b/lib/prado/framework/Web/THttpRequest.php
new file mode 100644
index 0000000..f89c354
--- /dev/null
+++ b/lib/prado/framework/Web/THttpRequest.php
@@ -0,0 +1,1406 @@
+<?php
+/**
+ * THttpRequest, THttpCookie, THttpCookieCollection, TUri class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web
+ */
+
+Prado::using('System.Web.TUrlManager');
+
+/**
+ * THttpRequest class
+ *
+ * THttpRequest provides storage and access scheme for user request sent via HTTP.
+ * It also encapsulates a uniform way to parse and construct URLs.
+ *
+ * User post data can be retrieved from THttpRequest by using it like an associative array.
+ * For example, to test if a user supplies a variable named 'param1', you can use,
+ * <code>
+ * if(isset($request['param1'])) ...
+ * // equivalent to:
+ * // if($request->contains('param1')) ...
+ * </code>
+ * To get the value of 'param1', use,
+ * <code>
+ * $value=$request['param1'];
+ * // equivalent to:
+ * // $value=$request->itemAt('param1');
+ * </code>
+ * To traverse the user post data, use
+ * <code>
+ * foreach($request as $name=>$value) ...
+ * </code>
+ * Note, POST and GET variables are merged together in THttpRequest.
+ * If a variable name appears in both POST and GET data, then POST data
+ * takes precedence.
+ *
+ * To construct a URL that can be recognized by Prado, use {@link constructUrl()}.
+ * The format of the recognizable URLs is determined according to
+ * {@link setUrlManager UrlManager}. By default, the following two formats
+ * are recognized:
+ * <code>
+ * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2
+ * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2
+ * </code>
+ * The first format is called 'Get' while the second 'Path', which is specified
+ * via {@link setUrlFormat UrlFormat}. For advanced users who want to use
+ * their own URL formats, they can write customized URL management modules
+ * and install the managers as application modules and set {@link setUrlManager UrlManager}.
+ *
+ * The ServiceID in the above URLs is as defined in the application configuration
+ * (e.g. the default page service's service ID is 'page').
+ * As a consequence, your GET variable names should not conflict with the service
+ * IDs that your application supports.
+ *
+ * THttpRequest also provides the cookies sent by the user, user information such
+ * as his browser capabilities, accepted languages, etc.
+ *
+ * By default, THttpRequest is registered with {@link TApplication} as the
+ * request module. It can be accessed via {@link TApplication::getRequest()}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web
+ * @since 3.0
+ */
+class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule
+{
+ const CGIFIX__PATH_INFO = 1;
+ const CGIFIX__SCRIPT_NAME = 2;
+ /**
+ * @var TUrlManager the URL manager module
+ */
+ private $_urlManager=null;
+ /**
+ * @var string the ID of the URL manager module
+ */
+ private $_urlManagerID='';
+ /**
+ * @var string Separator used to separate GET variable name and value when URL format is Path.
+ */
+ private $_separator=',';
+ /**
+ * @var string requested service ID
+ */
+ private $_serviceID=null;
+ /**
+ * @var string requested service parameter
+ */
+ private $_serviceParam=null;
+ /**
+ * @var THttpCookieCollection cookies sent from user
+ */
+ private $_cookies=null;
+ /**
+ * @var string requested URI (URL w/o host info)
+ */
+ private $_requestUri;
+ /**
+ * @var string path info of URL
+ */
+ private $_pathInfo;
+ /**
+ * @var boolean whether the session ID should be kept in cookie only
+ */
+ private $_cookieOnly=null;
+ private $_urlFormat=THttpRequestUrlFormat::Get;
+ private $_services;
+ private $_requestResolved=false;
+ private $_enableCookieValidation=false;
+ private $_cgiFix=0;
+ /**
+ * @var boolean whether to cache the TUrlManager class (useful with a lot of TUrlMappings)
+ */
+ private $_enableCache=false;
+ /**
+ * @var string request URL
+ */
+ private $_url=null;
+
+ /**
+ * @var string module id
+ */
+ private $_id;
+
+ /**
+ * @var array contains all request variables
+ */
+ private $_items=array();
+
+ /**
+ * @return string id of this module
+ */
+ public function getID()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * @param string id of this module
+ */
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+
+ /**
+ * Initializes the module.
+ * This method is required by IModule and is invoked by application.
+ * @param TXmlElement module configuration
+ */
+ public function init($config)
+ {
+ // Fill in default request info when the script is run in command line
+ if(php_sapi_name()==='cli')
+ {
+ $_SERVER['REMOTE_ADDR']='127.0.0.1';
+ $_SERVER['REQUEST_METHOD']='GET';
+ $_SERVER['SERVER_NAME']='localhost';
+ $_SERVER['SERVER_PORT']=80;
+ $_SERVER['HTTP_USER_AGENT']='';
+ }
+
+ // Info about server variables:
+ // PHP_SELF contains real URI (w/ path info, w/o query string)
+ // SCRIPT_NAME is the real URI for the requested script (w/o path info and query string)
+ // QUERY_STRING is the string following the '?' in the ur (eg the a=x part in http://foo/bar?a=x)
+ // REQUEST_URI contains the URI part entered in the browser address bar
+ // SCRIPT_FILENAME is the file path to the executing script
+ if(isset($_SERVER['REQUEST_URI']))
+ $this->_requestUri=$_SERVER['REQUEST_URI'];
+ else // TBD: in this case, SCRIPT_NAME need to be escaped
+ $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']);
+
+ if($this->_cgiFix&self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO']))
+ $this->_pathInfo=substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME']));
+ elseif(isset($_SERVER['PATH_INFO']))
+ $this->_pathInfo=$_SERVER['PATH_INFO'];
+ else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME'])
+ $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME']));
+ else
+ $this->_pathInfo='';
+
+ if(get_magic_quotes_gpc())
+ {
+ if(isset($_GET))
+ $_GET=$this->stripSlashes($_GET);
+ if(isset($_POST))
+ $_POST=$this->stripSlashes($_POST);
+ if(isset($_REQUEST))
+ $_REQUEST=$this->stripSlashes($_REQUEST);
+ if(isset($_COOKIE))
+ $_COOKIE=$this->stripSlashes($_COOKIE);
+ }
+
+ $this->getApplication()->setRequest($this);
+ }
+
+ /**
+ * Strips slashes from input data.
+ * This method is applied when magic quotes is enabled.
+ * @param mixed input data to be processed
+ * @return mixed processed data
+ */
+ public function stripSlashes(&$data)
+ {
+ return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data);
+ }
+
+ /**
+ * @return TUri the request URL
+ */
+ public function getUrl()
+ {
+ if($this->_url===null)
+ {
+ $secure=$this->getIsSecureConnection();
+ $url=$secure?'https://':'http://';
+ if(empty($_SERVER['HTTP_HOST']))
+ {
+ $url.=$_SERVER['SERVER_NAME'];
+ $port=$_SERVER['SERVER_PORT'];
+ if(($port!=80 && !$secure) || ($port!=443 && $secure))
+ $url.=':'.$port;
+ }
+ else
+ $url.=$_SERVER['HTTP_HOST'];
+ $url.=$this->getRequestUri();
+ $this->_url=new TUri($url);
+ }
+ return $this->_url;
+ }
+
+ /**
+ * Set true to cache the UrlManager instance. Consider to enable this cache
+ * when the application defines a lot of TUrlMappingPatterns
+ * @param boolean true to cache urlmanager instance.
+ */
+ public function setEnableCache($value)
+ {
+ $this->_enableCache = TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return boolean true if urlmanager instance should be cached, false otherwise.
+ */
+ public function getEnableCache()
+ {
+ return $this->_enableCache;
+ }
+
+ protected function getCacheKey()
+ {
+ return $this->getID();
+ }
+
+ /**
+ * Saves the current UrlManager instance to cache.
+ * @return boolean true if UrlManager instance was cached, false otherwise.
+ */
+ protected function cacheUrlManager($manager)
+ {
+ if($this->getEnableCache())
+ {
+ $cache = $this->getApplication()->getCache();
+ if($cache !== null)
+ {
+ $dependencies = null;
+ if($this->getApplication()->getMode() !== TApplicationMode::Performance)
+ if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile())
+ {
+ $fn = Prado::getPathOfNamespace($fn,$this->getApplication()->getConfigurationFileExt());
+ $dependencies = new TFileCacheDependency($fn);
+ }
+ return $cache->set($this->getCacheKey(), $manager, 0, $dependencies);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Loads UrlManager instance from cache.
+ * @return TUrlManager intance if load was successful, null otherwise.
+ */
+ protected function loadCachedUrlManager()
+ {
+ if($this->getEnableCache())
+ {
+ $cache = $this->getApplication()->getCache();
+ if($cache !== null)
+ {
+ $manager = $cache->get($this->getCacheKey());
+ if($manager instanceof TUrlManager)
+ return $manager;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return string the ID of the URL manager module
+ */
+ public function getUrlManager()
+ {
+ return $this->_urlManagerID;
+ }
+
+ /**
+ * Sets the URL manager module.
+ * By default, {@link TUrlManager} is used for managing URLs.
+ * You may specify a different module for URL managing tasks
+ * by loading it as an application module and setting this property
+ * with the module ID.
+ * @param string the ID of the URL manager module
+ */
+ public function setUrlManager($value)
+ {
+ $this->_urlManagerID=$value;
+ }
+
+ /**
+ * @return TUrlManager the URL manager module
+ */
+ public function getUrlManagerModule()
+ {
+ if($this->_urlManager===null)
+ {
+ if(($this->_urlManager = $this->loadCachedUrlManager())===null)
+ {
+ if(empty($this->_urlManagerID))
+ {
+ $this->_urlManager=new TUrlManager;
+ $this->_urlManager->init(null);
+ }
+ else
+ {
+ $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID);
+ if($this->_urlManager===null)
+ throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID);
+ if(!($this->_urlManager instanceof TUrlManager))
+ throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID);
+ }
+ $this->cacheUrlManager($this->_urlManager);
+ }
+ }
+ return $this->_urlManager;
+ }
+
+ /**
+ * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get.
+ */
+ public function getUrlFormat()
+ {
+ return $this->_urlFormat;
+ }
+
+ /**
+ * Sets the format of URLs constructed and interpretted by the request module.
+ * A Get URL format is like index.php?name1=value1&name2=value2
+ * while a Path URL format is like index.php/name1,value1/name2,value.
+ * Changing the UrlFormat will affect {@link constructUrl} and how GET variables
+ * are parsed.
+ * @param THttpRequestUrlFormat the format of URLs.
+ */
+ public function setUrlFormat($value)
+ {
+ $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat');
+ }
+
+ /**
+ * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to comma ','.
+ */
+ public function getUrlParamSeparator()
+ {
+ return $this->_separator;
+ }
+
+ /**
+ * @param string separator used to separate GET variable name and value when URL format is Path.
+ * @throws TInvalidDataValueException if the separator is not a single character
+ */
+ public function setUrlParamSeparator($value)
+ {
+ if(strlen($value)===1)
+ $this->_separator=$value;
+ else
+ throw new TInvalidDataValueException('httprequest_separator_invalid');
+ }
+
+ /**
+ * @return string request type, can be GET, POST, HEAD, or PUT
+ */
+ public function getRequestType()
+ {
+ return isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:null;
+ }
+
+ /**
+ * @param boolean $mimetypeOnly whether to return only the mimetype (default: true)
+ * @return string content type (e.g. 'application/json' or 'text/html; encoding=gzip') or null if not specified
+ */
+ public function getContentType($mimetypeOnly = true)
+ {
+ if(!isset($_SERVER['CONTENT_TYPE']))
+ return null;
+
+ if($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false)
+ return substr($_SERVER['CONTENT_TYPE'], 0, $_pos);
+
+ return $_SERVER['CONTENT_TYPE'];
+ }
+
+ /**
+ * @return boolean if the request is sent via secure channel (https)
+ */
+ public function getIsSecureConnection()
+ {
+ return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off');
+ }
+
+ /**
+ * @return string part of the request URL after script name and before question mark.
+ */
+ public function getPathInfo()
+ {
+ return $this->_pathInfo;
+ }
+
+ /**
+ * @return string part of that request URL after the question mark
+ */
+ public function getQueryString()
+ {
+ return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:null;
+ }
+
+ /**
+ * @return string the requested http procolol. Blank string if not defined.
+ */
+ public function getHttpProtocolVersion()
+ {
+ return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:null;
+ }
+
+ /**
+ * @param integer|null Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default)
+ * @return array
+ */
+ public function getHeaders($case=null)
+ {
+ static $result;
+
+ if($result === null && function_exists('apache_request_headers')) {
+ $result = apache_request_headers();
+ }
+ elseif($result === null) {
+ $result = array();
+ foreach($_SERVER as $key=>$value) {
+ if(strncasecmp($key, 'HTTP_', 5) !== 0) continue;
+ $key = str_replace(' ','-', ucwords(strtolower(str_replace('_',' ', substr($key, 5)))));
+ $result[$key] = $value;
+ }
+ }
+
+ if($case !== null)
+ return array_change_key_case($result, $case);
+
+ return $result;
+ }
+
+ /**
+ * @return string part of that request URL after the host info (including pathinfo and query string)
+ */
+ public function getRequestUri()
+ {
+ return $this->_requestUri;
+ }
+
+ /**
+ * @param boolean|null whether to use HTTPS instead of HTTP even if the current request is sent via HTTP or vice versa
+ * null - keep current schema
+ * true - force https
+ * false - force http
+ * @return string schema and hostname of the requested URL
+ */
+ public function getBaseUrl($forceSecureConnection=null)
+ {
+ $url=$this->getUrl();
+ $scheme=($forceSecureConnection)?"https": (($forceSecureConnection === null)?$url->getScheme():'http');
+ $host=$url->getHost();
+ if (($port=$url->getPort())) $host.=':'.$port;
+ return $scheme.'://'.$host;
+ }
+
+ /**
+ * @return string entry script URL (w/o host part)
+ */
+ public function getApplicationUrl()
+ {
+ if($this->_cgiFix&self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME']))
+ return $_SERVER['ORIG_SCRIPT_NAME'];
+
+ return isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:null;
+ }
+
+ /**
+ * @param boolean|null whether to use HTTPS instead of HTTP even if the current request is sent via HTTP or vice versa
+ * null - keep current schema
+ * true - force https
+ * false - force http
+ * @return string entry script URL (w/ host part)
+ */
+ public function getAbsoluteApplicationUrl($forceSecureConnection=null)
+ {
+ return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl();
+ }
+
+ /**
+ * @return string application entry script file path (processed w/ realpath())
+ */
+ public function getApplicationFilePath()
+ {
+ return realpath(isset($_SERVER['SCRIPT_FILENAME'])?$_SERVER['SCRIPT_FILENAME']:null);
+ }
+
+ /**
+ * @return string server name
+ */
+ public function getServerName()
+ {
+ return isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:null;
+ }
+
+ /**
+ * @return integer server port number
+ */
+ public function getServerPort()
+ {
+ return isset($_SERVER['SERVER_PORT'])?$_SERVER['SERVER_PORT']:null;
+ }
+
+ /**
+ * @return string URL referrer, null if not present
+ */
+ public function getUrlReferrer()
+ {
+ return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null;
+ }
+
+ /**
+ * @return array user browser capabilities
+ * @see get_browser
+ */
+ public function getBrowser()
+ {
+ try
+ {
+ return get_browser();
+ }
+ catch(TPhpErrorException $e)
+ {
+ throw new TConfigurationException('httprequest_browscap_required');
+ }
+ }
+
+ /**
+ * @return string user agent
+ */
+ public function getUserAgent()
+ {
+ return isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null;
+ }
+
+ /**
+ * @return string user IP address
+ */
+ public function getUserHostAddress()
+ {
+ return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null;
+ }
+
+ /**
+ * @return string user host name, null if cannot be determined
+ */
+ public function getUserHost()
+ {
+ return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null;
+ }
+
+ /**
+ * @return string user browser accept types
+ */
+ public function getAcceptTypes()
+ {
+ // TBD: break it into array??
+ return isset($_SERVER['HTTP_ACCEPT'])?$_SERVER['HTTP_ACCEPT']:null;
+ }
+
+ /**
+ * Returns a list of user preferred languages.
+ * The languages are returned as an array. Each array element
+ * represents a single language preference. The languages are ordered
+ * according to user preferences. The first language is the most preferred.
+ * @return array list of user preferred languages.
+ */
+ public function getUserLanguages()
+ {
+ return Prado::getUserLanguages();
+ }
+
+ /**
+ * @return boolean whether cookies should be validated. Defaults to false.
+ */
+ public function getEnableCookieValidation()
+ {
+ return $this->_enableCookieValidation;
+ }
+
+ /**
+ * @param boolean whether cookies should be validated.
+ */
+ public function setEnableCookieValidation($value)
+ {
+ $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return integer whether to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME. Defaults to 0.
+ * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
+ */
+ public function getCgiFix()
+ {
+ return $this->_cgiFix;
+ }
+
+ /**
+ * Enable this, if you're using PHP via CGI with php.ini setting "cgi.fix_pathinfo=1"
+ * and have trouble with friendly URL feature. Enable this only if you really know what you are doing!
+ * @param integer enable bitwise to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME.
+ * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
+ */
+ public function setCgiFix($value)
+ {
+ $this->_cgiFix=TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return THttpCookieCollection list of cookies to be sent
+ */
+ public function getCookies()
+ {
+ if($this->_cookies===null)
+ {
+ $this->_cookies=new THttpCookieCollection;
+ if($this->getEnableCookieValidation())
+ {
+ $sm=$this->getApplication()->getSecurityManager();
+ foreach($_COOKIE as $key=>$value)
+ {
+ if(($value=$sm->validateData($value))!==false)
+ $this->_cookies->add(new THttpCookie($key,$value));
+ }
+ }
+ else
+ {
+ foreach($_COOKIE as $key=>$value)
+ $this->_cookies->add(new THttpCookie($key,$value));
+ }
+ }
+ return $this->_cookies;
+ }
+
+ /**
+ * @return array list of uploaded files.
+ */
+ public function getUploadedFiles()
+ {
+ return $_FILES;
+ }
+
+ /**
+ * @return array list of server variables.
+ */
+ public function getServerVariables()
+ {
+ return $_SERVER;
+ }
+
+ /**
+ * @return array list of environment variables.
+ */
+ public function getEnvironmentVariables()
+ {
+ return $_ENV;
+ }
+
+ /**
+ * Constructs a URL that can be recognized by PRADO.
+ * The actual construction work is done by the URL manager module.
+ * This method may append session information to the generated URL if needed.
+ * You may provide your own URL manager module by setting {@link setUrlManager UrlManager}
+ * to provide your own URL scheme.
+ *
+ * Note, the constructed URL does not contain the protocol and hostname part.
+ * You may obtain an absolute URL by prepending the constructed URL with {@link getBaseUrl BaseUrl}.
+ * @param string service ID
+ * @param string service parameter
+ * @param array GET parameters, null if not needed
+ * @param boolean whether to encode the ampersand in URL, defaults to true.
+ * @param boolean whether to encode the GET parameters (their names and values), defaults to false.
+ * @return string URL
+ * @see TUrlManager::constructUrl
+ */
+ public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true)
+ {
+ if ($this->_cookieOnly===null)
+ $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies');
+ $url=$this->getUrlManagerModule()->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems);
+ if(defined('SID') && SID != '' && !$this->_cookieOnly)
+ return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&amp;':'&')) . SID;
+ else
+ return $url;
+ }
+
+ /**
+ * Parses the request URL and returns an array of input parameters (excluding GET variables).
+ * You may override this method to support customized URL format.
+ * @return array list of input parameters, indexed by parameter names
+ * @see TUrlManager::parseUrl
+ */
+ protected function parseUrl()
+ {
+ return $this->getUrlManagerModule()->parseUrl();
+ }
+
+ /**
+ * Resolves the requested service.
+ * This method implements a URL-based service resolution.
+ * A URL in the format of /index.php?sp=serviceID.serviceParameter
+ * will be resolved with the serviceID and the serviceParameter.
+ * You may override this method to provide your own way of service resolution.
+ * @param array list of valid service IDs
+ * @return string the currently requested service ID, null if no service ID is found
+ * @see constructUrl
+ */
+ public function resolveRequest($serviceIDs)
+ {
+ Prado::trace("Resolving request from ".$_SERVER['REMOTE_ADDR'],'System.Web.THttpRequest');
+ $getParams=$this->parseUrl();
+ foreach($getParams as $name=>$value)
+ $_GET[$name]=$value;
+ $this->_items=array_merge($_GET,$_POST);
+ $this->_requestResolved=true;
+ foreach($serviceIDs as $serviceID)
+ {
+ if($this->contains($serviceID))
+ {
+ $this->setServiceID($serviceID);
+ $this->setServiceParameter($this->itemAt($serviceID));
+ return $serviceID;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return boolean true if request is already resolved, false otherwise.
+ */
+ public function getRequestResolved()
+ {
+ return $this->_requestResolved;
+ }
+
+ /**
+ * @return string requested service ID
+ */
+ public function getServiceID()
+ {
+ return $this->_serviceID;
+ }
+
+ /**
+ * Sets the requested service ID.
+ * @param string requested service ID
+ */
+ public function setServiceID($value)
+ {
+ $this->_serviceID=$value;
+ }
+
+ /**
+ * @return string requested service parameter
+ */
+ public function getServiceParameter()
+ {
+ return $this->_serviceParam;
+ }
+
+ /**
+ * Sets the requested service parameter.
+ * @param string requested service parameter
+ */
+ public function setServiceParameter($value)
+ {
+ $this->_serviceParam=$value;
+ }
+
+ //------ The following methods enable THttpRequest to be TMap-like -----
+
+ /**
+ * Returns an iterator for traversing the items in the list.
+ * This method is required by the interface IteratorAggregate.
+ * @return Iterator an iterator for traversing the items in the list.
+ */
+ public function getIterator()
+ {
+ return new TMapIterator($this->_items);
+ }
+
+ /**
+ * @return integer the number of items in the request
+ */
+ public function getCount()
+ {
+ return count($this->_items);
+ }
+
+ /**
+ * Returns the number of items in the request.
+ * This method is required by Countable interface.
+ * @return integer number of items in the request.
+ */
+ public function count()
+ {
+ return $this->getCount();
+ }
+
+ /**
+ * @return array the key list
+ */
+ public function getKeys()
+ {
+ return array_keys($this->_items);
+ }
+
+ /**
+ * Returns the item with the specified key.
+ * This method is exactly the same as {@link offsetGet}.
+ * @param mixed the key
+ * @return mixed the element at the offset, null if no element is found at the offset
+ */
+ public function itemAt($key)
+ {
+ return isset($this->_items[$key]) ? $this->_items[$key] : null;
+ }
+
+ /**
+ * Adds an item into the request.
+ * Note, if the specified key already exists, the old value will be overwritten.
+ * @param mixed key
+ * @param mixed value
+ */
+ public function add($key,$value)
+ {
+ $this->_items[$key]=$value;
+ }
+
+ /**
+ * Removes an item from the request by its key.
+ * @param mixed the key of the item to be removed
+ * @return mixed the removed value, null if no such key exists.
+ * @throws TInvalidOperationException if the item cannot be removed
+ */
+ public function remove($key)
+ {
+ if(isset($this->_items[$key]) || array_key_exists($key,$this->_items))
+ {
+ $value=$this->_items[$key];
+ unset($this->_items[$key]);
+ return $value;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Removes all items in the request.
+ */
+ public function clear()
+ {
+ foreach(array_keys($this->_items) as $key)
+ $this->remove($key);
+ }
+
+ /**
+ * @param mixed the key
+ * @return boolean whether the request contains an item with the specified key
+ */
+ public function contains($key)
+ {
+ return isset($this->_items[$key]) || array_key_exists($key,$this->_items);
+ }
+
+ /**
+ * @return array the list of items in array
+ */
+ public function toArray()
+ {
+ return $this->_items;
+ }
+
+ /**
+ * Returns whether there is an element at the specified offset.
+ * This method is required by the interface ArrayAccess.
+ * @param mixed the offset to check on
+ * @return boolean
+ */
+ public function offsetExists($offset)
+ {
+ return $this->contains($offset);
+ }
+
+ /**
+ * Returns the element at the specified offset.
+ * This method is required by the interface ArrayAccess.
+ * @param integer the offset to retrieve element.
+ * @return mixed the element at the offset, null if no element is found at the offset
+ */
+ public function offsetGet($offset)
+ {
+ return $this->itemAt($offset);
+ }
+
+ /**
+ * Sets the element at the specified offset.
+ * This method is required by the interface ArrayAccess.
+ * @param integer the offset to set element
+ * @param mixed the element value
+ */
+ public function offsetSet($offset,$item)
+ {
+ $this->add($offset,$item);
+ }
+
+ /**
+ * Unsets the element at the specified offset.
+ * This method is required by the interface ArrayAccess.
+ * @param mixed the offset to unset element
+ */
+ public function offsetUnset($offset)
+ {
+ $this->remove($offset);
+ }
+}
+
+/**
+ * THttpCookieCollection class.
+ *
+ * THttpCookieCollection implements a collection class to store cookies.
+ * Besides using all functionalities from {@link TList}, you can also
+ * retrieve a cookie by its name using either {@link findCookieByName} or
+ * simply:
+ * <code>
+ * $cookie=$collection[$cookieName];
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web
+ * @since 3.0
+ */
+class THttpCookieCollection extends TList
+{
+ /**
+ * @var mixed owner of this collection
+ */
+ private $_o;
+
+ /**
+ * Constructor.
+ * @param mixed owner of this collection.
+ */
+ public function __construct($owner=null)
+ {
+ $this->_o=$owner;
+ }
+
+ /**
+ * Inserts an item at the specified position.
+ * This overrides the parent implementation by performing additional
+ * operations for each newly added THttpCookie object.
+ * @param integer the specified position.
+ * @param mixed new item
+ * @throws TInvalidDataTypeException if the item to be inserted is not a THttpCookie object.
+ */
+ public function insertAt($index,$item)
+ {
+ if($item instanceof THttpCookie)
+ {
+ parent::insertAt($index,$item);
+ if($this->_o instanceof THttpResponse)
+ $this->_o->addCookie($item);
+ }
+ else
+ throw new TInvalidDataTypeException('httpcookiecollection_httpcookie_required');
+ }
+
+ /**
+ * Removes an item at the specified position.
+ * This overrides the parent implementation by performing additional
+ * cleanup work when removing a TCookie object.
+ * @param integer the index of the item to be removed.
+ * @return mixed the removed item.
+ */
+ public function removeAt($index)
+ {
+ $item=parent::removeAt($index);
+ if($this->_o instanceof THttpResponse)
+ $this->_o->removeCookie($item);
+ return $item;
+ }
+
+ /**
+ * @param integer|string index of the cookie in the collection or the cookie's name
+ * @return THttpCookie the cookie found
+ */
+ public function itemAt($index)
+ {
+ if(is_integer($index))
+ return parent::itemAt($index);
+ else
+ return $this->findCookieByName($index);
+ }
+
+ /**
+ * Finds the cookie with the specified name.
+ * @param string the name of the cookie to be looked for
+ * @return THttpCookie the cookie, null if not found
+ */
+ public function findCookieByName($name)
+ {
+ foreach($this as $cookie)
+ if($cookie->getName()===$name)
+ return $cookie;
+ return null;
+ }
+}
+
+/**
+ * THttpCookie class.
+ *
+ * A THttpCookie instance stores a single cookie, including the cookie name, value,
+ * domain, path, expire, and secure.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web
+ * @since 3.0
+ */
+class THttpCookie extends TComponent
+{
+ /**
+ * @var string domain of the cookie
+ */
+ private $_domain='';
+ /**
+ * @var string name of the cookie
+ */
+ private $_name;
+ /**
+ * @var string value of the cookie
+ */
+ private $_value='';
+ /**
+ * @var integer expire of the cookie
+ */
+ private $_expire=0;
+ /**
+ * @var string path of the cookie
+ */
+ private $_path='/';
+ /**
+ * @var boolean whether cookie should be sent via secure connection
+ */
+ private $_secure=false;
+ /**
+ * @var boolean if true the cookie value will be unavailable to JavaScript
+ */
+ private $_httpOnly=false;
+
+ /**
+ * Constructor.
+ * @param string name of this cookie
+ * @param string value of this cookie
+ */
+ public function __construct($name,$value)
+ {
+ $this->_name=$name;
+ $this->_value=$value;
+ }
+
+ /**
+ * @return string the domain to associate the cookie with
+ */
+ public function getDomain()
+ {
+ return $this->_domain;
+ }
+
+ /**
+ * @param string the domain to associate the cookie with
+ */
+ public function setDomain($value)
+ {
+ $this->_domain=$value;
+ }
+
+ /**
+ * @return integer the time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch.
+ */
+ public function getExpire()
+ {
+ return $this->_expire;
+ }
+
+ /**
+ * @param integer the time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch.
+ */
+ public function setExpire($value)
+ {
+ $this->_expire=TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return boolean if true the cookie value will be unavailable to JavaScript
+ */
+ public function getHttpOnly()
+ {
+ return $this->_httpOnly;
+ }
+
+ /**
+ * @param boolean $value if true the cookie value will be unavailable to JavaScript
+ */
+ public function setHttpOnly($value)
+ {
+ $this->_httpOnly = TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return string the name of the cookie
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * @param string the name of the cookie
+ */
+ public function setName($value)
+ {
+ $this->_name=$value;
+ }
+
+ /**
+ * @return string the value of the cookie
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * @param string the value of the cookie
+ */
+ public function setValue($value)
+ {
+ $this->_value=$value;
+ }
+
+ /**
+ * @return string the path on the server in which the cookie will be available on, default is '/'
+ */
+ public function getPath()
+ {
+ return $this->_path;
+ }
+
+ /**
+ * @param string the path on the server in which the cookie will be available on
+ */
+ public function setPath($value)
+ {
+ $this->_path=$value;
+ }
+
+ /**
+ * @return boolean whether the cookie should only be transmitted over a secure HTTPS connection
+ */
+ public function getSecure()
+ {
+ return $this->_secure;
+ }
+
+ /**
+ * @param boolean ether the cookie should only be transmitted over a secure HTTPS connection
+ */
+ public function setSecure($value)
+ {
+ $this->_secure=TPropertyValue::ensureBoolean($value);
+ }
+}
+
+/**
+ * TUri class
+ *
+ * TUri represents a URI. Given a URI
+ * http://joe:whatever@example.com:8080/path/to/script.php?param=value#anchor
+ * it will be decomposed as follows,
+ * - scheme: http
+ * - host: example.com
+ * - port: 8080
+ * - user: joe
+ * - password: whatever
+ * - path: /path/to/script.php
+ * - query: param=value
+ * - fragment: anchor
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web
+ * @since 3.0
+ */
+class TUri extends TComponent
+{
+ /**
+ * @var array list of default ports for known schemes
+ */
+ private static $_defaultPort=array(
+ 'ftp'=>21,
+ 'gopher'=>70,
+ 'http'=>80,
+ 'https'=>443,
+ 'news'=>119,
+ 'nntp'=>119,
+ 'wais'=>210,
+ 'telnet'=>23
+ );
+ /**
+ * @var string scheme of the URI
+ */
+ private $_scheme;
+ /**
+ * @var string host name of the URI
+ */
+ private $_host;
+ /**
+ * @var integer port of the URI
+ */
+ private $_port;
+ /**
+ * @var string user of the URI
+ */
+ private $_user;
+ /**
+ * @var string password of the URI
+ */
+ private $_pass;
+ /**
+ * @var string path of the URI
+ */
+ private $_path;
+ /**
+ * @var string query string of the URI
+ */
+ private $_query;
+ /**
+ * @var string fragment of the URI
+ */
+ private $_fragment;
+ /**
+ * @var string the URI
+ */
+ private $_uri;
+
+ /**
+ * Constructor.
+ * Decomposes the specified URI into parts.
+ * @param string URI to be represented
+ * @throws TInvalidDataValueException if URI is of bad format
+ */
+ public function __construct($uri)
+ {
+ if(($ret=@parse_url($uri))!==false)
+ {
+ // decoding???
+ $this->_scheme=isset($ret['scheme'])?$ret['scheme']:'';
+ $this->_host=isset($ret['host'])?$ret['host']:'';
+ $this->_port=isset($ret['port'])?$ret['port']:'';
+ $this->_user=isset($ret['user'])?$ret['user']:'';
+ $this->_pass=isset($ret['pass'])?$ret['pass']:'';
+ $this->_path=isset($ret['path'])?$ret['path']:'';
+ $this->_query=isset($ret['query'])?$ret['query']:'';
+ $this->_fragment=isset($ret['fragment'])?$ret['fragment']:'';
+ $this->_uri=$uri;
+ }
+ else
+ {
+ throw new TInvalidDataValueException('uri_format_invalid',$uri);
+ }
+ }
+
+ /**
+ * @return string URI
+ */
+ public function getUri()
+ {
+ return $this->_uri;
+ }
+
+ /**
+ * @return string scheme of the URI, such as 'http', 'https', 'ftp', etc.
+ */
+ public function getScheme()
+ {
+ return $this->_scheme;
+ }
+
+ /**
+ * @return string hostname of the URI
+ */
+ public function getHost()
+ {
+ return $this->_host;
+ }
+
+ /**
+ * @return integer port number of the URI
+ */
+ public function getPort()
+ {
+ return $this->_port;
+ }
+
+ /**
+ * @return string username of the URI
+ */
+ public function getUser()
+ {
+ return $this->_user;
+ }
+
+ /**
+ * @return string password of the URI
+ */
+ public function getPassword()
+ {
+ return $this->_pass;
+ }
+
+ /**
+ * @return string path of the URI
+ */
+ public function getPath()
+ {
+ return $this->_path;
+ }
+
+ /**
+ * @return string query string of the URI
+ */
+ public function getQuery()
+ {
+ return $this->_query;
+ }
+
+ /**
+ * @return string fragment of the URI
+ */
+ public function getFragment()
+ {
+ return $this->_fragment;
+ }
+}
+
+/**
+ * THttpRequestUrlFormat class.
+ * THttpRequestUrlFormat defines the enumerable type for the possible URL formats
+ * that can be recognized by {@link THttpRequest}.
+ *
+ * The following enumerable values are defined:
+ * - Get: the URL format is like /path/to/index.php?name1=value1&name2=value2...
+ * - Path: the URL format is like /path/to/index.php/name1,value1/name2,value2...
+ * - HiddenPath: the URL format is like /path/to/name1,value1/name2,value2...
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web
+ * @since 3.0.4
+ */
+class THttpRequestUrlFormat extends TEnumerable
+{
+ const Get='Get';
+ const Path='Path';
+ const HiddenPath='HiddenPath';
+}
+