From deef1c3d4cbe39832e07edc308659e7fac890ddf Mon Sep 17 00:00:00 2001 From: xue <> Date: Sat, 2 Dec 2006 19:20:40 +0000 Subject: added TUrlManager and fixed #451. --- framework/Exceptions/messages.txt | 4 + framework/TApplication.php | 2 + framework/Web/THttpRequest.php | 194 ++++++++++++++++++-------------------- framework/Web/TUrlManager.php | 142 ++++++++++++++++++++++++++++ framework/Web/TUrlMapping.php | 54 +++++------ 5 files changed, 265 insertions(+), 131 deletions(-) create mode 100644 framework/Web/TUrlManager.php (limited to 'framework') diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index fb0f6836..191e93af 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -48,6 +48,10 @@ securitymanager_mcryptextension_required = Mcrypt PHP extension is required in o uri_format_invalid = '{0}' is not a valid URI. +httprequest_separator_invalid = THttpRequest.UrlParamSeparator can only contain a single character. +httprequest_urlmanager_inexist = THttpRequest.UrlManager '{0}' does not point to an existing module. +httprequest_urlmanager_invalid = THttpRequest.UrlManager '{0}' must point to a module extending from TUrlManager. + httpresponse_bufferoutput_unchangeable = THttpResponse.BufferOutput cannot be modified after THttpResponse is initialized. httpresponse_file_inexistent = THttpResponse cannot send file '{0}'. The file does not exist. diff --git a/framework/TApplication.php b/framework/TApplication.php index ba3d3551..be551089 100644 --- a/framework/TApplication.php +++ b/framework/TApplication.php @@ -879,6 +879,8 @@ class TApplication extends TComponent $request=$this->getRequest(); $request->setAvailableServices($serviceIDs); + $request->resolveRequest(); + if(($serviceID=$request->getServiceID())===null) $serviceID=self::PAGE_SERVICE_ID; if(isset($services[$serviceID])) diff --git a/framework/Web/THttpRequest.php b/framework/Web/THttpRequest.php index bb7b4281..47fe1230 100644 --- a/framework/Web/THttpRequest.php +++ b/framework/Web/THttpRequest.php @@ -10,6 +10,8 @@ * @package System.Web */ +Prado::using('System.Web.TUrlManager'); + /** * THttpRequest class * @@ -38,17 +40,26 @@ * takes precedence. * * To construct a URL that can be recognized by Prado, use {@link constructUrl()}. - * THttpRequest also provides the cookies sent by the user, user information such - * as his browser capabilities, accepted languages, etc. - * Currently, THttpRequest recognizes the following URL format: + * The format of the recognizable URLs is determined according to + * {@link setUrlManager UrlManager}. By default, the following two formats + * are recognized: * - * /index.php?ServiceID=ServiceParameter + * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2 + * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2 * - * where ServiceID is as defined in the application configuration (e.g. - * the default page service's service ID is 'page'). - * Therefore, your GET variable names should not conflict with the service + * 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()}. * @@ -60,13 +71,17 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule { /** - * Separator used to separate GET variable name and value when URL format is Path. + * @var TUrlManager the URL manager module */ - private $_separator=','; + private $_urlManager=null; /** - * @var boolean whether the module is initialized + * @var string the ID of the URL manager module */ - private $_initialized=false; + 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 */ @@ -87,7 +102,10 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar * @var string path info of URL */ private $_pathInfo; - + /** + * @var boolean whether the session ID should be kept in cookie only + */ + private $_cookieOnly=false; private $_urlFormat=THttpRequestUrlFormat::Get; private $_services; private $_requestResolved=false; @@ -130,6 +148,20 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar */ public function init($config) { + 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); + } + // Fill in default request info when the script is run in command line if(php_sapi_name()==='cli') { @@ -140,6 +172,8 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar $_SERVER['HTTP_USER_AGENT']=''; } + $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies'); + // 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) @@ -170,12 +204,6 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar $_COOKIE=$this->stripSlashes($_COOKIE); } - if($this->getUrlFormat()===THttpRequestUrlFormat::Path && ($pathInfo=trim($this->_pathInfo,'/'))!=='') - $this->_items=array_merge($this->parseUrl(),$_POST); - else - $this->_items=array_merge($_GET,$_POST); - - $this->_initialized=true; $this->getApplication()->setRequest($this); } @@ -214,6 +242,35 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar return $this->_url; } + /** + * @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() + { + return $this->_urlManager; + } + /** * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. */ @@ -472,105 +529,37 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar } /** - * Constructs a URL that is recognizable by Prado. - * You may override this method to provide your own way of URL formatting. - * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. - * The URL is constructed as the following format: - * /entryscript.php?serviceID=serviceParameter&get1=value1&... - * If {@link setUrlFormat UrlFormat} is Path, the following format is used instead: - * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... + * 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. * @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 parseUrl + * @see TUrlManager::constructUrl */ public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true) { - $url=$serviceID.'='.$serviceParam; - $amp=$encodeAmpersand?'&':'&'; - if(is_array($getItems) || $getItems instanceof Traversable) - { - if($encodeGetItems) - { - foreach($getItems as $name=>$value) - { - if(is_array($value)) - { - $name=urlencode($name.'[]'); - foreach($value as $v) - $url.=$amp.$name.'='.urlencode($v); - } - else - $url.=$amp.urlencode($name).'='.urlencode($value); - } - } - else - { - foreach($getItems as $name=>$value) - { - if(is_array($value)) - { - foreach($value as $v) - $url.=$amp.$name.'[]='.$v; - } - else - $url.=$amp.$name.'='.$value; - } - } - } - if($this->getUrlFormat()===THttpRequestUrlFormat::Path) - { - $url=strtr($url,array($amp=>'/','?'=>'/','='=>$this->_separator)); - if(defined('SID') && SID != '' && !((int)ini_get('session.use_cookies')===1 && ((int)ini_get('session.use_only_cookies')===1))) - $url.='?'.SID; - return $this->getApplicationUrl().'/'.$url; - } + $url=$this->_urlManager->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); + if(defined('SID') && SID != '' && !$this->_cookieOnly) + return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&':'&')) . SID; else - { - if(defined('SID') && SID != '' && !((int)ini_get('session.use_cookies')===1 && ((int)ini_get('session.use_only_cookies')===1))) - $url.=$amp.SID; - return $this->getApplicationUrl().'?'.$url; - } + return $url; } /** - * Parses the request URL and returns an array of input parameters (including GET variables). - * This method is invoked when the URL format is Path. + * 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 constructUrl + * @see TUrlManager::parseUrl */ protected function parseUrl() { - if($this->_pathInfo!=='') - { - $paths=explode('/',$this->_pathInfo); - $getVariables=$_GET; - $serviceID=null; - foreach($paths as $path) - { - if(($path=trim($path))!=='') - { - if(($pos=strpos($path,$this->_separator))!==false) - { - $name=substr($path,0,$pos); - $value=substr($path,$pos+1); - if(($pos=strpos($name,'[]'))!==false) - $getVariables[substr($name,0,$pos)][]=$value; - else - $getVariables[$name]=$value; - } - else - $getVariables[$path]=''; - } - } - return $getVariables; - } - else - return $_GET; + return $this->_urlManager->parseUrl(); } /** @@ -581,10 +570,11 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar * You may override this method to provide your own way of service resolution. * @see constructUrl */ - protected function resolveRequest() + public function resolveRequest() { Prado::trace("Resolving request from ".$_SERVER['REMOTE_ADDR'],'System.Web.THttpRequest'); $this->_requestResolved=true; + $this->_items=array_merge($_GET,$this->parseUrl(),$_POST); foreach($this->_services as $id) { if($this->contains($id)) @@ -625,8 +615,6 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar */ public function getServiceID() { - if(!$this->_requestResolved) - $this->resolveRequest(); return $this->_serviceID; } @@ -644,8 +632,6 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar */ public function getServiceParameter() { - if(!$this->_requestResolved) - $this->resolveRequest(); return $this->_serviceParam; } diff --git a/framework/Web/TUrlManager.php b/framework/Web/TUrlManager.php new file mode 100644 index 00000000..d7938a47 --- /dev/null +++ b/framework/Web/TUrlManager.php @@ -0,0 +1,142 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id $ + * @package System.Web + */ + +/** + * TUrlManager class + * + * TUrlManager is the base class for managing URLs that can be + * recognized by PRADO applications. It provides the default implementation + * for parsing and constructing URLs. + * + * Derived classes may override {@link constructUrl} and {@link parseUrl} + * to provide customized URL schemes. + * + * By default, {@link THttpRequest} uses TUrlManager as its URL manager. + * If you want to use your customized URL manager, load your manager class + * as an application module and set {@link THttpRequest::setUrlManager THttpRequest.UrlManager} + * with the ID of your URL manager module. + * + * @author Qiang Xue + * @version $Id $ + * @package System.Web + * @since 3.0.6 + */ +class TUrlManager extends TModule +{ + /** + * Constructs a URL that can be recognized by PRADO. + * + * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. + * Override this method if you want to provide your own way of URL formatting. + * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. + * + * The URL is constructed as the following format: + * /entryscript.php?serviceID=serviceParameter&get1=value1&... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', + * the following format is used instead: + * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... + * @param string service ID + * @param string service parameter + * @param array GET parameters, null if not provided + * @param boolean whether to encode the ampersand in URL + * @param boolean whether to encode the GET parameters (their names and values) + * @return string URL + * @see parseUrl + */ + public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) + { + $url=$serviceID.'='.$serviceParam; + $amp=$encodeAmpersand?'&':'&'; + $request=$this->getRequest(); + if(is_array($getItems) || $getItems instanceof Traversable) + { + if($encodeGetItems) + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + $name=urlencode($name.'[]'); + foreach($value as $v) + $url.=$amp.$name.'='.urlencode($v); + } + else + $url.=$amp.urlencode($name).'='.urlencode($value); + } + } + else + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + foreach($value as $v) + $url.=$amp.$name.'[]='.$v; + } + else + $url.=$amp.$name.'='.$value; + } + } + } + if($request->getUrlFormat()===THttpRequestUrlFormat::Path) + return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); + else + return $request->getApplicationUrl().'?'.$url; + } + + /** + * Parses the request URL and returns an array of input parameters. + * This mehtod is automatically invoked by {@link THttpRequest} when + * handling a user request. + * + * In general, this method should parse the path info part of the requesting URL + * and generate an array of name-value pairs according to some scheme. + * The current implementation deals with both 'Get' and 'Path' URL formats. + * + * You may override this method to support customized URL format. + * @return array list of input parameters, indexed by parameter names + * @see constructUrl + */ + public function parseUrl() + { + $request=$this->getRequest(); + $pathInfo=trim($request->getPathInfo(),'/'); + if($request->getUrlFormat()===THttpRequestUrlFormat::Path && $pathInfo!=='') + { + $separator=$request->getUrlParamSeparator(); + $paths=explode('/',$pathInfo); + $getVariables=array(); + foreach($paths as $path) + { + if(($path=trim($path))!=='') + { + if(($pos=strpos($path,$separator))!==false) + { + $name=substr($path,0,$pos); + $value=substr($path,$pos+1); + if(($pos=strpos($name,'[]'))!==false) + $getVariables[substr($name,0,$pos)][]=$value; + else + $getVariables[$name]=$value; + } + else + $getVariables[$path]=''; + } + } + return $getVariables; + } + else + return array(); + } +} + +?> \ No newline at end of file diff --git a/framework/Web/TUrlMapping.php b/framework/Web/TUrlMapping.php index 6b47652c..50fe9f18 100644 --- a/framework/Web/TUrlMapping.php +++ b/framework/Web/TUrlMapping.php @@ -10,6 +10,8 @@ * @package System.Web */ +Prado::using('System.Web.TUrlManager'); + /** * TUrlMapping Class * @@ -17,14 +19,13 @@ * particular service and page class. This module must be configured * before a service is initialized, thus this module should be configured * globally in the application.xml file and before any services. - * - * The mapping format is as follows. * * * * * * + * * * * See {@link TUrlMappingPattern} for details regarding the mapping patterns. @@ -38,13 +39,21 @@ * The mapping can be load from an external file by specifying a configuration * file using the {@link setConfigFile ConfigFile} property. * + * Since TUrlMapping is a URL manager extending from {@link TUrlManager}, + * you may override {@link TUrlManager::constructUrl} to support your pattern-based + * URL scheme. + * * @author Wei Zhuo * @version $Id$ * @package System.Web * @since 3.0.5 */ -class TUrlMapping extends TModule +class TUrlMapping extends TUrlManager { + /** + * File extension of external configuration file + */ + const CONFIG_FILE_EXT='.xml'; /** * @var string default pattern class. */ @@ -57,10 +66,6 @@ class TUrlMapping extends TModule * @var TUrlMappingPattern matched pattern. */ private $_matched; - /** - * File extension of external configuration file - */ - const CONFIG_FILE_EXT='.xml'; /** * @var string external configuration file */ @@ -74,12 +79,12 @@ class TUrlMapping extends TModule */ public function init($xml) { + parent::init($xml); if($this->getRequest()->getRequestResolved()) throw new TConfigurationException('urlpath_dispatch_module_must_be_global'); if($this->_configFile!==null) $this->loadConfigFile(); $this->loadUrlMappings($xml); - $this->resolveMappings(); } /** @@ -142,10 +147,14 @@ class TUrlMapping extends TModule } /** - * Using the request URL path, find the first matching pattern. If found - * the matched pattern parameters are used in the Request object. + * Parses the request URL and returns an array of input parameters. + * This method overrides the parent implementation. + * The input parameters do not include GET and POST variables. + * This method uses the request URL path to find the first matching pattern. If found + * the matched pattern parameters are used to return as the input parameters. + * @return array list of input parameters */ - protected function resolveMappings() + public function parseUrl() { $url = $this->getRequest()->getUrl(); foreach($this->_patterns as $pattern) @@ -153,12 +162,16 @@ class TUrlMapping extends TModule $matches = $pattern->getPatternMatches($url); if(count($matches) > 0) { - $this->changeServiceParameters($pattern); - $this->initializeRequestParameters($matches); $this->_matched=$pattern; - break; + $this->changeServiceParameters($pattern); + $params=array(); + foreach($matches as $key=>$value) + if(is_string($key)) + $params[$key]=$value; + return $params; } } + return array(); } /** @@ -169,19 +182,6 @@ class TUrlMapping extends TModule return $this->_matched; } - /** - * @param array initialize the Request with matched parameters. - */ - protected function initializeRequestParameters($matches) - { - $request = $this->getRequest(); - foreach($matches as $k => $v) - { - if(!is_int($k)) - $request->add($k,$v); - } - } - /** * @param TUrlMappingPattern change the Request service ID and page class. */ -- cgit v1.2.3