YRouter.class.php
32.6 KB
<?php/**
* 路由处理
*
* @name Framework_YRouter
* @version 0.1 2012-6-25
* @package framework
* @author xiaoma
*/class Framework_YRouter {
/**
* 匹配模式各部分的类型
*/
const PARTTYPE_VAR = 'var';
const PARTTYPE_STATIC = 'static';
const PARTTYPE_WILDCARD = 'wildcard';
/**
* 路由的类型
*/
const ROUTE_SIMPLE = 'simple';
const ROUTE_REGEX = 'regex';
/**
* 当前匹配成功的路由名字
*
* @var string
*/
protected $_matched_route_name;
/**
* 最后一次反向匹配成功的路由名称
*
* @var string
*/
protected $_reverse_matched_route_name;
/**
* 路由
*
* @var array
*/
protected $_routes = array();
/**
* 用于识别变量的前缀符号
*
* @var string
*/
protected $_var_prefix = ':';
/**
* 用于分割匹配模式多个部分的定界符
*
* @var string
*/
protected $_part_delimiter = '/';
/**
* 匹配变量的正则表达式
*
* @var string
*/
protected $_default_var_regex = '.+';
/**
* 保留变量的默认比对规则
*
* @var string
*/
protected $_udi_var_regex = '([a-z][a-z0-9]*)*';
/**
* 保留的变量
*
* @var array
*/
protected $_udi_parts = array(
Framework_YHttpRequest::UDI_MODULE => Framework_YHttpRequest::UDI_DEFAULT_MODULE,
Framework_YHttpRequest::UDI_NAMESPACE => Framework_YHttpRequest::UDI_DEFAULT_NAMESPACE,
Framework_YHttpRequest::UDI_CONTROLLER => Framework_YHttpRequest::UDI_DEFAULT_CONTROLLER,
Framework_YHttpRequest::UDI_ACTION => Framework_YHttpRequest::UDI_DEFAULT_ACTION,
);
/**
* 导入路由规则
*
*
* @param array $rules
*
* @return QRouter
*/
public function import(array $rules)
{
//@todo 这里需要增加缓存.否则每次都需要解析 (暂未实现)
$routes = array();
foreach ($rules as $route_name => $rule)
{
$routes[$route_name] = $this->prepareRoute($route_name, $rule);
}
$this->_routes = array_merge($this->_routes, $routes);
return $this;
}
/**
* 返回框架默认路由
*
* @return unknown
*/
public function getDefaultRouter(){
$udi_controller = Framework_YHttpRequest::UDI_CONTROLLER ;
$udi_action = Framework_YHttpRequest::UDI_ACTION ;
$def_controller = Framework_YHttpRequest::UDI_DEFAULT_CONTROLLER ;
$def_action = Framework_YHttpRequest::UDI_DEFAULT_ACTION ;
return array(
'global_default_routes' => array(
'pattern' => "/:{$udi_controller}/:{$udi_action}/*",
'defaults' => array($udi_controller => $def_controller,$udi_action=>$def_action),
),
);
}
/**
* 添加一条路由规则
*
* @param string $route_name
* @param array $rule
*
* @return QRouter
*/
public function add($route_name, array $rule)
{
$this->_routes[$route_name] = $this->prepareRoute($route_name, $rule);
return $this;
}
/**
* 准备指定的路由
*
* @param string $route_name
* @param array $rule
*
* @return array
*/
public function prepareRoute($route_name, array $rule)
{
if (isset($rule['regex']))
{
return $this->_prepareRegexRoute($route_name, $rule);
}
else
{
return $this->_prepareSimpleRoute($route_name, $rule);
}
}
/**
* 移除指定的路由规则
*
* @param string $route_name
*
* @return QRouter
*/
public function remove($route_name)
{
unset($this->_routes[$route_name]);
return $this;
}
/**
* 取得指定名称的路由规则
*
* @param string $route_name
*
* @return array
*/
public function get($route_name)
{
return $this->_routes[$route_name];
}
/**
* 匹配路由规则,成功返回匹配结果,失败返回 false
*
* @param string $path
*
* @return array|boolean
*/
public function match($path)
{
// 分割 URL
$path_parts = explode($this->_part_delimiter, trim($path, $this->_part_delimiter));
$this->_matched_route_name = null;
foreach ($this->_routes as $route_name => $route)
{
if ($route['type'] == self::ROUTE_SIMPLE)
{
$result = $this->_matchSimpleRoute($route, $path_parts);
}
else
{
$result = $this->_matchRegexRoute($route, $path);
}
if ($result !== false)
{
$this->_matched_route_name = $route_name;
foreach ($this->_udi_parts as $varname => $value)
{
if (!isset($result[$varname])) $result[$varname] = $value;
}
return $result;
}
}
return false;
}
/**
* 返回最后一次匹配成功的路由名称
*
* @return string
*/
public function lastMatchedRouteName()
{
return $this->_matched_route_name;
}
/**
* 通过反相匹配路由规则来生成 URL
*
* @param array $url_args
* @param string $route_name
*
* @return string
*/
public function url(array $url_args, $route_name = null)
{
// 补齐 UDI 参数
foreach ($this->_udi_parts as $varname => $default_value)
{
if (empty($url_args[$varname]))
{
$url_args[$varname] = $default_value;
}
}
if ($route_name && isset($this->_routes[$route_name]))
{
// 用指定路由解析
$url = $this->_reverseMatch($this->_routes[$route_name], $url_args);
if ($url !== false)
{
$this->_reverse_matched_route_name = $route_name;
return $url;
}
}
else
{
foreach ($this->_routes as $route_name => $route)
{
$url = $this->_reverseMatch($route, $url_args);
if ($url !== false)
{
$this->_reverse_matched_route_name = $route_name;
return $url;
}
}
}
/**
* 如果没有找到匹配的规则,则使用内置的规则
*/
$this->_reverse_matched_route_name = null;
if (empty($url_args)) return '';
$url = '?' . http_build_query($url_args, '', '&');
return $url;
}
/**
* 返回最后一次反向匹配成功的路由名称
*
* @return string
*/
public function lastReverseMatchedRouteName()
{
return $this->_reverse_matched_route_name;
}
/**
* 匹配基于正则的路由规则,成功返回匹配结果,失败返回 false
*
* @param array $route
* @param array $path
*
* @return array|boolean
*/
protected function _matchRegexRoute(array $route, $path)
{
//如果配置了域名,则验证当前域名是否与配置中的域名一致
if ($route['domain'] && $this->currentDomain() != $route['domain'])
{
return false;
}
$m = null;
if (!preg_match($route['regex'], $path, $m)) return false;
$result = array();
foreach ($route['defaults'] as $varname => $has_default)
{
if ($has_default) $result[$varname] = $route['varnames'][$varname];
}
foreach ($route['vars'] as $varname => $offset)
{
if (!isset($m[$offset])) return false;
$result[$varname] = $m[$offset];
}
return $result;
}
/**
* 获取当前域名
*
* @return string
* @since 0.2
*/
public function currentDomain()
{
// 协议
$protocol = 'http';
if (isset($_SERVER ['HTTPS']) && $_SERVER ['HTTPS'] == 'on')
{
$protocol .= 's';
}
$protocol .= '://';
// 端口号
if (! isset($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 80)
{
$port = '';
}
else
{
$port = ':' . $_SERVER ['SERVER_PORT'];
}
// 主机名
if (isset($_SERVER['HTTP_HOST']))
{
$server = strtolower($_SERVER['HTTP_HOST']) . $port;
}
elseif (isset($_SERVER['SERVER_NAME']))
{
$server = $_SERVER['SERVER_NAME'] . $port;
}
else
{
$server = getenv('SERVER_NAME') . $port;
}
return $protocol . $server;
}
/**
* 匹配路径,成功返回匹配结果,失败返回 false
*
* @param array $route
* @param array $path
*
* @return array|boolean
*/
protected function _matchSimpleRoute(array $route, array $path)
{
// 保存匹配成功后获得的变量名值对
$values = array();
// 遍历匹配模式的每一个部分,确认 URL 中包含需要的内容
foreach ($route['pattern_parts'] as $pos => $part_type)
{
switch ($part_type)
{
case self::PARTTYPE_VAR:
$varname = $route['vars'][$pos];
if (isset($path[$pos]))
{
// 用 URL 的相应部分和变量比对规则进行比对
$regex = "#^{$route['config'][$varname]}\$#i";
if (!preg_match($regex, $path[$pos]))
{
return false;
}
$value = $path[$pos];
}
elseif ($route['defaults'][$varname])
{
// 如果该变量有默认值,则使用默认值
$value = $route['varnames'][$varname];
}
else
{
// 如果 URL 没有对应部分,并且变量没有默认值,则视为比对失败
return false;
}
$values[$varname] = $value;
break;
case self::PARTTYPE_STATIC:
if (!isset($path[$pos]) || strlen($path[$pos]) == 0)
{
if ($route['static_optional'][$pos])
{
// 对于可选的静态部分,允许不提供
continue;
}
return false;
}
$value = $path[$pos];
if ((string)$route['static_parts'][$pos] != (string)$value)
{
// 对于静态部分,如果 URL 没有提供该部分,或者与预期的不符,则比对失败
return false;
}
break;
case self::PARTTYPE_WILDCARD:
/**
* 对于通配符,获得剩余的所有部分,并停止匹配
*
* 剩余的所有参数都按照 /name/value 的形式解析,并存入 $values。
*/
while (isset($path[$pos]))
{
$varname = urldecode($path[$pos]);
// 如果路由中明确定义了一个变量,则不应该让通配符匹配的变量覆盖已定义变量的值
if (strlen($varname) && !isset($route['varnames'][$varname]))
{
if (isset($path[$pos + 1]))
{
$values[$varname] = urldecode($path[$pos + 1]);
}
else
{
$values[$varname] = '';
}
}
$pos += 2;
}
break;
}
}
// 如果 URL 还包含更多的部分,则比对失败
if (isset($path[$pos + 1]))
{
return false;
}
foreach ($route['varnames'] as $varname => $default_value)
{
if (!isset($values[$varname]))
{
if (!$route['defaults'][$varname])
{
// 如果某个变量没有在 URL 中提供,而该变量又没有默认值,则比对失败
return false;
}
$values[$varname] = $default_value;
}
}
return $values;
}
/**
* 根据参数创建匹配该路由的 URL,成功返回 URL 字符串,失败返回 FALSE
*
* @param array $route
* @param array $url_args
*
* @return string|boolean
*/
protected function _reverseMatch(array $route, array $url_args)
{
if ($route['type'] == self::ROUTE_SIMPLE)
{
return $this->_reverseMatchSimpleRoute($route, $url_args);
}
else
{
return $this->_reverseMatchRegexRoute($route, $url_args);
}
}
/**
* 根据参数创建匹配该路由的 URL,成功返回 URL 字符串,失败返回 FALSE
*
* @param array $route
* @param array $url_args
*
* @return string|boolean
*/
protected function _reverseMatchRegexRoute(array $route, array $url_args)
{
$valid = $this->_reverseMatchVars($route, $url_args);
if ($valid === false) return false;
if (!empty($url_args))
{
// 存在多余的变量,比对失败
return false;
}
$path = '';
$offset = 1;
$vars = array_flip($route['vars']);
foreach ($route['pattern'] as $part)
{
$path .= $part;
if (isset($vars[$offset]))
{
$varname = $vars[$offset];
$path .= (string)$valid[$varname];
}
$offset++;
}
return $path;
}
/**
* 根据参数创建匹配该路由的 URL,成功返回 URL 字符串,失败返回 FALSE
*
* @param array $route
* @param array $url_args
*
* @return string|boolean
*/
protected function _reverseMatchSimpleRoute(array $route, array $url_args)
{
$valid = $this->_reverseMatchVars($route, $url_args);
if ($valid === false) return false;
// 构造 URL
$path = array();
$query_args = array();
$use_wildcard = false;
foreach ($route['pattern_parts'] as $pos => $part_type)
{
switch ($part_type)
{
case self::PARTTYPE_VAR:
$varname = $route['vars'][$pos];
$path[$pos] = $valid[$varname];
break;
case self::PARTTYPE_STATIC:
$path[$pos] = $route['static_parts'][$pos];
break;
case self::PARTTYPE_WILDCARD:
// 处理通配符时,将 $url_args 中剩余的变量都用掉
if ($route['static_parts'][$pos] == '*')
{
$use_wildcard = !empty($url_args);
foreach ($url_args as $varname => $value)
{
$path[] = $varname;
$path[] = $value;
}
}
else
{
$query_args = $url_args;
}
$url_args = array();
break;
}
}
// 如果构造完 URL 后 $url_args 不为空,则说明 $url_args 存在该路由无法匹配的参数
if (!empty($url_args))
{
return false;
}
// 在没有使用通配符的情况下尝试消除 URL 中不必要的部分
if (!$use_wildcard)
{
$count = count($path);
for ($pos = $count - 1; $pos >= 0; $pos--)
{
switch ($route['pattern_parts'][$pos])
{
case self::PARTTYPE_STATIC:
// 一旦该部分是不可选的静态内容,则停止消除
if (!$route['static_optional'][$pos])
{
$pos = -1;
}
else
{
unset($path[$pos]);
}
break;
default:
// 如果该部分是变量,同时 $path 中的值又和路由中指定的默认值一样,则该部分可以消除
$varname = $route['vars'][$pos];
$default_value = $route['varnames'][$varname];
if ((string)$path[$pos] == (string)$default_value && $route['defaults'][$varname])
{
unset($path[$pos]);
}
else
{
// 否则终止消除
$pos = -1;
}
}
}
}
// 构造 URL
foreach ($path as $offset => $path_part)
{
if ((string)$path_part !== '')
{
$path[$offset] = rawurlencode($path_part);
}
else
{
unset($path[$offset]);
}
}
$url = $this->_part_delimiter;
if (!empty($path))
{
$url .= implode($this->_part_delimiter, $path);
}
if (!empty($query_args))
{
$url .= '?' . http_build_query($query_args, '', '&');
}
return $url;
}
/**
* 反向比对变量
*
* @param array $route
* @param array $url_args
*
* @return array
*/
protected function _reverseMatchVars(array $route, array & $url_args)
{
// 比对所有的变量
$valid = array();
foreach ($route['varnames'] as $varname => $default_value)
{
/**
* 1. 对于指定了验证规则的变量,首先检查 $url_args 中是否包含该变量
* 1.1 如果不包含,则检查路由中是否指定了默认值
* 1.1.1 如果没有指定默认值,则比对失败
* 1.1.2 如果提供了默认值,则将变量及默认值添加到 $url_args 中
* 1.2 如果包含,则用变量的验证规则进行比对
* 2. 对于没有指定验证规则的变量,首先检查 $url_args 中是否包含该变量
* 2.1 如果不包含,则检查是否有默认值
* 2.1.1 如果有默认值,则使用默认值
* 2.1.2 如果没有默认值,则比对失败
* 2.2 如果包含但不相等,或者没有提供默认值,则比对失败
*/
$has_default = $route['defaults'][$varname];
if (isset($route['config'][$varname]))
{
// 1. 对于指定了验证规则的变量,首先检查 $url_args 中是否包含该变量
if (!isset($url_args[$varname]))
{
// 1.1 如果不包含,则检查路由中是否指定了默认值
if (!$has_default)
{
// 1.1.1 如果没有指定默认值,则比对失败
return false;
}
// 1.1.2 如果提供了默认值,则将变量及默认值添加到 $url_args 中
$valid[$varname] = $default_value;
continue;
}
// 1.2 如果包含,则用变量的验证规则进行比对
$regex = "#^{$route['config'][$varname]}\$#i";
if (!preg_match($regex, $url_args[$varname]))
{
return false;
}
$valid[$varname] = $url_args[$varname];
}
else
{
// 2. 对于没有指定验证规则的变量,检查 $url_args 中是否包含该变量
if (!isset($url_args[$varname]))
{
// 2.1 如果不包含,则检查是否有默认值
if ($has_default)
{
// 2.1.1 如果有默认值,则使用默认值
$valid[$varname] = $default_value;
}
else
{
// 2.1.2 如果没有默认值,则比对失败
return false;
}
}
else
{
if ((string)$url_args[$varname] != (string)$default_value || !$has_default)
{
// 2.2 如果包含但与默认值不同,或者没有提供默认值,则比对失败
return false;
}
$valid[$varname] = $url_args[$varname];
}
}
unset($url_args[$varname]);
}
return $valid;
}
/**
* 准备基于正则的路由
*
* @param string $route_name
* @param array $rule
*
* @return array
*/
protected function _prepareRegexRoute($route_name, array $rule)
{
static $defaults = array(
'config' => array(),
'defaults' => array(),
);
foreach ($defaults as $key => $value)
{
if (!isset($rule[$key])) $rule[$key] = $value;
}
$route = array
(
'type' => self::ROUTE_REGEX,
// 路由的名字
'name' => $route_name,
// 正则表达式
'regex' => '',
// 用于构造 URL 的模式信息
'pattern' => array(),
// 变量的比对规则
'config' => array(),
// 变量在匹配模式中的位置及其名称
'vars' => $rule['config'],
// 所有已定义的变量及默认值
'varnames' => array(),
// 指示所有变量是否设置了默认值
'defaults' => array(),
//如果设置了域名,则反向解析时会匹配,如果域名相同,则匹配,反之不匹配
'domain' => NULL,
);
$route['domain'] = (isset($rule['domain']) && $rule['domain'])?$rule['domain']:NULL;
$regex = $rule['regex'];
$route['regex'] = "#^{$regex}\$#i";
if (preg_match($route['regex'], '') === false)
{
// LC_MSG: 无效的路由规则 "%s",因为这个基于正则的路由在指定了无效的规则 "%s".
$msg = '无效的路由规则 "%s",因为这个基于正则的路由指定了无效的比对规则 "%s".';
//@TODO 无效的路由规则
throw new QRouter_InvalidRouteException($route_name, array($msg, $route_name, $rule['regex']));
}
# contents:
# regex: contents\-([a-z0-9]+)\-([0-9]+)(\.html)?
# config:
# category: 1
# id: 2
# format: 3
# defaults:
# module: cms
# controller: contents
# action: view
# format: .html
#
// 从 regex 中分离出每个变量的比对规则
$m = null;
if (preg_match_all('/\((.+?)\)/i', $regex, $m, PREG_OFFSET_CAPTURE) === false)
{
// LC_MSG: 无效的路由规则 "%s",因为这个基于正则的路由在指定了无效的规则 "%s".
$msg = '无效的路由规则 "%s",因为这个基于正则的路由指定了无效的比对规则 "%s".';
//@TODO 无效的路由规则
throw new QRouter_InvalidRouteException($route_name, array($msg, $route_name, $rule['regex']));
}
if (count($m[1]) != count($rule['config']))
{
// LC_MSG: 无效的路由规则 "%s",因为这个基于正则的路由在比对规则中指定的变量个数与 config 设置中指定的个数不同.
$msg = '无效的路由规则 "%s",因为这个基于正则的路由在比对规则中指定的变量个数 (%d) 与 config 设置中指定的个数 (%d) 不同.';
//@TODO 无效的路由规则
throw new QRouter_InvalidRouteException($route_name, array($msg, $route_name, count($m[1]), count($rule['config'])));
}
$index2vars = array_flip($route['vars']);
$pattern = array();
$pos = 0;
foreach ($index2vars as $index => $varname)
{
--$index;
$route['config'][$varname] = $m[1][$index][0];
$route['varnames'][$varname] = '';
$route['defaults'][$varname] = false;
$offset = $m[0][$index][1];
$len = strlen($m[0][$index][0]);
$pattern[] = substr($regex, $pos, $offset - $pos);
$pos = $offset + $len;
}
$pattern[] = substr($regex, $pos);
// . \ + * ? [ ^ ] $ ( ) { } = ! < > |
static $exp_chars = array(
'search' => array(
'\\.', '\\\\', '\\+', '\\*', '\\?', '\\[', '\\^', '\\]', '\\$',
'\\(', '\\)', '\\{', '\\}', '\\=', '\\!', '\\<', '\\>', '\\|',
'\\-',
),
'replace' => array(
'.', '\\', '+', '*', '?', '[', '^', ']', '$',
'(', ')', '{', '}', '=', '!', '<', '>', '|',
'-',
),
);
foreach ($pattern as $offset => $part)
{
if (strlen($part) == 0)
{
unset($pattern[$offset]);
}
else
{
$part = str_replace($exp_chars['search'], $exp_chars['replace'], $part);
$pattern[$offset] = $part;
}
}
$route['pattern'] = $pattern;
// 将规则中指定了默认值的变量添加到变量列表中
foreach ($rule['defaults'] as $varname => $value)
{
$route['varnames'][$varname] = $value;
$route['defaults'][$varname] = true;
}
// 将 UDI 中的变量添加到列表
foreach ($this->_udi_parts as $varname => $value)
{
if (!isset($route['varnames'][$varname]) || strlen($route['varnames'][$varname]) == 0)
{
$route['varnames'][$varname] = $value;
$route['defaults'][$varname] = true;
}
}
return $route;
}
/**
* 准备简单路由规则
*
* @param string $route_name
* @param array $rule
*
* @return array
*/
protected function _prepareSimpleRoute($route_name, array $rule)
{
static $defaults = array(
'pattern' => '',
'config' => array(),
'defaults' => array(),
);
foreach ($defaults as $key => $value)
{
if (!isset($rule[$key])) $rule[$key] = $value;
}
$route = array
(
'type' => self::ROUTE_SIMPLE,
// 路由的名字
'name' => $route_name,
// 匹配模式
'pattern' => ltrim($rule['pattern'], $this->_part_delimiter),
// 匹配模式各部分的类型
'pattern_parts' => array(),
// 变量的比对规则
'config' => $rule['config'],
// 变量在匹配模式中的位置及其名称
'vars' => array(),
// 所有已定义的变量及默认值
'varnames' => array(),
// 指示所有变量是否设置了默认值
'defaults' => array(),
// 静态文本部分
'static_parts' => array(),
// 静态部分的可选状态
'static_optional' => array(),
//如果设置了域名,则反向解析时会匹配,如果域名相同,则匹配,反之不匹配
'domain' => NULL,
);
$route['domain'] = (isset($rule['domain']) && $rule['domain'])?$rule['domain']:NULL;
// 将规则中指定了默认值的变量添加到变量列表中
foreach ($rule['defaults'] as $varname => $value)
{
$route['varnames'][$varname] = $value;
$route['defaults'][$varname] = true;
}
// 将 UDI 中的变量添加到列表
foreach ($this->_udi_parts as $varname => $value)
{
if (empty($route['varnames'][$varname]))
{
$route['varnames'][$varname] = $value;
$route['defaults'][$varname] = true;
}
}
// 将匹配模式 pattern 按照“/”进行分割
$parts = explode($this->_part_delimiter, $route['pattern']);
$use_static_optional = false;
foreach ($parts as $pos => $part)
{
if ($part{0} == $this->_var_prefix)
{
// 从分割后的组成部分中提取出变量名
$varname = substr($part, 1);
if ($use_static_optional && !isset($rule['defaults'][$varname]))
{
// LC_MSG: 无效的路由规则 "%s",因为在指定不带默认值的变量 "%s" 时,该变量左侧已经出现了可选的静态部分.
$msg = '无效的路由规则 "%s",因为在指定不带默认值的变量 "%s" 时,该变量左侧已经出现了可选的静态部分.';
//@TODO 无效的路由规则
throw new QRouter_InvalidRouteException($route_name, array($msg, $route_name, $varname));
}
if (isset($this->_udi_parts[$varname]))
{
// 如果是 UDI 保留变量,则使用默认的比对规则
$route['config'][$varname] = $this->_udi_var_regex;
}
elseif (!isset($route['config'][$varname]))
{
$route['config'][$varname] = $this->_default_var_regex;
}
// 记录下变量名及其位置
$route['vars'][$pos] = $varname;
if (!isset($route['varnames'][$varname]))
{
// 如果还未为这个变量指定默认值,则指定默认值
$route['varnames'][$varname] = '';
$route['defaults'][$varname] = false;
}
$route['pattern_parts'][$pos] = self::PARTTYPE_VAR;
}
else
{
// 如果静态部分是 [text] 这样的形式,则转换为变量
if (substr($part, 0, 1) == '[' && substr($part, -1) == ']')
{
$part = substr($part, 1, -1);
$use_static_optional = $optional = true;
}
else
{
if ($use_static_optional)
{
// LC_MSG: 无效的路由规则 "%s",因为在指定静态部分 "%s" 时,左侧已经出现了可选的静态部分.
$msg = '无效的路由规则 "%s",因为在指定静态部分 "%s" 时,左侧已经出现了可选的静态部分.';
//@TODO 无效的路由规则
throw new QRouter_InvalidRouteException($route_name, array($msg, $route_name, $part));
}
// 如果要在静态部分的首位使用 [] 符号,必须使用 \[ \] 的形式
$part = str_replace(array('\\[', '\\]'), array('[', ']'), $part);
$optional = false;
}
// 保存静态部分位置和可选状态
$route['static_parts'][$pos] = $part;
$route['static_optional'][$pos] = $optional;
if ($part != '*' && $part != '?')
{
$route['pattern_parts'][$pos] = self::PARTTYPE_STATIC;
}
else
{
$route['pattern_parts'][$pos] = self::PARTTYPE_WILDCARD;
if (isset($parts[$pos + 1]))
{
// LC_MSG: 无效的路由规则 "%s",因为通配符 "%s" 没有出现在匹配模式的最后.
$msg = '无效的路由规则 "%s",因为通配符 "%s" 只能放置在匹配模式的最后.';
//@TODO 无效的路由规则
throw new QRouter_InvalidRouteException($route_name, array($msg, $route_name, $part));
}
}
}
}
return $route;
}}class QRouter_InvalidRouteException{
function __construct($route_name,$args){
call_user_func_array('sprintf', $args);
}}