diff options
-rw-r--r-- | .gitattributes | 1 | ||||
-rw-r--r-- | HISTORY | 2 | ||||
-rw-r--r-- | framework/Exceptions/messages.txt | 4 | ||||
-rw-r--r-- | framework/TApplication.php | 2 | ||||
-rw-r--r-- | framework/Web/THttpRequest.php | 194 | ||||
-rw-r--r-- | framework/Web/TUrlManager.php | 142 | ||||
-rw-r--r-- | framework/Web/TUrlMapping.php | 54 |
7 files changed, 268 insertions, 131 deletions
diff --git a/.gitattributes b/.gitattributes index 665367a7..751393fa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1922,6 +1922,7 @@ framework/Web/THttpRequest.php -text framework/Web/THttpResponse.php -text framework/Web/THttpSession.php -text framework/Web/THttpUtility.php -text +framework/Web/TUrlManager.php -text framework/Web/TUrlMapping.php -text framework/Web/UI/ActiveControls/TActiveButton.php -text framework/Web/UI/ActiveControls/TActiveControlAdapter.php -text @@ -24,6 +24,7 @@ BUG: TTableCell should render only when Text is not set and there's no ch BUG: global state was not saved when redirect() is invoked (Qiang) BUG: TPager would not display if it was invisible previously (Qiang) ENH: Ticket#446 - Added TMetaTagCollection.getMetaTagByID method (Qiang) +ENH: Ticket#451 - Modified TUrlMapping to extend from TUrlManager (Qiang) ENH: Ticket#468 - Added support of using all property tags in skins (Qiang) ENH: Ticket#471 - Added methods in TAssetManager to expose published path and URL (Qiang) CHG: Ticket#437 - __autoload is replaced by spl_autoload_register (Qiang) @@ -33,6 +34,7 @@ CHG: TDataGrid now does not set default table styles (Qiang) CHG: TRepeater does not render <span> anymore for empty item template (Qiang) CHG: THttpRequest.constructUrl() now encodes ampersand by default (Qiang) CHG: TDataBoundControl will not throw exception if CurrentPageIndex is out of range (Qiang) +NEW: TUrlManager (Qiang) Version 3.0.5 October 23, 2006 ============================== diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index 3afc7c3d..dcd3c749 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 ce05893b..be2cce19 100644 --- a/framework/TApplication.php +++ b/framework/TApplication.php @@ -880,6 +880,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:
* <code>
- * /index.php?ServiceID=ServiceParameter
+ * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2
+ * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2
* </code>
- * 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);
}
@@ -215,6 +243,35 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar }
/**
+ * @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.
*/
public function getUrlFormat()
@@ -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 @@ +<?php
+/**
+ * TUrlManager class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 5bfd8a42..69af7f2f 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 <tt>application.xml</tt> file and before any services.
- *
- * The mapping format is as follows.
* <code>
* <module id="friendly-url" class="System.Web.TUrlMapping">
* <url ServiceParameter="Posts.ViewPost" pattern="post/{id}/?" parameters.id="\d+" />
* <url ServiceParameter="Posts.ListPost" pattern="archive/{time}/?" parameters.time="\d{6}" />
* <url ServiceParameter="Posts.ListPost" pattern="category/{cat}/?" parameters.cat="\d+" />
* </module>
+ * <module id="request" class="THttpRequest" UrlManager="friendly-url" />
* </code>
*
* See {@link TUrlMappingPattern} for details regarding the mapping patterns.
@@ -38,14 +39,22 @@ * 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 <weizhuo[at]gmail[dot]com>
* @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.
*/
private $_defaultPatternClass='TUrlMappingPattern';
@@ -58,10 +67,6 @@ class TUrlMapping extends TModule */
private $_matched;
/**
- * File extension of external configuration file
- */
- const CONFIG_FILE_EXT='.xml';
- /**
* @var string external configuration file
*/
private $_configFile=null;
@@ -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();
}
/**
@@ -170,19 +183,6 @@ class TUrlMapping extends TModule }
/**
- * @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.
*/
protected function changeServiceParameters($pattern)
|