www.gusucode.com > iWebshop开源PHP商城系统 v2.8源码程序 > iwebshop/lib/core/util/urlmanager_class.php

    <?php
/**
 * @copyright (c) 2011 jooyea.cn
 * @file urlmanager_class.php
 * @brief URL处理类
 * @author walu
 * @date 2011-07-18
 * @version 0.7
 * @note
 */

/**
 * @class IUrl
 * @brief URL处理类
 * @note
 */
class IUrl
{
	const UrlNative		= 1; //原生态的Url形式,指从index.php  例如:index.php?controller=blog&action=read&id=100
	const UrlPathinfo	= 2; //路径形式的Url,                 例如:/blog/read/id/100
	const UrlDiy		= 3; //经过urlRoute后的Url            例如: /blog-100.html

	const UrlCtrlName	= 'controller';
	const UrlActionName	= 'action';
	const UrlModuleName	= 'module';

	const Anchor = "/#&"; //urlArray中表示锚点的索引

	const QuestionMarkKey = "?";// /site/abc/?callback=/site/login callback=/site/login部分在UrlArray里的key

	private static $urlRoute = array(); //路由规则的缓存

	/**
	 * @brief 获取当前Controller、action、module的信息
	 * @param string $key controller或者action或者module
	 * @return string
	 */
	public static function getInfo($key)
	{
		$arr = array(
			'controller'=> self::UrlCtrlName,
			'action'    => self::UrlActionName,
			'module'    => self::UrlModuleName
		);

		return isset($arr[$key]) ? IReq::get($arr[$key]) : '';
	}

	/**
	 * @brief 将Url从IWeb支持的一种Url格式转成另一个格式。
	 * @param string $url 想转换的url
	 * @param int $from IUrl::UrlNative或者.....
	 * @param int $to IUrl::UrlPathinfo或者.....
	 * @return string 如果转换失败则返回false
	 */
	public static function convertUrl($url,$from,$to)
	{
		if($from == $to)
		{
			return $url;
		}

		$urlArray = "";
		$fun_re = false;
		switch($from)
		{
			case self::UrlNative :
				$urlTmp = parse_url($url);
				$urlArray = self::queryStringToArray($urlTmp);
				break;
			case self::UrlPathinfo :
				$urlArray = self::pathinfoToArray($url);
				break;
			case self::UrlDiy :
				$urlArray = self::diyToArray($url);
				break;
			default:
				return $fun_re;
				break;
		}

		switch($to)
		{
			case self::UrlNative :
				$fun_re = self::urlArrayToNative($urlArray);
				break;
			case self::UrlPathinfo :
				$fun_re = self::urlArrayToPathinfo($urlArray);
				break;
			case self::UrlDiy:
				$fun_re = self::urlArrayToDiy($urlArray);
				break;
		}
		return $fun_re;
	}

	/**
	 * @brief 将controller=blog&action=read&id=100类的query转成数组的形式
	 * @param string $url
	 * @return array
	 */
	public static function queryStringToArray($url)
	{
		if(!is_array($url))
		{
			$url = parse_url($url);
		}
		$query = isset($url['query'])?explode("&",$url['query']):array();
		$re = array();
		foreach($query as $value)
		{
			$tmp = explode("=",$value);
			if( count($tmp) == 2 )
			{
				$re[$tmp[0]] = $tmp[1];
			}
		}
		$re = self::sortUrlArray($re);
		isset($url['fragment']) && ($re[self::Anchor] = $url['fragment'] );
		return $re;
	}

	/**
	 * @brief 将/blog/read/id/100形式的url转成数组的形式
	 * @param string $url
	 * @return array
	 */
	public static function pathinfoToArray($url)
	{
		//blog/read/id/100
		//blog/read/id/100?comment=true#abcde
		$data = array();
		preg_match("!^(.*?)?(\\?[^#]*?)?(#.*)?$!",$url,$data);
		$re = array();
		if( isset($data[1]) && trim($data[1],"/ "))
		{
			$string = explode("/", trim($data[1],"/ ")   );
			$key = null;
			$i = 1;
			//前两个是ctrl和action,后面的是参数名和值
			foreach($string as $value)
			{
				if($i <= 2  )
				{
					$tmpKey = ($i==1) ? self::UrlCtrlName : self::UrlActionName;
					$re[$tmpKey] = $value;
					$i ++ ;
					continue;
				}

				if($key === null)
				{
					$key = $value;
					$re[$key]="";
				}
				else
				{
					$re[$key] = $value;
					$key = null;
				}
			}
		}
		if( isset($data[2]) || isset($data[3]) )
		{
			$re[ self::QuestionMarkKey ] = ltrim($data[2],"?");
		}

		if(isset($data[3]))
		{
			$re[ self::Anchor ] = ltrim($data[3],"#");
		}

		$re = self::sortUrlArray($re);
		return $re;

	}

	/**
	 * @brief 将用户请求的url进行路由转换,得到urlArray
	 * @param string  $url
	 * @return array
	 */
	public static function diyToArray($url)
	{
		return self::decodeRouteUrl($url);
	}

	/**
	 * @brief 对Url数组里的数据进行排序
	 * ctrl和action最靠前,其余的按key排序
	 * @param array $re
	 * @access private
	 */
	private static function sortUrlArray($re)
	{
		$fun_re=array();
		isset( $re[self::UrlCtrlName] ) && ($fun_re[self::UrlCtrlName]=$re[self::UrlCtrlName]);
		isset( $re[self::UrlActionName] ) && ($fun_re[self::UrlActionName]=$re[self::UrlActionName]);
		unset($re[self::UrlCtrlName],$re[self::UrlActionName]);
		ksort($re);
		$fun_re = array_merge($fun_re,$re);
		return $fun_re;
	}

	/**
	 * @brief 将urlArray用pathinfo的形式表示出来
	 * @access private
	 */
	private static function urlArrayToPathinfo($arr)
	{
		$re = "";
		$ctrl	= isset($arr[self::UrlCtrlName])   ? $arr[self::UrlCtrlName]   : '';
		$action	= isset($arr[self::UrlActionName]) ? $arr[self::UrlActionName] : '';

		$ctrl   != "" && ($re.="/{$ctrl}");
		$action != "" && ($re.="/{$action}");

		$fragment = isset($arr[self::Anchor]) ? $arr[self::Anchor] : "";
		$questionMark = isset($arr[self::QuestionMarkKey]) ? $arr[self::QuestionMarkKey] : "";
		unset($arr[self::UrlCtrlName],$arr[self::UrlActionName],$arr[self::Anchor]);
		foreach($arr as $key=>$value)
		{
			$re.="/{$key}/{$value}";
		}
		if($questionMark != "")
		{
			$re .= "?". $questionMark;
		}
		$fragment != "" && ($re .= "#{$fragment}");
		return $re;
	}

	/**
	 * @brief 将urlArray用原生url形式表现出来
	 * @access private
	 */
	private static function urlArrayToNative($arr)
	{
		$re = "/";
		$re .= self::getIndexFile();
		$fragment = isset($arr[self::Anchor]) ? $arr[self::Anchor] : "";

		$questionMark = isset($arr[self::QuestionMarkKey]) ? $arr[self::QuestionMarkKey] : "";

		unset($arr[self::Anchor] , $arr[self::QuestionMarkKey]  );
		if(count($arr))
		{
			$tmp = array();
			foreach($arr as $key=>$value)
			{
				$tmp[] ="{$key}={$value}";
			}
			$tmp = implode("&",$tmp);
			$re .= "?{$tmp}";
		}
		if( count($arr) && $questionMark!="" )
		{
			$re .= "&".$questionMark;
		}
		elseif($questionMark!="")
		{
			$re .= "?".$questionMark;
		}

		if($fragment != "")
		{
			$re .= "#{$fragment}";
		}
		return $re;
	}

	/**
	 * @brief 获取路由缓存
	 * @return array
	 */
	private static function getRouteCache()
	{
		//配置文件中不存在路由规则
		if(self::$urlRoute === false)
		{
			return null;
		}

		//存在路由的缓存信息
		if(self::$urlRoute)
		{
			return self::$urlRoute;
		}

		//第一次初始化
		$routeList = isset(IWeb::$app->config['urlRoute']) ? IWeb::$app->config['urlRoute'] : array();
		if(empty($routeList))
		{
			self::$urlRoute = false;
			return null;
		}

		$cacheRoute = array();
		foreach($routeList as $key => $val)
		{
			if(is_array($val))
			{
				continue;
			}

			$tempArray = explode('/',trim($val,'/'),3);
			if($tempArray < 2)
			{
				continue;
			}

			//进行路由规则的级别划分,$level越低表示匹配优先
			$level = 3;
			if    ( ($tempArray[0] != '<'.self::UrlCtrlName.'>') && ($tempArray[1] != '<'.self::UrlActionName.'>') ) $level = 0;
			elseif( ($tempArray[0] == '<'.self::UrlCtrlName.'>') && ($tempArray[1] != '<'.self::UrlActionName.'>') ) $level = 1;
			elseif( ($tempArray[0] != '<'.self::UrlCtrlName.'>') && ($tempArray[1] == '<'.self::UrlActionName.'>') ) $level = 2;

			$cacheRoute[$level][$key] = $val;
		}

		if(empty($cacheRoute))
		{
			self::$urlRoute = false;
			return null;
		}

		ksort($cacheRoute);
		self::$urlRoute = $cacheRoute;
		return self::$urlRoute;
	}

	/**
	 * @brief 将urlArray转成路由后的url
	 * @access private
	 */
	private static function urlArrayToDiy($arr)
	{
		if(!isset( $arr[self::UrlCtrlName] ) || !isset($arr[self::UrlActionName]) || !($routeList = self::getRouteCache()) )
		{
			return false;
		}

		foreach($routeList as $level => $regArray)
		{
			foreach($regArray as $regPattern => $value)
			{
				$urlArray = explode('/',trim($value,'/'),3);

				if($level == 0 && ($arr[self::UrlCtrlName].'/'.$arr[self::UrlActionName] != $urlArray[0].'/'.$urlArray[1]) )
				{
					continue;
				}
				else if($level == 1 && ($arr[self::UrlActionName] != $urlArray[1]) )
				{
					continue;
				}
				else if($level == 2 && ($arr[self::UrlCtrlName] != $urlArray[0]) )
				{
					continue;
				}

				$url = self::parseRegPattern($arr,array($regPattern => $value));

				if($url)
				{
					return $url;
				}
			}
		}
		return false;
	}

	/**
	 * @brief 根据规则生成URL
	 * @param $urlArray array url信息数组
	 * @param $regPattern array 路由规则
	 * @return string or false
	 */
	private static function parseRegPattern($urlArray,$regArray)
	{
		$regPattern = key($regArray);
		$value      = current($regArray);

		//存在自定义正则式
		if(preg_match_all("%<\w+?:.*?>%",$regPattern,$customRegMatch))
		{
			$regInfo = array();
			foreach($customRegMatch[0] as $val)
			{
				$val     = trim($val,'<>');
				$regTemp = explode(':',$val,2);
				$regInfo[$regTemp[0]] = $regTemp[1];
			}

			//匹配表达式参数
			$replaceArray = array();
			foreach($regInfo as $key => $val)
			{
				if(strpos($val,'%') !== false)
				{
					$val = str_replace('%','\%',$val);
				}

				if(isset($urlArray[$key]) && preg_match("%$val%",$urlArray[$key]))
				{
					$replaceArray[] = $urlArray[$key];
					unset($urlArray[$key]);
				}
				else
				{
					return false;
				}
			}

			$url = str_replace($customRegMatch[0],$replaceArray,$regPattern);
		}
		else
		{
			$url = $regPattern;
		}

		//处理多余参数
		$paramArray      = self::pathinfoToArray($value);

		$questionMarkKey = isset($urlArray[self::QuestionMarkKey]) ? $urlArray[self::QuestionMarkKey] : '';
		$anchor          = isset($urlArray[self::Anchor])          ? $urlArray[self::Anchor]          : '';
		unset($urlArray[self::UrlCtrlName],$urlArray[self::UrlActionName],$urlArray[self::Anchor],$urlArray[self::QuestionMarkKey]);
		foreach($urlArray as $key => $rs)
		{
			if(!isset($paramArray[$key]))
			{
				$questionMarkKey .= '&'.$key.'='.$rs;
			}
		}
		$url .= ($questionMarkKey) ? '?'.trim($questionMarkKey,'&') : '';
		$url .= ($anchor)          ? '#'.$anchor                    : '';

		return $url;
	}

	/**
	 * @brief 将请求的url通过路由规则解析成urlArray
	 * @param $url string 要解析的url地址
	 */
	private static function decodeRouteUrl($url)
	{
		$url       = trim($url,'/');
		$urlArray  = array();//url的数组形式
		$routeList = self::getRouteCache();
		if(!$routeList)
		{
			return $urlArray;
		}

		foreach($routeList as $level => $regArray)
		{
			foreach($regArray as $regPattern => $value)
			{
				//解析执行规则的url地址
				$exeUrlArray = explode('/',$value);

				//判断当前url是否符合某条路由规则,并且提取url参数
				$regPatternReplace = preg_replace("%<\w+?:(.*?)>%","($1)",$regPattern);
				if(strpos($regPatternReplace,'%') !== false)
				{
					$regPatternReplace = str_replace('%','\%',$regPatternReplace);
				}

				if(preg_match("%$regPatternReplace%",$url,$matchValue))
				{
					//是否完全匹配整个完整url
					$matchAll = array_shift($matchValue);
					if($matchAll != $url)
					{
						continue;
					}

					//如果url存在动态参数,则获取到$urlArray
					if($matchValue)
					{
						preg_match_all("%<\w+?:.*?>%",$regPattern,$matchReg);
						foreach($matchReg[0] as $key => $val)
						{
							$val                     = trim($val,'<>');
							$tempArray               = explode(':',$val,2);
							$urlArray[$tempArray[0]] = isset($matchValue[$key]) ? $matchValue[$key] : '';
						}

						//检测controller和action的有效性
						if( (isset($urlArray[ self::UrlCtrlName ]) && !preg_match("%^\w+$%",$urlArray[ self::UrlCtrlName ]) ) || (isset($urlArray[ self::UrlActionName ]) && !preg_match("%^\w+$%",$urlArray[ self::UrlActionName ]) ) )
						{
							$urlArray  = array();
							continue;
						}

						//对执行规则中的模糊变量进行赋值
						foreach($exeUrlArray as $key => $val)
						{
							$paramName = trim($val,'<>');
							if( ($val != $paramName) && isset($urlArray[$paramName]) )
							{
								$exeUrlArray[$key] = $urlArray[$paramName];
							}
						}
					}

					//分配执行规则中指定的参数
					$paramArray = self::pathinfoToArray(join('/',$exeUrlArray));
					$urlArray   = array_merge($urlArray,$paramArray);
					return $urlArray;
				}
			}
		}
		return $urlArray;
	}

	/**
	 * @brief  接收基准格式的URL,将其转换为Config中设置的模式
	 * @param  String $url      传入的url
	 * @return String $finalUrl url地址
	 */
	public static function creatUrl($url='')
	{
		//外部连接情况
		if(preg_match("!^[a-z]+://!i",$url))
		{
			return $url;
		}

		//缩略图路径情况,把路径参数中的 "/" 替换为 "-"
		if(strpos($url,'pic/thumb/') !== false)
		{
			$url = preg_replace_callback("!(?<=/img/)(.*)(?=\.\w+[/|$])!",create_function(
				'$matches',
				'return IFile::dirExplodeEncode($matches[1]);'
			),$url);
		}

		$baseUrl = self::getPhpSelf();
		if($url == "")
		{
			return self::getScriptDir();
		}
		elseif($url == "/")
		{
			return  self::getScriptDir().$baseUrl;
		}

		$rewriteRule = isset(IWeb::$app->config['rewriteRule'])?IWeb::$app->config['rewriteRule']:'native';

		//判断是否需要返回绝对路径的url
		$baseDir = self::getScriptDir();
		$tmpUrl = false;

		if($rewriteRule == 'pathinfo')
		{
			$tmpUrl = self::convertUrl($url,self::UrlPathinfo,self::UrlDiy);
		}

		if($tmpUrl !== false)
		{
			$url = $tmpUrl;
		}
		else
		{
			switch($rewriteRule)
			{
				case 'url': // 兼容以前的
				case 'get': //config文件里叫get
					$url = self::convertUrl($url,self::UrlPathinfo,self::UrlNative);
					break;
				case 'url-pathinfo':
					$url = "/".self::getIndexFile().$url;
					break;
			}
		}
		return $baseDir.trim($url,"/");
	}

	/**
	 * @brief 获取网站根路径
	 * @param  string $protocol 协议  默认为http协议,不需要带'://'
	 * @return String $baseUrl  网站根路径
	 *
	 */
	public static function getHost($protocol='http')
	{
		$host	 = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
		$baseUrl = $protocol.'://'.$host;
		return $baseUrl;
	}
	/**
	 * @brief 获取当前执行文件名
	 * @return String 文件名
	 */
	public static function getPhpSelf()
	{
		$re = explode("/",$_SERVER['SCRIPT_NAME']);
		return end($re);
	}
	/**
	 * @brief 返回入口文件URl地址
	 * @return string 返回入口文件URl地址
	 */
	public static function getEntryUrl()
	{
		return self::getHost().$_SERVER['SCRIPT_NAME'];
	}

	/**
	 * @brief 获取入口文件名
	 */
	public static function getIndexFile()
	{
		if(!isset($_SERVER['SCRIPT_NAME']))
		{
			return 'index.php';
		}
		else
		{
			return basename($_SERVER['SCRIPT_NAME']);
		}
	}

	/**
	 * @brief 返回页面的前一页路由地址伪静态格式,必须是本主机名下的
	 * @return string 返回页面的前一页路由地址
	 */
	public static function getRefRoute()
	{
		$host = self::getHost();
		$dir  = self::getScriptDir();
		if(isset($_SERVER['HTTP_REFERER']) && ($host & $_SERVER['HTTP_REFERER']) == $host)
		{
			$localUrlType = IWeb::$app->config['rewriteRule'] == 'pathinfo' ? self::UrlPathinfo : self::UrlNative;
			$url = self::convertUrl($_SERVER['HTTP_REFERER'],$localUrlType,self::UrlPathinfo);
			$url = str_replace(array($host,self::getIndexFile()),'',$url);
			if($dir != '/')
			{
				$url = str_replace($dir,'',$url);
			}
			return '/'.trim($url,'/\\');
		}
		else
		{
			return '';
		}
	}
	/**
	 * @brief  获取当前脚本所在文件夹
	 * @return 脚本所在文件夹
	 */
	public static function getScriptDir()
	{
		$return = strtr(dirname($_SERVER['SCRIPT_NAME']),"\\","/");
		return $return == '/' ? '/' : $return.'/';
	}

	/**
	 * @brief 获取当前url地址[经过RewriteRule之后的]
	 * @return String 当前url地址
	 */
	public static function getUrl()
	{
		if (isset($_SERVER['HTTP_X_REWRITE_URL']))
		{
			// check this first so IIS will catch
			$requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
		}
		elseif(isset($_SERVER['IIS_WasUrlRewritten']) && $_SERVER['IIS_WasUrlRewritten'] == '1' && isset($_SERVER['UNENCODED_URL'])       && $_SERVER['UNENCODED_URL'] != '')
		{
			// IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
			$requestUri = $_SERVER['UNENCODED_URL'];
		}
		elseif (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'],"Apache")!==false )
		{
			$requestUri = $_SERVER['PHP_SELF'];
		}
		elseif(isset($_SERVER['REQUEST_URI']))
		{
			$requestUri = $_SERVER['REQUEST_URI'];
		}
		elseif(isset($_SERVER['ORIG_PATH_INFO']))
		{
			// IIS 5.0, PHP as CGI
			$requestUri = $_SERVER['ORIG_PATH_INFO'];
			if (!empty($_SERVER['QUERY_STRING']))
			{
				$requestUri .= '?' . $_SERVER['QUERY_STRING'];
			}
		}
		else
		{
			die("getUrl is error");
		}
		return self::getHost().$requestUri;
	}

	/**
	 * @brief 获取当前URI地址
	 * @return String 当前URI地址
	 */
	public static function getUri()
	{
		if( !isset($_SERVER['REQUEST_URI']) ||  $_SERVER['REQUEST_URI'] == "" )
		{
			// IIS 的两种重写
			if (isset($_SERVER['HTTP_X_ORIGINAL_URL']))
			{
				$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
			}
			else if (isset($_SERVER['HTTP_X_REWRITE_URL']))
			{
				$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
			}
			else
			{
				//修正pathinfo
				if ( !isset($_SERVER['PATH_INFO']) && isset($_SERVER['ORIG_PATH_INFO']) )
					$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];


				if ( isset($_SERVER['PATH_INFO']) ) {
					if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] )
						$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
					else
						$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
				}

				//修正query
				if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']))
				{
					$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
				}

			}
 		}
		return $_SERVER['REQUEST_URI'];
	}

	/**
	 * @brief 获取url参数
	 * @param String url 需要分析的url,默认为当前url
	 */
	public static function beginUrl($url='')
	{
		//四种
		//native: /index.php?controller=blog&action=read&id=100
		//pathinfo:/blog/read/id/100
		//native-pathinfo:/index.php/blog/read/id/100
		//diy:/blog-100.html
		$obj = IServerVars::factory($_SERVER['SERVER_SOFTWARE']);
		$url  = !empty($url)?$url:$obj->realUri();
		preg_match('/\.php(.*)/',$url,$phpurl);
		if(!isset($phpurl[1]) || !$phpurl[1])
		{
			if($url != "" )
			{
				//强行赋值
				//todo:检测是否有bug
				$phpurl = array(1=>"?");
			}
			else
			{
				return;
			}
		}
		$url = $phpurl[1];
		$urlArray = array();
		$rewriteRule = isset(IWeb::$app->config['rewriteRule'])?IWeb::$app->config['rewriteRule']:'native';
		if($rewriteRule!='native')
		{
			$urlArray = self::decodeRouteUrl($url);
		}
		if($urlArray == array())
		{
			if( $url[0] == '?' )
			{
				$urlArray = $_GET;
			}
			else
			{
				$urlArray = self::pathinfoToArray($url);
			}
		}
		if( isset($urlArray[self::UrlCtrlName]) )
		{
			$tmp = explode('-',$urlArray[self::UrlCtrlName]);
			if( count($tmp) == 2 )
			{
				IReq::set('module',$tmp[0]);
				IReq::set(self::UrlCtrlName , $tmp[1]);
			}
			else
			{
				IReq::set(self::UrlCtrlName , $urlArray[self::UrlCtrlName] );
			}
		}
		if( isset($urlArray[self::UrlActionName])  )
		{
			IReq::set(self::UrlActionName,$urlArray[self::UrlActionName]);
			if(IReq::get('action')=='run')
			{
				IReq::set('action',null);
			}
		}

		unset($urlArray[self::UrlActionName] , $urlArray[self::UrlActionName] , $urlArray[self::Anchor] );
		foreach($urlArray as $key=>$value)
		{
			IReq::set($key,$value);
		}
	 }
	/**
	 * @brief  获取拼接两个地址
	 * @param  String $path_a
	 * @param  String $path_b
	 * @return string 处理后的URL地址
	 */
	public static function getRelative($path_a,$path_b)
	{
		$path_a = strtolower(str_replace('\\','/',$path_a));
		$path_b = strtolower(str_replace('\\','/',$path_b));
		$arr_a = explode("/" , $path_a) ;
		$arr_b = explode("/" , $path_b) ;
		$i = 0 ;
		while (true)
		{
			if($arr_a[$i] == $arr_b[$i]) $i++ ;
			else break ;
		}
		$len_b = count($arr_b) ;
		$len_a = count($arr_a) ;
		if(!$arr_b[$len_b-1])$len_b = $len_b - 1;
		if(!$len_a[$len_a-1])$len_a = $len_a - 1;
		$len = ($len_b>$len_a)?$len_b:$len_a ;
		$str_a = '' ;
		$str_b = '' ;
		for ($j = $i ;$j<$len ;$j++)
		{
			if(isset($arr_a[$j]))
			{
				$str_a .= $arr_a[$j].'/' ;
			}
			if(isset($arr_b[$j])) $str_b .= "../" ;
		}
		return $str_b . $str_a ;
	}
}

//$_SERVER里与url有关的两个量的兼容处理方案
//这是个不成熟的解决方案,可能仅适用于本框架
interface IIServerVars
{
	/**
	 * 获取REQUEST_URI
	 * 标准:真实的从浏览器地址栏体现出来的url,不包括#锚点
	 */
	public function requestUri();

	/**
	 * 最终呈现给服务器的url
	 * 标准:伪静态前与request_uri相同,伪静态后是最后一条规则调整后的url
	 */
	public function realUri();
}

class IServerVars implements IIServerVars
{
	public static function factory($server_type)
	{
		$obj = null;
		$type = array(
			'apache' => 'IServerVars_Apache',
			'iis'	=> 'IServerVars_IIS' ,
			'nginx' => 'IServerVars_Nginx'
		);

		foreach($type as $key=>$value)
		{
			if(stripos($server_type,$key) !== false )
			{
				$obj = new $value($server_type);
				break;
			}
		}

		if($obj === null)
		{
			return new IServerVars();
		}
		else
		{
			return $obj;
		}
	}

	public function requestUri()
	{
		return $_SERVER['REQUEST_URI'];
	}

	public function realUri()
	{
		return $_SERVER['REQUEST_URI'];
	}
}

class IServerVars_Apache implements IIServerVars
{
	public function __construct($server_type)
	{}

	public function requestUri()
	{
		return $_SERVER['REQUEST_URI'];
	}

	public function realUri()
	{
		return $_SERVER['PHP_SELF'];
	}
}

class IServerVars_IIS implements IIServerVars
{
	public function __construct($server_type){}

	public function requestUri()
	{
		$re = "";
		if(isset($_SERVER['REQUEST_URI']))
		{
			$re = $_SERVER['REQUEST_URI'];
		}
		elseif( isset($_SERVER['HTTP_X_REWRITE_URL']) )
		{
			//不取HTTP_X_REWRITE_URL
			$re = $_SERVER['HTTP_X_REWRITE_URL'];
		}
		elseif(isset($_SERVER["SCRIPT_NAME"] ) && isset($_SERVER['QUERY_STRING']) )
		{
			$re = $_SERVER["SCRIPT_NAME"] .'?'. $_SERVER['QUERY_STRING'];
		}
		return $re;
	}

	public function realUri()
	{
		$re= "";
		if( isset($_SERVER['HTTP_X_REWRITE_URL'])  )
		{
			$re = isset($_SERVER['ORIG_PATH_INFO']) ? $_SERVER['ORIG_PATH_INFO'] : $_SERVER['HTTP_X_REWRITE_URL'];
		}
		elseif(isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != "" )
		{
			$re = $_SERVER['PATH_INFO'];
		}
		elseif(isset($_SERVER["SCRIPT_NAME"] ) && isset($_SERVER['QUERY_STRING']) )
		{
			$re = $_SERVER["SCRIPT_NAME"] .'?'. $_SERVER['QUERY_STRING'];
		}
		return $re;
	}
}

class IServerVars_Nginx implements IIServerVars
{
	public function __construct($server_type){}
	public function requestUri()
	{
		$re = "";
		if(isset($_SERVER['REQUEST_URI']))
		{
			$re = $_SERVER['REQUEST_URI'];
		}
		return $re;
	}
	public function realUri()
	{
		$re = "";
		if(isset($_SERVER['DOCUMENT_URI']) )
		{
			$re = $_SERVER['DOCUMENT_URI'];
		}
		elseif( isset($_SERVER['REQUEST_URI']) )
		{
			$re = $_SERVER['REQUEST_URI'];
		}
		return $re;
	}
}