From 8310eca035cbaa463fdcfa5ce7b688ae0dbfb530 Mon Sep 17 00:00:00 2001 From: wei <> Date: Fri, 22 Sep 2006 23:36:51 +0000 Subject: Add url mapping. --- framework/Web/TUrlMapping.php | 391 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 framework/Web/TUrlMapping.php (limited to 'framework') diff --git a/framework/Web/TUrlMapping.php b/framework/Web/TUrlMapping.php new file mode 100644 index 00000000..3062b898 --- /dev/null +++ b/framework/Web/TUrlMapping.php @@ -0,0 +1,391 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +/** + * TUrlMapping Class + * + * The TUrlMapping module allows aributary URL path to be mapped to a + * 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. + * Similar to other modules, the <url /> configuration class + * can be customised using the class property. + * + * The URL mapping are evaluated in order, only the first mapping that matches + * the URL will be used. Cascaded mapping can be achieved by placing the URL mappings + * in particular order. For example, placing the most specific mappings first. + * + * The mapping can be load from an external file by specifying a configuration + * file using the {@link setConfigFile ConfigFile} property. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web + * @since 3.0.5 + */ +class TUrlMapping extends THttpRequest +{ + /** + * @var string default pattern class. + */ + private $_defaultPatternClass='TUrlMappingPattern'; + /** + * @var TUrlMappingPattern[] list of patterns. + */ + private $_patterns=array(); + /** + * @var TUrlMappingPattern matched pattern. + */ + private $_matched; + /** + * File extension of external configuration file + */ + const CONFIG_FILE_EXT='.xml'; + /** + * @var string external configuration file + */ + private $_configFile=null; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if module is configured in the global scope. + */ + public function 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(); + } + + /** + * Initialize the module from configuration file. + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + protected function loadConfigFile() + { + if(is_file($this->_configFile)) + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadUrlMappings($dom); + } + else + throw new TConfigurationException( + 'urlpath_dispatch_configfile_invalid',$this->_configFile); + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,self::CONFIG_FILE_EXT))===null) + throw new TConfigurationException('logrouter_configfile_invalid',$value); + } + + /** + * Load and configure each url mapping pattern. + * @param TXmlElement configuration node + * @throws TConfigurationException if specific pattern class is invalid + */ + protected function loadUrlMappings($xml) + { + foreach($xml->getElementsByTagName('url') as $url) + { + $properties=$url->getAttributes(); + $class=$properties->remove('class'); + if($class===null) + $class = $this->_defaultPatternClass; + $pattern = Prado::createComponent($class); + if(!($pattern instanceof TUrlMappingPattern)) + throw new TConfigurationException('urlpath_dispatch_invalid_pattern_class'); + foreach($properties as $name=>$value) + $pattern->setSubproperty($name,$value); + $this->_patterns[] = $pattern; + $pattern->init($url); + } + } + + /** + * Using the request URL path, find the first matching pattern. If found + * the matched pattern parameters are used in the Request object. + */ + protected function resolveMappings() + { + $url = $this->getRequest()->getUrl(); + foreach($this->_patterns as $pattern) + { + $matches = $pattern->getPatternMatches($url); + if(count($matches) > 0) + { + $this->changeServiceParameters($pattern); + $this->initializeRequestParameters($matches); + $this->_matched=$pattern; + break; + } + } + } + + /** + * @return TUrlMappingPattern the matched pattern, null if not found. + */ + public function getMatchingPattern() + { + 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. + */ + protected function changeServiceParameters($pattern) + { + $request = $this->getRequest(); + $id = $pattern->getServiceID(); + $page = $pattern->getPageClass(); + $request->setServiceID($id); + $request->setServiceParameter($page); + $request->add($id,$page); + } +} + +/** + * URL Mapping Pattern Class + * + * Describes an URL mapping pattern, if a given URL matches the pattern, the + * TUrlMapping class will alter the THttpRequest parameters. The + * url matching is done using regular expressions. + * + * The {@link setPattern Pattern} property takes a regular expression with + * parameter names enclosed between a left brace '{' and a right brace '}'. + * The pattens for each parameter can be set using {@link getParameters Parameters} + * attribute collection. For example + * + * + * + * + * In the above example, the pattern contains 3 parameters named "year", + * "month" and "day". The pattern for these parameters are, respectively, + * "\d{4}" (4 digits), "\d{2}" (2 digits) and "\d+" (1 or more digits). + * + * In the TUrlMappingPattern class, the pattern is matched against the + * path property of the url only. + * + * Thus, only an url that matches the pattern will be valid. For example, + * an url "http://example.com/articles/2006/07/21" will matches and is valid. + * However, "http://example.com/articles/2006/07/hello" is not + * valid since the "day" parameter pattern is not satisfied. + * + * The parameter values are available through the standard Request + * object. For example, $this->Request['year']. + * + * The {@link setPageClass PageClass} and {@link setServiceID ServiceID} + * (the default ID is 'page') set the class and the service that will + * handle the matching URL. + * + * For more complicated mappings, the body of the <url> + * can be used to specify the mapping pattern. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web + * @since 3.0.5 + */ +class TUrlMappingPattern extends TComponent +{ + /** + * @var string page class name. + */ + private $_pageClass; + /** + * @var string service ID, default is 'page'. + */ + private $_serviceID='page'; + /** + * @var string url pattern to match. + */ + private $_pattern; + /** + * @var TMap parameter regular expressions. + */ + private $_parameters; + /** + * @var string regular expression pattern. + */ + private $_regexp; + + public function __construct() + { + $this->_parameters = Prado::createComponent('System.Collections.TAttributeCollection'); + } + + /** + * Initialize the pattern, uses the body content as pattern is available. + * @param TXmlElement configuration for this module. + * @throws TConfigurationException if page class is not specified. + */ + public function init($config) + { + $body = trim($config->getValue()); + if(strlen($body)>0) + $this->setPattern($body); + if(is_null($this->_pageClass)) + { + throw new TConfigurationException( + 'dispatcher_url_page_class_missing', $this->getPattern()); + } + $this->initializePattern(); + } + + /** + * Subsitutue the parameter key value pairs as named groupings + * in the regular expression matching pattern. + */ + protected function initializePattern() + { + $params= array(); + $values = array(); + foreach($this->parameters as $key => $value) + { + $params[] = '{'.$key.'}'; + $values[] = '(?P<'.$key.'>'.$value.')'; + } + $this->_regexp = str_replace($params,$values,$this->getPattern()); + } + + /** + * @return string mapping pattern + */ + protected function getRegExpPattern() + { + return $this->_regexp; + } + + /** + * @param string name of the page class to handle the request. + */ + public function setPageClass($value) + { + $this->_pageClass=$value; + } + + /** + * @return string page class name. + */ + public function getPageClass() + { + return $this->_pageClass; + } + + /** + * @param string service id to handle. + */ + public function setServiceID($value) + { + $this->_serviceID=$value; + } + + /** + * @return string service id. + */ + public function getServiceID() + { + return $this->_serviceID; + } + + /** + * @return string url pattern to match. + */ + public function getPattern() + { + return $this->_pattern; + } + + /** + * @param string url pattern to match. + */ + public function setPattern($value) + { + $this->_pattern = $value; + } + + /** + * @return TAttributeCollection parameter key value pairs. + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @param TAttributeCollection new parameter key value pairs. + */ + public function setParameters($value) + { + $this->_parameters=$value; + } + + /** + * Use regular expression to match the given url path. + * @param TUri url to match against + * @return array matched parameters, empty if no matches. + */ + public function getPatternMatches($url) + { + $path = $url->getPath(); + $matches=array(); + $pattern = str_replace('/', '\\/', $this->getRegExpPattern()); + preg_match('/'.$pattern.'/', $path, $matches); + return $matches; + } +} + +?> \ No newline at end of file -- cgit v1.2.3