]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/THttpRequest.php
baculum: New Baculum API and Baculum Web
[bacula/bacula] / gui / baculum / framework / Web / THttpRequest.php
1 <?php
2 /**
3  * THttpRequest, THttpCookie, THttpCookieCollection, TUri class file
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link https://github.com/pradosoft/prado
7  * @copyright Copyright &copy; 2005-2016 The PRADO Group
8  * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
9  * @package System.Web
10  */
11
12 Prado::using('System.Web.TUrlManager');
13
14 /**
15  * THttpRequest class
16  *
17  * THttpRequest provides storage and access scheme for user request sent via HTTP.
18  * It also encapsulates a uniform way to parse and construct URLs.
19  *
20  * User post data can be retrieved from THttpRequest by using it like an associative array.
21  * For example, to test if a user supplies a variable named 'param1', you can use,
22  * <code>
23  *   if(isset($request['param1'])) ...
24  *   // equivalent to:
25  *   // if($request->contains('param1')) ...
26  * </code>
27  * To get the value of 'param1', use,
28  * <code>
29  *   $value=$request['param1'];
30  *   // equivalent to:
31  *   //   $value=$request->itemAt('param1');
32  * </code>
33  * To traverse the user post data, use
34  * <code>
35  *   foreach($request as $name=>$value) ...
36  * </code>
37  * Note, POST and GET variables are merged together in THttpRequest.
38  * If a variable name appears in both POST and GET data, then POST data
39  * takes precedence.
40  *
41  * To construct a URL that can be recognized by Prado, use {@link constructUrl()}.
42  * The format of the recognizable URLs is determined according to
43  * {@link setUrlManager UrlManager}. By default, the following two formats
44  * are recognized:
45  * <code>
46  * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2
47  * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2
48  * </code>
49  * The first format is called 'Get' while the second 'Path', which is specified
50  * via {@link setUrlFormat UrlFormat}. For advanced users who want to use
51  * their own URL formats, they can write customized URL management modules
52  * and install the managers as application modules and set {@link setUrlManager UrlManager}.
53  *
54  * The ServiceID in the above URLs is as defined in the application configuration
55  * (e.g. the default page service's service ID is 'page').
56  * As a consequence, your GET variable names should not conflict with the service
57  * IDs that your application supports.
58  *
59  * THttpRequest also provides the cookies sent by the user, user information such
60  * as his browser capabilities, accepted languages, etc.
61  *
62  * By default, THttpRequest is registered with {@link TApplication} as the
63  * request module. It can be accessed via {@link TApplication::getRequest()}.
64  *
65  * @author Qiang Xue <qiang.xue@gmail.com>
66  * @package System.Web
67  * @since 3.0
68  */
69 class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule
70 {
71         const CGIFIX__PATH_INFO         = 1;
72         const CGIFIX__SCRIPT_NAME       = 2;
73         /**
74          * @var TUrlManager the URL manager module
75          */
76         private $_urlManager=null;
77         /**
78          * @var string the ID of the URL manager module
79          */
80         private $_urlManagerID='';
81         /**
82          * @var string Separator used to separate GET variable name and value when URL format is Path.
83          */
84         private $_separator=',';
85         /**
86          * @var string requested service ID
87          */
88         private $_serviceID=null;
89         /**
90          * @var string requested service parameter
91          */
92         private $_serviceParam=null;
93         /**
94          * @var THttpCookieCollection cookies sent from user
95          */
96         private $_cookies=null;
97         /**
98          * @var string requested URI (URL w/o host info)
99          */
100         private $_requestUri;
101         /**
102          * @var string path info of URL
103          */
104         private $_pathInfo;
105         /**
106          * @var boolean whether the session ID should be kept in cookie only
107          */
108         private $_cookieOnly=null;
109         private $_urlFormat=THttpRequestUrlFormat::Get;
110         private $_services;
111         private $_requestResolved=false;
112         private $_enableCookieValidation=false;
113         private $_cgiFix=0;
114         /**
115          * @var boolean whether to cache the TUrlManager class (useful with a lot of TUrlMappings)
116          */
117         private $_enableCache=false;
118         /**
119          * @var string request URL
120          */
121         private $_url=null;
122
123         /**
124          * @var string module id
125          */
126         private $_id;
127
128         /**
129          * @var array contains all request variables
130          */
131         private $_items=array();
132
133         /**
134          * @return string id of this module
135          */
136         public function getID()
137         {
138                 return $this->_id;
139         }
140
141         /**
142          * @param string id of this module
143          */
144         public function setID($value)
145         {
146                 $this->_id=$value;
147         }
148
149         /**
150          * Initializes the module.
151          * This method is required by IModule and is invoked by application.
152          * @param TXmlElement module configuration
153          */
154         public function init($config)
155         {
156                 // Fill in default request info when the script is run in command line
157                 if(php_sapi_name()==='cli')
158                 {
159                         $_SERVER['REMOTE_ADDR']='127.0.0.1';
160                         $_SERVER['REQUEST_METHOD']='GET';
161                         $_SERVER['SERVER_NAME']='localhost';
162                         $_SERVER['SERVER_PORT']=80;
163                         $_SERVER['HTTP_USER_AGENT']='';
164                 }
165
166                 // Info about server variables:
167                 // PHP_SELF contains real URI (w/ path info, w/o query string)
168                 // SCRIPT_NAME is the real URI for the requested script (w/o path info and query string)
169                 // QUERY_STRING is the string following the '?' in the ur (eg the a=x part in http://foo/bar?a=x)
170                 // REQUEST_URI contains the URI part entered in the browser address bar
171                 // SCRIPT_FILENAME is the file path to the executing script
172                 if(isset($_SERVER['REQUEST_URI']))
173                         $this->_requestUri=$_SERVER['REQUEST_URI'];
174                 else  // TBD: in this case, SCRIPT_NAME need to be escaped
175                         $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']);
176
177                 if($this->_cgiFix&self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO']))
178                         $this->_pathInfo=substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME']));
179                 elseif(isset($_SERVER['PATH_INFO']))
180                         $this->_pathInfo=$_SERVER['PATH_INFO'];
181                 else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME'])
182                         $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME']));
183                 else
184                         $this->_pathInfo='';
185
186                 if(get_magic_quotes_gpc())
187                 {
188                         if(isset($_GET))
189                                 $_GET=$this->stripSlashes($_GET);
190                         if(isset($_POST))
191                                 $_POST=$this->stripSlashes($_POST);
192                         if(isset($_REQUEST))
193                                 $_REQUEST=$this->stripSlashes($_REQUEST);
194                         if(isset($_COOKIE))
195                                 $_COOKIE=$this->stripSlashes($_COOKIE);
196                 }
197
198                 $this->getApplication()->setRequest($this);
199         }
200
201         /**
202          * Strips slashes from input data.
203          * This method is applied when magic quotes is enabled.
204          * @param mixed input data to be processed
205          * @return mixed processed data
206          */
207         public function stripSlashes(&$data)
208         {
209                 return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data);
210         }
211
212         /**
213          * @return TUri the request URL
214          */
215         public function getUrl()
216         {
217                 if($this->_url===null)
218                 {
219                         $secure=$this->getIsSecureConnection();
220                         $url=$secure?'https://':'http://';
221                         if(empty($_SERVER['HTTP_HOST']))
222                         {
223                                 $url.=$_SERVER['SERVER_NAME'];
224                                 $port=$_SERVER['SERVER_PORT'];
225                                 if(($port!=80 && !$secure) || ($port!=443 && $secure))
226                                         $url.=':'.$port;
227                         }
228                         else
229                                 $url.=$_SERVER['HTTP_HOST'];
230                         $url.=$this->getRequestUri();
231                         $this->_url=new TUri($url);
232                 }
233                 return $this->_url;
234         }
235
236         /**
237          * Set true to cache the UrlManager instance. Consider to enable this cache
238          * when the application defines a lot of TUrlMappingPatterns
239          * @param boolean true to cache urlmanager instance.
240          */
241         public function setEnableCache($value)
242         {
243                 $this->_enableCache = TPropertyValue::ensureBoolean($value);
244         }
245
246         /**
247          * @return boolean true if urlmanager instance should be cached, false otherwise.
248          */
249         public function getEnableCache()
250         {
251                 return $this->_enableCache;
252         }
253
254         protected function getCacheKey()
255         {
256                 return $this->getID();
257         }
258
259         /**
260          * Saves the current UrlManager instance to cache.
261          * @return boolean true if UrlManager instance was cached, false otherwise.
262          */
263         protected function cacheUrlManager($manager)
264         {
265                 if($this->getEnableCache())
266                 {
267                         $cache = $this->getApplication()->getCache();
268                         if($cache !== null)
269                         {
270                                 $dependencies = null;
271                                 if($this->getApplication()->getMode() !== TApplicationMode::Performance)
272                                         if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile())
273                                         {
274                                                 $fn = Prado::getPathOfNamespace($fn,$this->getApplication()->getConfigurationFileExt());
275                                                 $dependencies = new TFileCacheDependency($fn);
276                                         }
277                                 return $cache->set($this->getCacheKey(), $manager, 0, $dependencies);
278                         }
279                 }
280                 return false;
281         }
282
283         /**
284          * Loads UrlManager instance from cache.
285          * @return TUrlManager intance if load was successful, null otherwise.
286          */
287         protected function loadCachedUrlManager()
288         {
289                 if($this->getEnableCache())
290                 {
291                         $cache = $this->getApplication()->getCache();
292                         if($cache !== null)
293                         {
294                                 $manager = $cache->get($this->getCacheKey());
295                                 if($manager instanceof TUrlManager)
296                                         return $manager;
297                         }
298                 }
299                 return null;
300         }
301
302         /**
303          * @return string the ID of the URL manager module
304          */
305         public function getUrlManager()
306         {
307                 return $this->_urlManagerID;
308         }
309
310         /**
311          * Sets the URL manager module.
312          * By default, {@link TUrlManager} is used for managing URLs.
313          * You may specify a different module for URL managing tasks
314          * by loading it as an application module and setting this property
315          * with the module ID.
316          * @param string the ID of the URL manager module
317          */
318         public function setUrlManager($value)
319         {
320                 $this->_urlManagerID=$value;
321         }
322
323         /**
324          * @return TUrlManager the URL manager module
325          */
326         public function getUrlManagerModule()
327         {
328                 if($this->_urlManager===null)
329                 {
330                         if(($this->_urlManager = $this->loadCachedUrlManager())===null)
331                         {
332                                 if(empty($this->_urlManagerID))
333                                 {
334                                         $this->_urlManager=new TUrlManager;
335                                         $this->_urlManager->init(null);
336                                 }
337                                 else
338                                 {
339                                         $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID);
340                                         if($this->_urlManager===null)
341                                                 throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID);
342                                         if(!($this->_urlManager instanceof TUrlManager))
343                                                 throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID);
344                                 }
345                                 $this->cacheUrlManager($this->_urlManager);
346                         }
347                 }
348                 return $this->_urlManager;
349         }
350
351         /**
352          * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get.
353          */
354         public function getUrlFormat()
355         {
356                 return $this->_urlFormat;
357         }
358
359         /**
360          * Sets the format of URLs constructed and interpretted by the request module.
361          * A Get URL format is like index.php?name1=value1&name2=value2
362          * while a Path URL format is like index.php/name1,value1/name2,value.
363          * Changing the UrlFormat will affect {@link constructUrl} and how GET variables
364          * are parsed.
365          * @param THttpRequestUrlFormat the format of URLs.
366          */
367         public function setUrlFormat($value)
368         {
369                 $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat');
370         }
371
372         /**
373          * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to comma ','.
374          */
375         public function getUrlParamSeparator()
376         {
377                 return $this->_separator;
378         }
379
380         /**
381          * @param string separator used to separate GET variable name and value when URL format is Path.
382          * @throws TInvalidDataValueException if the separator is not a single character
383          */
384         public function setUrlParamSeparator($value)
385         {
386                 if(strlen($value)===1)
387                         $this->_separator=$value;
388                 else
389                         throw new TInvalidDataValueException('httprequest_separator_invalid');
390         }
391
392         /**
393          * @return string request type, can be GET, POST, HEAD, or PUT
394          */
395         public function getRequestType()
396         {
397                 return isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:null;
398         }
399
400         /**
401          * @param boolean $mimetypeOnly whether to return only the mimetype (default: true)
402          * @return string content type (e.g. 'application/json' or 'text/html; encoding=gzip') or null if not specified
403          */
404         public function getContentType($mimetypeOnly = true)
405         {
406                 if(!isset($_SERVER['CONTENT_TYPE']))
407                         return null;
408
409                 if($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false)
410                         return substr($_SERVER['CONTENT_TYPE'], 0, $_pos);
411
412                 return $_SERVER['CONTENT_TYPE'];
413         }
414
415         /**
416          * @return boolean if the request is sent via secure channel (https)
417          */
418         public function getIsSecureConnection()
419         {
420                         return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off');
421         }
422
423         /**
424          * @return string part of the request URL after script name and before question mark.
425          */
426         public function getPathInfo()
427         {
428                 return $this->_pathInfo;
429         }
430
431         /**
432          * @return string part of that request URL after the question mark
433          */
434         public function getQueryString()
435         {
436                 return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:null;
437         }
438
439         /**
440          * @return string the requested http procolol. Blank string if not defined.
441          */
442         public function getHttpProtocolVersion()
443         {
444                 return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:null;
445         }
446
447         /**
448          * @param integer|null Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default)
449          * @return array
450          */
451         public function getHeaders($case=null)
452         {
453                 static $result;
454
455                 if($result === null && function_exists('apache_request_headers')) {
456                         $result = apache_request_headers();
457                 }
458                 elseif($result === null) {
459                         $result = array();
460                         foreach($_SERVER as $key=>$value) {
461                                 if(strncasecmp($key, 'HTTP_', 5) !== 0) continue;
462                                         $key = str_replace(' ','-', ucwords(strtolower(str_replace('_',' ', substr($key, 5)))));
463                                         $result[$key] = $value;
464                         }
465                 }
466
467                 if($case !== null)
468                         return array_change_key_case($result, $case);
469
470                 return $result;
471         }
472
473         /**
474          * @return string part of that request URL after the host info (including pathinfo and query string)
475          */
476         public function getRequestUri()
477         {
478                 return $this->_requestUri;
479         }
480
481         /**
482          * @param boolean|null whether to use HTTPS instead of HTTP even if the current request is sent via HTTP or vice versa
483          *                                              null - keep current schema
484          *                                              true - force https
485          *                                              false - force http
486          * @return string schema and hostname of the requested URL
487          */
488         public function getBaseUrl($forceSecureConnection=null)
489         {
490                 $url=$this->getUrl();
491                 $scheme=($forceSecureConnection)?"https": (($forceSecureConnection === null)?$url->getScheme():'http');
492                 $host=$url->getHost();
493                 if (($port=$url->getPort())) $host.=':'.$port;
494                 return $scheme.'://'.$host;
495         }
496
497         /**
498          * @return string entry script URL (w/o host part)
499          */
500         public function getApplicationUrl()
501         {
502                 if($this->_cgiFix&self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME']))
503                         return $_SERVER['ORIG_SCRIPT_NAME'];
504
505                 return isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:null;
506         }
507
508         /**
509          * @param boolean|null whether to use HTTPS instead of HTTP even if the current request is sent via HTTP or vice versa
510          *                                              null - keep current schema
511          *                                              true - force https
512          *                                              false - force http
513          * @return string entry script URL (w/ host part)
514          */
515         public function getAbsoluteApplicationUrl($forceSecureConnection=null)
516         {
517                 return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl();
518         }
519
520         /**
521          * @return string application entry script file path (processed w/ realpath())
522          */
523         public function getApplicationFilePath()
524         {
525                 return realpath(isset($_SERVER['SCRIPT_FILENAME'])?$_SERVER['SCRIPT_FILENAME']:null);
526         }
527
528         /**
529          * @return string server name
530          */
531         public function getServerName()
532         {
533                 return isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:null;
534         }
535
536         /**
537          * @return integer server port number
538          */
539         public function getServerPort()
540         {
541                 return isset($_SERVER['SERVER_PORT'])?$_SERVER['SERVER_PORT']:null;
542         }
543
544         /**
545          * @return string URL referrer, null if not present
546          */
547         public function getUrlReferrer()
548         {
549                 return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null;
550         }
551
552         /**
553          * @return array user browser capabilities
554          * @see get_browser
555          */
556         public function getBrowser()
557         {
558                 try
559                 {
560                         return get_browser();
561                 }
562                 catch(TPhpErrorException $e)
563                 {
564                         throw new TConfigurationException('httprequest_browscap_required');
565                 }
566         }
567
568         /**
569          * @return string user agent
570          */
571         public function getUserAgent()
572         {
573                 return isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null;
574         }
575
576         /**
577          * @return string user IP address
578          */
579         public function getUserHostAddress()
580         {
581                 return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null;
582         }
583
584         /**
585          * @return string user host name, null if cannot be determined
586          */
587         public function getUserHost()
588         {
589                 return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null;
590         }
591
592         /**
593          * @return string user browser accept types
594          */
595         public function getAcceptTypes()
596         {
597                 // TBD: break it into array??
598                 return isset($_SERVER['HTTP_ACCEPT'])?$_SERVER['HTTP_ACCEPT']:null;
599         }
600
601         /**
602          * Returns a list of user preferred languages.
603          * The languages are returned as an array. Each array element
604          * represents a single language preference. The languages are ordered
605          * according to user preferences. The first language is the most preferred.
606          * @return array list of user preferred languages.
607          */
608         public function getUserLanguages()
609         {
610                 return Prado::getUserLanguages();
611         }
612
613         /**
614          * @return boolean whether cookies should be validated. Defaults to false.
615          */
616         public function getEnableCookieValidation()
617         {
618                 return $this->_enableCookieValidation;
619         }
620
621         /**
622          * @param boolean whether cookies should be validated.
623          */
624         public function setEnableCookieValidation($value)
625         {
626                 $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value);
627         }
628
629         /**
630          * @return integer whether to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME. Defaults to 0.
631          * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
632          */
633         public function getCgiFix()
634         {
635                 return $this->_cgiFix;
636         }
637
638         /**
639          * Enable this, if you're using PHP via CGI with php.ini setting "cgi.fix_pathinfo=1"
640          * and have trouble with friendly URL feature. Enable this only if you really know what you are doing!
641          * @param integer enable bitwise to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME.
642          * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
643          */
644         public function setCgiFix($value)
645         {
646                 $this->_cgiFix=TPropertyValue::ensureInteger($value);
647         }
648
649         /**
650          * @return THttpCookieCollection list of cookies to be sent
651          */
652         public function getCookies()
653         {
654                 if($this->_cookies===null)
655                 {
656                         $this->_cookies=new THttpCookieCollection;
657                         if($this->getEnableCookieValidation())
658                         {
659                                 $sm=$this->getApplication()->getSecurityManager();
660                                 foreach($_COOKIE as $key=>$value)
661                                 {
662                                         if(($value=$sm->validateData($value))!==false)
663                                                 $this->_cookies->add(new THttpCookie($key,$value));
664                                 }
665                         }
666                         else
667                         {
668                                 foreach($_COOKIE as $key=>$value)
669                                         $this->_cookies->add(new THttpCookie($key,$value));
670                         }
671                 }
672                 return $this->_cookies;
673         }
674
675         /**
676          * @return array list of uploaded files.
677          */
678         public function getUploadedFiles()
679         {
680                 return $_FILES;
681         }
682
683         /**
684          * @return array list of server variables.
685          */
686         public function getServerVariables()
687         {
688                 return $_SERVER;
689         }
690
691         /**
692          * @return array list of environment variables.
693          */
694         public function getEnvironmentVariables()
695         {
696                 return $_ENV;
697         }
698
699         /**
700          * Constructs a URL that can be recognized by PRADO.
701          * The actual construction work is done by the URL manager module.
702          * This method may append session information to the generated URL if needed.
703          * You may provide your own URL manager module by setting {@link setUrlManager UrlManager}
704          * to provide your own URL scheme.
705          *
706          * Note, the constructed URL does not contain the protocol and hostname part.
707          * You may obtain an absolute URL by prepending the constructed URL with {@link getBaseUrl BaseUrl}.
708          * @param string service ID
709          * @param string service parameter
710          * @param array GET parameters, null if not needed
711          * @param boolean whether to encode the ampersand in URL, defaults to true.
712          * @param boolean whether to encode the GET parameters (their names and values), defaults to false.
713          * @return string URL
714          * @see TUrlManager::constructUrl
715          */
716         public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true)
717         {
718                 if ($this->_cookieOnly===null)
719                                 $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies');
720                 $url=$this->getUrlManagerModule()->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems);
721                 if(defined('SID') && SID != '' && !$this->_cookieOnly)
722                         return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&amp;':'&')) . SID;
723                 else
724                         return $url;
725         }
726
727         /**
728          * Parses the request URL and returns an array of input parameters (excluding GET variables).
729          * You may override this method to support customized URL format.
730          * @return array list of input parameters, indexed by parameter names
731          * @see TUrlManager::parseUrl
732          */
733         protected function parseUrl()
734         {
735                 return $this->getUrlManagerModule()->parseUrl();
736         }
737
738         /**
739          * Resolves the requested service.
740          * This method implements a URL-based service resolution.
741          * A URL in the format of /index.php?sp=serviceID.serviceParameter
742          * will be resolved with the serviceID and the serviceParameter.
743          * You may override this method to provide your own way of service resolution.
744          * @param array list of valid service IDs
745          * @return string the currently requested service ID, null if no service ID is found
746          * @see constructUrl
747          */
748         public function resolveRequest($serviceIDs)
749         {
750                 Prado::trace("Resolving request from ".$_SERVER['REMOTE_ADDR'],'System.Web.THttpRequest');
751                 $getParams=$this->parseUrl();
752                 foreach($getParams as $name=>$value)
753                         $_GET[$name]=$value;
754                 $this->_items=array_merge($_GET,$_POST);
755                 $this->_requestResolved=true;
756                 foreach($serviceIDs as $serviceID)
757                 {
758                         if($this->contains($serviceID))
759                         {
760                                 $this->setServiceID($serviceID);
761                                 $this->setServiceParameter($this->itemAt($serviceID));
762                                 return $serviceID;
763                         }
764                 }
765                 return null;
766         }
767
768         /**
769          * @return boolean true if request is already resolved, false otherwise.
770          */
771         public function getRequestResolved()
772         {
773                 return $this->_requestResolved;
774         }
775
776         /**
777          * @return string requested service ID
778          */
779         public function getServiceID()
780         {
781                 return $this->_serviceID;
782         }
783
784         /**
785          * Sets the requested service ID.
786          * @param string requested service ID
787          */
788         public function setServiceID($value)
789         {
790                 $this->_serviceID=$value;
791         }
792
793         /**
794          * @return string requested service parameter
795          */
796         public function getServiceParameter()
797         {
798                 return $this->_serviceParam;
799         }
800
801         /**
802          * Sets the requested service parameter.
803          * @param string requested service parameter
804          */
805         public function setServiceParameter($value)
806         {
807                 $this->_serviceParam=$value;
808         }
809
810         //------ The following methods enable THttpRequest to be TMap-like -----
811
812         /**
813          * Returns an iterator for traversing the items in the list.
814          * This method is required by the interface IteratorAggregate.
815          * @return Iterator an iterator for traversing the items in the list.
816          */
817         public function getIterator()
818         {
819                 return new ArrayIterator($this->_items);
820         }
821
822         /**
823          * @return integer the number of items in the request
824          */
825         public function getCount()
826         {
827                 return count($this->_items);
828         }
829
830         /**
831          * Returns the number of items in the request.
832          * This method is required by Countable interface.
833          * @return integer number of items in the request.
834          */
835         public function count()
836         {
837                 return $this->getCount();
838         }
839
840         /**
841          * @return array the key list
842          */
843         public function getKeys()
844         {
845                 return array_keys($this->_items);
846         }
847
848         /**
849          * Returns the item with the specified key.
850          * This method is exactly the same as {@link offsetGet}.
851          * @param mixed the key
852          * @return mixed the element at the offset, null if no element is found at the offset
853          */
854         public function itemAt($key)
855         {
856                 return isset($this->_items[$key]) ? $this->_items[$key] : null;
857         }
858
859         /**
860          * Adds an item into the request.
861          * Note, if the specified key already exists, the old value will be overwritten.
862          * @param mixed key
863          * @param mixed value
864          */
865         public function add($key,$value)
866         {
867                 $this->_items[$key]=$value;
868         }
869
870         /**
871          * Removes an item from the request by its key.
872          * @param mixed the key of the item to be removed
873          * @return mixed the removed value, null if no such key exists.
874          * @throws TInvalidOperationException if the item cannot be removed
875          */
876         public function remove($key)
877         {
878                 if(isset($this->_items[$key]) || array_key_exists($key,$this->_items))
879                 {
880                         $value=$this->_items[$key];
881                         unset($this->_items[$key]);
882                         return $value;
883                 }
884                 else
885                         return null;
886         }
887
888         /**
889          * Removes all items in the request.
890          */
891         public function clear()
892         {
893                 foreach(array_keys($this->_items) as $key)
894                         $this->remove($key);
895         }
896
897         /**
898          * @param mixed the key
899          * @return boolean whether the request contains an item with the specified key
900          */
901         public function contains($key)
902         {
903                 return isset($this->_items[$key]) || array_key_exists($key,$this->_items);
904         }
905
906         /**
907          * @return array the list of items in array
908          */
909         public function toArray()
910         {
911                 return $this->_items;
912         }
913
914         /**
915          * Returns whether there is an element at the specified offset.
916          * This method is required by the interface ArrayAccess.
917          * @param mixed the offset to check on
918          * @return boolean
919          */
920         public function offsetExists($offset)
921         {
922                 return $this->contains($offset);
923         }
924
925         /**
926          * Returns the element at the specified offset.
927          * This method is required by the interface ArrayAccess.
928          * @param integer the offset to retrieve element.
929          * @return mixed the element at the offset, null if no element is found at the offset
930          */
931         public function offsetGet($offset)
932         {
933                 return $this->itemAt($offset);
934         }
935
936         /**
937          * Sets the element at the specified offset.
938          * This method is required by the interface ArrayAccess.
939          * @param integer the offset to set element
940          * @param mixed the element value
941          */
942         public function offsetSet($offset,$item)
943         {
944                 $this->add($offset,$item);
945         }
946
947         /**
948          * Unsets the element at the specified offset.
949          * This method is required by the interface ArrayAccess.
950          * @param mixed the offset to unset element
951          */
952         public function offsetUnset($offset)
953         {
954                 $this->remove($offset);
955         }
956 }
957
958 /**
959  * THttpCookieCollection class.
960  *
961  * THttpCookieCollection implements a collection class to store cookies.
962  * Besides using all functionalities from {@link TList}, you can also
963  * retrieve a cookie by its name using either {@link findCookieByName} or
964  * simply:
965  * <code>
966  *   $cookie=$collection[$cookieName];
967  * </code>
968  *
969  * @author Qiang Xue <qiang.xue@gmail.com>
970  * @package System.Web
971  * @since 3.0
972  */
973 class THttpCookieCollection extends TList
974 {
975         /**
976          * @var mixed owner of this collection
977          */
978         private $_o;
979
980         /**
981          * Constructor.
982          * @param mixed owner of this collection.
983          */
984         public function __construct($owner=null)
985         {
986                 $this->_o=$owner;
987         }
988
989         /**
990          * Inserts an item at the specified position.
991          * This overrides the parent implementation by performing additional
992          * operations for each newly added THttpCookie object.
993          * @param integer the specified position.
994          * @param mixed new item
995          * @throws TInvalidDataTypeException if the item to be inserted is not a THttpCookie object.
996          */
997         public function insertAt($index,$item)
998         {
999                 if($item instanceof THttpCookie)
1000                 {
1001                         parent::insertAt($index,$item);
1002                         if($this->_o instanceof THttpResponse)
1003                                 $this->_o->addCookie($item);
1004                 }
1005                 else
1006                         throw new TInvalidDataTypeException('httpcookiecollection_httpcookie_required');
1007         }
1008
1009         /**
1010          * Removes an item at the specified position.
1011          * This overrides the parent implementation by performing additional
1012          * cleanup work when removing a TCookie object.
1013          * @param integer the index of the item to be removed.
1014          * @return mixed the removed item.
1015          */
1016         public function removeAt($index)
1017         {
1018                 $item=parent::removeAt($index);
1019                 if($this->_o instanceof THttpResponse)
1020                         $this->_o->removeCookie($item);
1021                 return $item;
1022         }
1023
1024         /**
1025          * @param integer|string index of the cookie in the collection or the cookie's name
1026          * @return THttpCookie the cookie found
1027          */
1028         public function itemAt($index)
1029         {
1030                 if(is_integer($index))
1031                         return parent::itemAt($index);
1032                 else
1033                         return $this->findCookieByName($index);
1034         }
1035
1036         /**
1037          * Finds the cookie with the specified name.
1038          * @param string the name of the cookie to be looked for
1039          * @return THttpCookie the cookie, null if not found
1040          */
1041         public function findCookieByName($name)
1042         {
1043                 foreach($this as $cookie)
1044                         if($cookie->getName()===$name)
1045                                 return $cookie;
1046                 return null;
1047         }
1048 }
1049
1050 /**
1051  * THttpCookie class.
1052  *
1053  * A THttpCookie instance stores a single cookie, including the cookie name, value,
1054  * domain, path, expire, and secure.
1055  *
1056  * @author Qiang Xue <qiang.xue@gmail.com>
1057  * @package System.Web
1058  * @since 3.0
1059  */
1060 class THttpCookie extends TComponent
1061 {
1062         /**
1063          * @var string domain of the cookie
1064          */
1065         private $_domain='';
1066         /**
1067          * @var string name of the cookie
1068          */
1069         private $_name;
1070         /**
1071          * @var string value of the cookie
1072          */
1073         private $_value='';
1074         /**
1075          * @var integer expire of the cookie
1076          */
1077         private $_expire=0;
1078         /**
1079          * @var string path of the cookie
1080          */
1081         private $_path='/';
1082         /**
1083          * @var boolean whether cookie should be sent via secure connection
1084          */
1085         private $_secure=false;
1086         /**
1087          * @var boolean if true the cookie value will be unavailable to JavaScript
1088          */
1089         private $_httpOnly=false;
1090
1091         /**
1092          * Constructor.
1093          * @param string name of this cookie
1094          * @param string value of this cookie
1095          */
1096         public function __construct($name,$value)
1097         {
1098                 $this->_name=$name;
1099                 $this->_value=$value;
1100         }
1101
1102         /**
1103          * @return string the domain to associate the cookie with
1104          */
1105         public function getDomain()
1106         {
1107                 return $this->_domain;
1108         }
1109
1110         /**
1111          * @param string the domain to associate the cookie with
1112          */
1113         public function setDomain($value)
1114         {
1115                 $this->_domain=$value;
1116         }
1117
1118         /**
1119          * @return integer the time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch.
1120          */
1121         public function getExpire()
1122         {
1123                 return $this->_expire;
1124         }
1125
1126         /**
1127          * @param integer the time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch.
1128          */
1129         public function setExpire($value)
1130         {
1131                 $this->_expire=TPropertyValue::ensureInteger($value);
1132         }
1133
1134         /**
1135          * @return boolean if true the cookie value will be unavailable to JavaScript
1136          */
1137         public function getHttpOnly()
1138         {
1139                 return $this->_httpOnly;
1140         }
1141
1142         /**
1143          * @param boolean $value if true the cookie value will be unavailable to JavaScript
1144          */
1145         public function setHttpOnly($value)
1146         {
1147                 $this->_httpOnly = TPropertyValue::ensureBoolean($value);
1148         }
1149
1150         /**
1151          * @return string the name of the cookie
1152          */
1153         public function getName()
1154         {
1155                 return $this->_name;
1156         }
1157
1158         /**
1159          * @param string the name of the cookie
1160          */
1161         public function setName($value)
1162         {
1163                 $this->_name=$value;
1164         }
1165
1166         /**
1167          * @return string the value of the cookie
1168          */
1169         public function getValue()
1170         {
1171                 return $this->_value;
1172         }
1173
1174         /**
1175          * @param string the value of the cookie
1176          */
1177         public function setValue($value)
1178         {
1179                 $this->_value=$value;
1180         }
1181
1182         /**
1183          * @return string the path on the server in which the cookie will be available on, default is '/'
1184          */
1185         public function getPath()
1186         {
1187                 return $this->_path;
1188         }
1189
1190         /**
1191          * @param string the path on the server in which the cookie will be available on
1192          */
1193         public function setPath($value)
1194         {
1195                 $this->_path=$value;
1196         }
1197
1198         /**
1199          * @return boolean whether the cookie should only be transmitted over a secure HTTPS connection
1200          */
1201         public function getSecure()
1202         {
1203                 return $this->_secure;
1204         }
1205
1206         /**
1207          * @param boolean ether the cookie should only be transmitted over a secure HTTPS connection
1208          */
1209         public function setSecure($value)
1210         {
1211                 $this->_secure=TPropertyValue::ensureBoolean($value);
1212         }
1213 }
1214
1215 /**
1216  * TUri class
1217  *
1218  * TUri represents a URI. Given a URI
1219  * http://joe:whatever@example.com:8080/path/to/script.php?param=value#anchor
1220  * it will be decomposed as follows,
1221  * - scheme: http
1222  * - host: example.com
1223  * - port: 8080
1224  * - user: joe
1225  * - password: whatever
1226  * - path: /path/to/script.php
1227  * - query: param=value
1228  * - fragment: anchor
1229  *
1230  * @author Qiang Xue <qiang.xue@gmail.com>
1231  * @package System.Web
1232  * @since 3.0
1233  */
1234 class TUri extends TComponent
1235 {
1236         /**
1237          * @var array list of default ports for known schemes
1238          */
1239         private static $_defaultPort=array(
1240                 'ftp'=>21,
1241                 'gopher'=>70,
1242                 'http'=>80,
1243                 'https'=>443,
1244                 'news'=>119,
1245                 'nntp'=>119,
1246                 'wais'=>210,
1247                 'telnet'=>23
1248         );
1249         /**
1250          * @var string scheme of the URI
1251          */
1252         private $_scheme;
1253         /**
1254          * @var string host name of the URI
1255          */
1256         private $_host;
1257         /**
1258          * @var integer port of the URI
1259          */
1260         private $_port;
1261         /**
1262          * @var string user of the URI
1263          */
1264         private $_user;
1265         /**
1266          * @var string password of the URI
1267          */
1268         private $_pass;
1269         /**
1270          * @var string path of the URI
1271          */
1272         private $_path;
1273         /**
1274          * @var string query string of the URI
1275          */
1276         private $_query;
1277         /**
1278          * @var string fragment of the URI
1279          */
1280         private $_fragment;
1281         /**
1282          * @var string the URI
1283          */
1284         private $_uri;
1285
1286         /**
1287          * Constructor.
1288          * Decomposes the specified URI into parts.
1289          * @param string URI to be represented
1290          * @throws TInvalidDataValueException if URI is of bad format
1291          */
1292         public function __construct($uri)
1293         {
1294                 if(($ret=@parse_url($uri))!==false)
1295                 {
1296                         // decoding???
1297                         $this->_scheme=isset($ret['scheme'])?$ret['scheme']:'';
1298                         $this->_host=isset($ret['host'])?$ret['host']:'';
1299                         $this->_port=isset($ret['port'])?$ret['port']:'';
1300                         $this->_user=isset($ret['user'])?$ret['user']:'';
1301                         $this->_pass=isset($ret['pass'])?$ret['pass']:'';
1302                         $this->_path=isset($ret['path'])?$ret['path']:'';
1303                         $this->_query=isset($ret['query'])?$ret['query']:'';
1304                         $this->_fragment=isset($ret['fragment'])?$ret['fragment']:'';
1305                         $this->_uri=$uri;
1306                 }
1307                 else
1308                 {
1309                         throw new TInvalidDataValueException('uri_format_invalid',$uri);
1310                 }
1311         }
1312
1313         /**
1314          * @return string URI
1315          */
1316         public function getUri()
1317         {
1318                 return $this->_uri;
1319         }
1320
1321         /**
1322          * @return string scheme of the URI, such as 'http', 'https', 'ftp', etc.
1323          */
1324         public function getScheme()
1325         {
1326                 return $this->_scheme;
1327         }
1328
1329         /**
1330          * @return string hostname of the URI
1331          */
1332         public function getHost()
1333         {
1334                 return $this->_host;
1335         }
1336
1337         /**
1338          * @return integer port number of the URI
1339          */
1340         public function getPort()
1341         {
1342                 return $this->_port;
1343         }
1344
1345         /**
1346          * @return string username of the URI
1347          */
1348         public function getUser()
1349         {
1350                 return $this->_user;
1351         }
1352
1353         /**
1354          * @return string password of the URI
1355          */
1356         public function getPassword()
1357         {
1358                 return $this->_pass;
1359         }
1360
1361         /**
1362          * @return string path of the URI
1363          */
1364         public function getPath()
1365         {
1366                 return $this->_path;
1367         }
1368
1369         /**
1370          * @return string query string of the URI
1371          */
1372         public function getQuery()
1373         {
1374                 return $this->_query;
1375         }
1376
1377         /**
1378          * @return string fragment of the URI
1379          */
1380         public function getFragment()
1381         {
1382                 return $this->_fragment;
1383         }
1384 }
1385
1386 /**
1387  * THttpRequestUrlFormat class.
1388  * THttpRequestUrlFormat defines the enumerable type for the possible URL formats
1389  * that can be recognized by {@link THttpRequest}.
1390  *
1391  * The following enumerable values are defined:
1392  * - Get: the URL format is like /path/to/index.php?name1=value1&name2=value2...
1393  * - Path: the URL format is like /path/to/index.php/name1,value1/name2,value2...
1394  * - HiddenPath: the URL format is like /path/to/name1,value1/name2,value2...
1395  *
1396  * @author Qiang Xue <qiang.xue@gmail.com>
1397  * @package System.Web
1398  * @since 3.0.4
1399  */
1400 class THttpRequestUrlFormat extends TEnumerable
1401 {
1402         const Get='Get';
1403         const Path='Path';
1404         const HiddenPath='HiddenPath';
1405 }
1406