Rule: /books/:category/:id/:keyname
Data: /books/php/12345/this-is-a-test-keyname
输出应该是这样的:
array(
'category' => 'php',
'id' => '12345',
'keyname' => 'this-is-a-test-keyname'
);
所以,问题是:我怎么能在PHP中这样做?
P.S规则的组合可能有所不同。因此,主键是带有':'符号的单词。例如:
/book-:id/:category/:keyname
/book/:id_:category~:keyname
P.S。 2:这是我以前的一段代码。它有效但不灵活。
function rule_process($rule, $data) {
// extract chunks
$ruleItems = explode('/',$rule);
$dataItems = explode('/',$data);
// remove empty items
array_clean(&$ruleItems);
array_clean(&$dataItems);
// rule and data supposed to have the same structure
if (count($ruleItems) == count($dataItems)) {
$result = array();
foreach($ruleItems as $ruleKey => $ruleValue) {
// check if the chunk is a key
if (preg_match('/^:[\w]{1,}$/',$ruleValue)) {
// ok, found key, adding data to result
$ruleValue = substr($ruleValue,1);
$result[$ruleValue] = $dataItems[$ruleKey];
}
}
if (count($result) > 0) return $result;
unset($result);
}
return false;
}
function array_clean($array) {
foreach($array as $key => $value) {
if (strlen($value) == 0) unset($array[$key]);
}
}
事实上这个版本的路由器对我来说已经足够了,但我只是对如何制作灵活的解决方案感兴趣。顺便说一下,一些测试:(10000次操作的30次):
TEST #0 => Time:0.689285993576, Failures: 0
TEST #1 => Time:0.684408903122, Failures: 0
TEST #2 => Time:0.683394908905, Failures: 0
TEST #3 => Time:0.68522810936, Failures: 0
TEST #4 => Time:0.681587934494, Failures: 0
TEST #5 => Time:0.681943893433, Failures: 0
TEST #6 => Time:0.683794975281, Failures: 0
TEST #7 => Time:0.683885097504, Failures: 0
TEST #8 => Time:0.684013843536, Failures: 0
TEST #9 => Time:0.684071063995, Failures: 0
TEST #10 => Time:0.685361146927, Failures: 0
TEST #11 => Time:0.68728518486, Failures: 0
TEST #12 => Time:0.688632011414, Failures: 0
TEST #13 => Time:0.688556909561, Failures: 0
TEST #14 => Time:0.688539981842, Failures: 0
TEST #15 => Time:0.689876079559, Failures: 0
TEST #16 => Time:0.689854860306, Failures: 0
TEST #17 => Time:0.68727684021, Failures: 0
TEST #18 => Time:0.686210155487, Failures: 0
TEST #19 => Time:0.687953948975, Failures: 0
TEST #20 => Time:0.687957048416, Failures: 0
TEST #21 => Time:0.686664819717, Failures: 0
TEST #22 => Time:0.686244010925, Failures: 0
TEST #23 => Time:0.686643123627, Failures: 0
TEST #24 => Time:0.685017108917, Failures: 0
TEST #25 => Time:0.686363935471, Failures: 0
TEST #26 => Time:0.687278985977, Failures: 0
TEST #27 => Time:0.688650846481, Failures: 0
TEST #28 => Time:0.688835144043, Failures: 0
TEST #29 => Time:0.68886089325, Failures: 0
所以,它足够快。我正常测试笔记本电脑。所以,当然 - 这个可以用在真实的网站上。
还有其他解决方案吗?
答案 0 :(得分:1)
我认为这只有一个正则表达式是不可能的。 Zend Framework就像你的例子一样工作。看看他们的source code。
答案 1 :(得分:1)
尝试这个简单的解决方案:
$data = Array (
"/book/:id/:category/:keyname" => "/book/12345/php/this-is-a-test-keyname",
"/book-:id/:category/:keyname" => "/book-12345/php/this-is-a-test-keyname",
"/book/:id_:category~:keyname" => "/book/12345_php~this-is-a-test-keyname",
);
foreach ($data as $rule => $uri) {
$reRule = preg_replace('/:([a-z]+)/', '(?P<\1>[^/]+)', $rule);
$reRule = str_replace('/', '\/', $reRule);
preg_match('/' . $reRule .'/', $uri, $matches);
print_r($matches);
}
唯一的缺点是,此时您无法进行花式数据验证,因此您必须在其他地方进行此操作。如果规则与正则表达式语法冲突,它可能会变得混乱(你必须在这里做一些重要的逃避工作)。
答案 2 :(得分:1)
答案 3 :(得分:0)
我首先要为每个元素定义一些模式
$element=array(
'id'=>'(\d+)',
'category'=>'([^/]+)'
);
然后建立一个正则表达式
$rule="/book-:id/:category/:keyname";
$pattern=preg_quote($rule);
$map=array();
$map[]=null;
function initrule($matches)
{
//forgive the globals - quickest way to demonstrate this, in
//production code I'd wrap this into a class...
global $element;
global $map;
//remember the order we did these replacements
$map[]=$matches[1];
//return the desired pattern
return $element[$matches[1]];
}
$pattern=preg_replace_callback('/:(\w+)/', "initrule", $pattern);
请注意,您可以在目标数据上使用该模式,并且您获得的匹配数组应与$ map数组中的元素名称相对应 - 例如name $ match [1]在$ map [1]等中。
答案 4 :(得分:0)
$Url = preg_replace("/^(.*)\\/\\/\/|(.*)\\/*.php\//i", "", $_SERVER['REQUEST_URI']);
$Url = str_replace("index.php", "", $Url);
$data = array();
$data["/ttt/:xyz/:id"] =(object) Array ( "default" => array("controller"=>"default","action"=>"detail"),
"rule" => array("xyz"=>"([a-zA-Z0-9_\+\-%]+)","id"=>"([0-9]+)"));
$data["/xid-:id"] =(object) Array ( "default" => array("controller"=>"default","action"=>"categori"),
"rule" => array("id"=>"([a-z]+)"));
function parsePath($match)
{
global $data;
foreach($data as $router)
{
foreach($router->rule as $key=>$value)
{
if($match[1] == $key){
$str ='(?P<'.$match[1].'>'.$value.')';
} else {
$str = '(?P<'.$match[1].'>[^/]+)';
}
}
}
return $str;
}
foreach($data as $path => $router)
{
$o=preg_replace_callback('/:([a-z]+)/',"parsePath", $path);
}
$regex = str_replace('/', '\/',$o);
$regex ='/^' . $regex . '$/';
preg_match($regex, $Url, $matches);
$map = array();
foreach($matches as $key => $value)
{
foreach($data as $route)
{
if(array_key_exists($key,$route->rule)){
$map['controller'] = $route->default['controller'];
$map['action']= $route->default['action'];
$map[$key] = $value;
}
}
}