root/trunk/framework/Web/THttpRequest.php

Revision 2482, 29.4 kB (checked in by knut, 4 months ago)

updated copyright

  • Property svn:keywords set to Id
Line 
1<?php
2/**
3 * THttpRequest, THttpCookie, THttpCookieCollection, TUri class file
4 *
5 * @author Qiang Xue <qiang.xue@gmail.com>
6 * @link http://www.pradosoft.com/
7 * @copyright Copyright &copy; 2005-2008 PradoSoft
8 * @license http://www.pradosoft.com/license/
9 * @version $Id$
10 * @package System.Web
11 */
12
13Prado::using('System.Web.TUrlManager');
14
15/**
16 * THttpRequest class
17 *
18 * THttpRequest provides storage and access scheme for user request sent via HTTP.
19 * It also encapsulates a uniform way to parse and construct URLs.
20 *
21 * User post data can be retrieved from THttpRequest by using it like an associative array.
22 * For example, to test if a user supplies a variable named 'param1', you can use,
23 * <code>
24 *   if(isset($request['param1'])) ...
25 *   // equivalent to:
26 *   // if($request->contains('param1')) ...
27 * </code>
28 * To get the value of 'param1', use,
29 * <code>
30 *   $value=$request['param1'];
31 *   // equivalent to:
32 *   //   $value=$request->itemAt('param1');
33 * </code>
34 * To traverse the user post data, use
35 * <code>
36 *   foreach($request as $name=>$value) ...
37 * </code>
38 * Note, POST and GET variables are merged together in THttpRequest.
39 * If a variable name appears in both POST and GET data, then POST data
40 * takes precedence.
41 *
42 * To construct a URL that can be recognized by Prado, use {@link constructUrl()}.
43 * The format of the recognizable URLs is determined according to
44 * {@link setUrlManager UrlManager}. By default, the following two formats
45 * are recognized:
46 * <code>
47 * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2
48 * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2
49 * </code>
50 * The first format is called 'Get' while the second 'Path', which is specified
51 * via {@link setUrlFormat UrlFormat}. For advanced users who want to use
52 * their own URL formats, they can write customized URL management modules
53 * and install the managers as application modules and set {@link setUrlManager UrlManager}.
54 *
55 * The ServiceID in the above URLs is as defined in the application configuration
56 * (e.g. the default page service's service ID is 'page').
57 * As a consequence, your GET variable names should not conflict with the service
58 * IDs that your application supports.
59 *
60 * THttpRequest also provides the cookies sent by the user, user information such
61 * as his browser capabilities, accepted languages, etc.
62 *
63 * By default, THttpRequest is registered with {@link TApplication} as the
64 * request module. It can be accessed via {@link TApplication::getRequest()}.
65 *
66 * @author Qiang Xue <qiang.xue@gmail.com>
67 * @version $Id$
68 * @package System.Web
69 * @since 3.0
70 */
71class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule
72{
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=false;
109    private $_urlFormat=THttpRequestUrlFormat::Get;
110    private $_services;
111    private $_requestResolved=false;
112    private $_enableCookieValidation=false;
113    /**
114     * @var string request URL
115     */
116    private $_url=null;
117
118    /**
119     * @var string module id
120     */
121    private $_id;
122
123    /**
124     * @var array contains all request variables
125     */
126    private $_items=array();
127
128    /**
129     * @return string id of this module
130     */
131    public function getID()
132    {
133        return $this->_id;
134    }
135
136    /**
137     * @param string id of this module
138     */
139    public function setID($value)
140    {
141        $this->_id=$value;
142    }
143
144    /**
145     * Initializes the module.
146     * This method is required by IModule and is invoked by application.
147     * @param TXmlElement module configuration
148     */
149    public function init($config)
150    {
151        if(empty($this->_urlManagerID))
152        {
153            $this->_urlManager=new TUrlManager;
154            $this->_urlManager->init(null);
155        }
156        else
157        {
158            $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID);
159            if($this->_urlManager===null)
160                throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID);
161            if(!($this->_urlManager instanceof TUrlManager))
162                throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID);
163        }
164
165        // Fill in default request info when the script is run in command line
166        if(php_sapi_name()==='cli')
167        {
168            $_SERVER['REMOTE_ADDR']='127.0.0.1';
169            $_SERVER['REQUEST_METHOD']='GET';
170            $_SERVER['SERVER_NAME']='localhost';
171            $_SERVER['SERVER_PORT']=80;
172            $_SERVER['HTTP_USER_AGENT']='';
173        }
174
175        $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies');
176
177        // Info about server variables:
178        // PHP_SELF contains real URI (w/ path info, w/o query string)
179        // SCRIPT_NAME is the real URI for the requested script (w/o path info and query string)
180        // QUERY_STRING is the string following the '?' in the ur (eg the a=x part in http://foo/bar?a=x)
181        // REQUEST_URI contains the URI part entered in the browser address bar
182        // SCRIPT_FILENAME is the file path to the executing script
183        if(isset($_SERVER['REQUEST_URI']))
184            $this->_requestUri=$_SERVER['REQUEST_URI'];
185        else  // TBD: in this case, SCRIPT_NAME need to be escaped
186            $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']);
187
188        if(isset($_SERVER['PATH_INFO']))
189            $this->_pathInfo=$_SERVER['PATH_INFO'];
190        else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME'])
191            $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME']));
192        else
193            $this->_pathInfo='';
194
195        if(get_magic_quotes_gpc())
196        {
197            if(isset($_GET))
198                $_GET=$this->stripSlashes($_GET);
199            if(isset($_POST))
200                $_POST=$this->stripSlashes($_POST);
201            if(isset($_REQUEST))
202                $_REQUEST=$this->stripSlashes($_REQUEST);
203            if(isset($_COOKIE))
204                $_COOKIE=$this->stripSlashes($_COOKIE);
205        }
206
207        $this->getApplication()->setRequest($this);
208    }
209
210    /**
211     * Strips slashes from input data.
212     * This method is applied when magic quotes is enabled.
213     * @param mixed input data to be processed
214     * @return mixed processed data
215     */
216    public function stripSlashes(&$data)
217    {
218        return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data);
219    }
220
221    /**
222     * @return TUri the request URL
223     */
224    public function getUrl()
225    {
226        if($this->_url===null)
227        {
228            $secure=$this->getIsSecureConnection();
229            $url=$secure?'https://':'http://';
230            if(empty($_SERVER['HTTP_HOST']))
231            {
232                $url.=$_SERVER['SERVER_NAME'];
233                $port=$_SERVER['SERVER_PORT'];
234                if(($port!=80 && !$secure) || ($port!=443 && $secure))
235                    $url.=':'.$port;
236            }
237            else
238                $url.=$_SERVER['HTTP_HOST'];
239            $url.=$this->getRequestUri();
240            $this->_url=new TUri($url);
241        }
242        return $this->_url;
243    }
244
245    /**
246     * @return string the ID of the URL manager module
247     */
248    public function getUrlManager()
249    {
250        return $this->_urlManagerID;
251    }
252
253    /**
254     * Sets the URL manager module.
255     * By default, {@link TUrlManager} is used for managing URLs.
256     * You may specify a different module for URL managing tasks
257     * by loading it as an application module and setting this property
258     * with the module ID.
259     * @param string the ID of the URL manager module
260     */
261    public function setUrlManager($value)
262    {
263        $this->_urlManagerID=$value;
264    }
265
266    /**
267     * @return TUrlManager the URL manager module
268     */
269    public function getUrlManagerModule()
270    {
271        return $this->_urlManager;
272    }
273
274    /**
275     * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get.
276     */
277    public function getUrlFormat()
278    {
279        return $this->_urlFormat;
280    }
281
282    /**
283     * Sets the format of URLs constructed and interpretted by the request module.
284     * A Get URL format is like index.php?name1=value1&name2=value2
285     * while a Path URL format is like index.php/name1,value1/name2,value.
286     * Changing the UrlFormat will affect {@link constructUrl} and how GET variables
287     * are parsed.
288     * @param THttpRequestUrlFormat the format of URLs.
289     */
290    public function setUrlFormat($value)
291    {
292        $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat');
293    }
294
295    /**
296     * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to comma ','.
297     */
298    public function getUrlParamSeparator()
299    {
300        return $this->_separator;
301    }
302
303    /**
304     * @param string separator used to separate GET variable name and value when URL format is Path.
305     * @throws TInvalidDataValueException if the separator is not a single character
306     */
307    public function setUrlParamSeparator($value)
308    {
309        if(strlen($value)===1)
310            $this->_separator=$value;
311        else
312            throw new TInvalidDataValueException('httprequest_separator_invalid');
313    }
314
315    /**
316     * @return string request type, can be GET, POST, HEAD, or PUT
317     */
318    public function getRequestType()
319    {
320        return $_SERVER['REQUEST_METHOD'];
321    }
322
323    /**
324     * @return boolean if the request is sent via secure channel (https)
325     */
326    public function getIsSecureConnection()
327    {
328        return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off');
329    }
330
331    /**
332     * @return string part of the request URL after script name and before question mark.
333     */
334    public function getPathInfo()
335    {
336        return $this->_pathInfo;
337    }
338
339    /**
340     * @return string part of that request URL after the question mark
341     */
342    public function getQueryString()
343    {
344        return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:'';
345    }
346
347    /**
348     * @return string the requested http procolol. Blank string if not defined.
349     */
350    public function getHttpProtocolVersion ()
351    {
352        return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:'';
353    }
354   
355    /**
356     * @return string part of that request URL after the host info (including pathinfo and query string)
357     */
358    public function getRequestUri()
359    {
360        return $this->_requestUri;
361    }
362
363    /**
364     * @param boolean whether to use HTTPS instead of HTTP even if the current request is sent via HTTP
365     * @return string schema and hostname of the requested URL
366     */
367    public function getBaseUrl($forceSecureConnection=false)
368    {
369        $url=$this->getUrl();
370        $scheme=($forceSecureConnection)?"https":$url->getScheme();
371        $host=$url->getHost();
372        if (($port=$url->getPort())) $host.=':'.$port;
373        return $scheme.'://'.$host;
374    }
375
376    /**
377     * @return string entry script URL (w/o host part)
378     */
379    public function getApplicationUrl()
380    {
381        return $_SERVER['SCRIPT_NAME'];
382    }
383
384    /**
385     * @param boolean whether to use HTTPS instead of HTTP even if the current request is sent via HTTP
386     * @return string entry script URL (w/ host part)
387     */
388    public function getAbsoluteApplicationUrl($forceSecureConnection=false)
389    {
390        return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl();
391    }
392
393    /**
394     * @return string application entry script file path (processed w/ realpath())
395     */
396    public function getApplicationFilePath()
397    {
398        return realpath($_SERVER['SCRIPT_FILENAME']);
399    }
400
401    /**
402     * @return string server name
403     */
404    public function getServerName()
405    {
406        return $_SERVER['SERVER_NAME'];
407    }
408
409    /**
410     * @return integer server port number
411     */
412    public function getServerPort()
413    {
414        return $_SERVER['SERVER_PORT'];
415    }
416
417    /**
418     * @return string URL referrer, null if not present
419     */
420    public function getUrlReferrer()
421    {
422        return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null;
423    }
424
425    /**
426     * @return array user browser capabilities
427     * @see get_browser
428     */
429    public function getBrowser()
430    {
431        try
432        {
433            return get_browser();
434        }
435        catch(TPhpErrorException $e)
436        {
437            throw new TConfigurationException('httprequest_browscap_required');
438        }
439    }
440
441    /**
442     * @return string user agent
443     */
444    public function getUserAgent()
445    {
446        return $_SERVER['HTTP_USER_AGENT'];
447    }
448
449    /**
450     * @return string user IP address
451     */
452    public function getUserHostAddress()
453    {
454        return $_SERVER['REMOTE_ADDR'];
455    }
456
457    /**
458     * @return string user host name, null if cannot be determined
459     */
460    public function getUserHost()
461    {
462        return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null;
463    }
464
465    /**
466     * @return string user browser accept types
467     */
468    public function getAcceptTypes()
469    {
470        // TBD: break it into array??
471        return $_SERVER['HTTP_ACCEPT'];
472    }
473
474    /**
475     * Returns a list of user preferred languages.
476     * The languages are returned as an array. Each array element
477     * represents a single language preference. The languages are ordered
478     * according to user preferences. The first language is the most preferred.
479     * @return array list of user preferred languages.
480     */
481    public function getUserLanguages()
482    {
483        return Prado::getUserLanguages();
484    }
485
486    /**
487     * @return boolean whether cookies should be validated. Defaults to false.
488     */
489    public function getEnableCookieValidation()
490    {
491        return $this->_enableCookieValidation;
492    }
493
494    /**
495     * @param boolean whether cookies should be validated.
496     */
497    public function setEnableCookieValidation($value)
498    {
499        $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value);
500    }
501
502    /**
503     * @return THttpCookieCollection list of cookies to be sent
504     */
505    public function getCookies()
506    {
507        if($this->_cookies===null)
508        {
509            $this->_cookies=new THttpCookieCollection;
510            if($this->getEnableCookieValidation())
511            {
512                $sm=$this->getApplication()->getSecurityManager();
513                foreach($_COOKIE as $key=>$value)
514                {
515                    if(($value=$sm->validateData($value))!==false)
516                        $this->_cookies->add(new THttpCookie($key,$value));
517                }
518            }
519            else
520            {
521                foreach($_COOKIE as $key=>$value)
522                    $this->_cookies->add(new THttpCookie($key,$value));
523            }
524        }
525        return $this->_cookies;
526    }
527
528    /**
529     * @return array list of uploaded files.
530     */
531    public function getUploadedFiles()
532    {
533        return $_FILES;
534    }
535
536    /**
537     * @return array list of server variables.
538     */
539    public function getServerVariables()
540    {
541        return $_SERVER;
542    }
543
544    /**
545     * @return array list of environment variables.
546     */
547    public function getEnvironmentVariables()
548    {
549        return $_ENV;
550    }
551
552    /**
553     * Constructs a URL that can be recognized by PRADO.
554     * The actual construction work is done by the URL manager module.
555     * This method may append session information to the generated URL if needed.
556     * You may provide your own URL manager module by setting {@link setUrlManager UrlManager}
557     * to provide your own URL scheme.
558     *
559     * Note, the constructed URL does not contain the protocol and hostname part.
560     * You may obtain an absolute URL by prepending the constructed URL with {@link getBaseUrl BaseUrl}.
561     * @param string service ID
562     * @param string service parameter
563     * @param array GET parameters, null if not needed
564     * @param boolean whether to encode the ampersand in URL, defaults to true.
565     * @param boolean whether to encode the GET parameters (their names and values), defaults to false.
566     * @return string URL
567     * @see TUrlManager::constructUrl
568     */
569    public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true)
570    {
571        $url=$this->_urlManager->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems);
572        if(defined('SID') && SID != '' && !$this->_cookieOnly)
573            return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&amp;':'&')) . SID;
574        else
575            return $url;
576    }
577
578    /**
579     * Parses the request URL and returns an array of input parameters (excluding GET variables).
580     * You may override this method to support customized URL format.
581     * @return array list of input parameters, indexed by parameter names
582     * @see TUrlManager::parseUrl
583     */
584    protected function parseUrl()
585    {
586        return $this->_urlManager->parseUrl();
587    }
588
589    /**
590     * Resolves the requested service.
591     * This method implements a URL-based service resolution.
592     * A URL in the format of /index.php?sp=serviceID.serviceParameter
593     * will be resolved with the serviceID and the serviceParameter.
594     * You may override this method to provide your own way of service resolution.
595     * @param array list of valid service IDs
596     * @return string the currently requested service ID, null if no service ID is found
597     * @see constructUrl
598     */
599    public function resolveRequest($serviceIDs)
600    {
601        Prado::trace("Resolving request from ".$_SERVER['REMOTE_ADDR'],'System.Web.THttpRequest');
602        $getParams=$this->parseUrl();
603        foreach($getParams as $name=>$value)
604            $_GET[$name]=$value;
605        $this->_items=array_merge($_GET,$_POST);
606        $this->_requestResolved=true;
607        foreach($serviceIDs as $serviceID)
608        {
609            if($this->contains($serviceID))
610            {
611                $this->setServiceID($serviceID);
612                $this->setServiceParameter($this->itemAt($serviceID));
613                return $serviceID;
614            }
615        }
616        return null;
617    }
618
619    /**
620     * @return boolean true if request is already resolved, false otherwise.
621     */
622    public function getRequestResolved()
623    {
624        return $this->_requestResolved;
625    }
626
627    /**
628     * @return string requested service ID
629     */
630    public function getServiceID()
631    {
632        return $this->_serviceID;
633    }
634
635    /**
636     * Sets the requested service ID.
637     * @param string requested service ID
638     */
639    public function setServiceID($value)
640    {
641        $this->_serviceID=$value;
642    }
643
644    /**
645     * @return string requested service parameter
646     */
647    public function getServiceParameter()
648    {
649        return $this->_serviceParam;
650    }
651
652    /**
653     * Sets the requested service parameter.
654     * @param string requested service parameter
655     */
656    public function setServiceParameter($value)
657    {
658        $this->_serviceParam=$value;
659    }
660
661    //------ The following methods enable THttpRequest to be TMap-like -----
662
663    /**
664     * Returns an iterator for traversing the items in the list.
665     * This method is required by the interface IteratorAggregate.
666     * @return Iterator an iterator for traversing the items in the list.
667     */
668    public function getIterator()
669    {
670        return new TMapIterator($this->_items);
671    }
672
673    /**
674     * @return integer the number of items in the request
675     */
676<