6

嘿伙计!我需要一些关于正则表达式/等的帮助。我需要从 url 中提取虚拟键,这是应用程序中的某种路由器。以下是参数:

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 中做到这一点?

PS 规则的组合可能会有所不同。因此,主键是带有“:”符号的单词。例如像这样:

/book-:id/:category/:keyname
/book/:id_:category~:keyname

PS 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

所以,它足够快。我在普通笔记本电脑上测试。所以,可以肯定 - 这个可以在真实网站中使用。

还有其他解决方案吗?

4

5 回答 5

1

Try this simple solution:

    $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);
    }

The only downside is, you cannot have fancy data validation at this point, so you have to do it elsewhere. Also it could get messy if the rules conflict with the regex syntax (you’d have to do some heavy escaping job here).

于 2009-07-02T10:54:35.867 回答
1

我认为仅使用一个正则表达式是不可能的。Zend Framework 就像您的示例一样工作。看看他们的源代码

于 2009-07-02T10:27:23.457 回答
1

我有另一个版本的脚本:http: //blog.sosedoff.com/2009/09/20/rails-like-php-url-router/

于 2009-09-26T19:22:00.250 回答
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;
        }
    }
}
于 2011-06-25T07:57:22.933 回答
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 数组中的元素名称相对应 - 例如名称 $match[1] 在 $map[1] 等中。

于 2009-07-02T10:27:46.217 回答