2009-07-02 13 views
6

Ehi gente! Ho bisogno di aiuto con le espressioni regolari/ecc. Ho bisogno di estrarre le chiavi virtuali dall'URL, una sorta di router nell'applicazione. Ecco i params:router Php, estrazione chiavi da uri

Rule: /books/:category/:id/:keyname 
Data: /books/php/12345/this-is-a-test-keyname 

uscita dovrebbe essere qualcosa di simile:

array(
    'category' => 'php', 
    'id' => '12345', 
    'keyname' => 'this-is-a-test-keyname' 
); 

Quindi, la domanda è: come posso fare questo in php?

P.S Le combinazioni di regole possono variare. Quindi, i tasti principali sono le parole con il simbolo ':'. per esempio in questo modo:

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

P.S. 2: Questo è un pezzo di codice che avevo prima. Funziona, ma non è flessibile.

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

In effetti questa versione di router può essere abbastanza per me, ma mi interessa solo come rendere la soluzione flessibile. A proposito, alcuni test: (30 volte su 10000 operazioni):

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 

Quindi, è abbastanza veloce. Im test su laptop normale. Quindi, di sicuro - questo può essere utilizzato nel sito web reale.

Altre soluzioni?

+0

necessità di utilizzare preg_split –

risposta

1

Non penso che sia possibile con una sola espressione regolare. Zend Framework funziona esattamente come il tuo esempio. Dai un'occhiata al loro source code.

+0

Si, ho visto tali rotte a rotaie –

0

Vorrei iniziare definendo alcuni modelli per ogni elemento

$element=array(
    'id'=>'(\d+)', 
    'category'=>'([^/]+)' 
); 

Poi costruire un'espressione regolare

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

Nota È possibile utilizzare quel modello sui dati di destinazione, e la serie di partite che si tornare indietro dovrebbe corrispondere con i nomi degli elementi nella matrice $ map - es Nome $ partita [1] è in $ Mappa [1] ecc

+0

ma cosa succede se ho 20-30 regole diverse? il problema è - non posso fare la soluzione universale. –

+0

dovresti creare un modello per ogni regola e testarli in sequenza. Altrimenti, non penso che tu abbia un modo semplice per capire a cosa corrisponde ciascuna delle corrispondenze catturate. –

1

Provate questa semplice soluzione:

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

L'unico inconveniente è, non si può avere la convalida fantasia dei dati a questo punto, in modo da devi farlo altrove. Inoltre potrebbe diventare disordinato se le regole sono in conflitto con la sintassi regex (dovresti fare un grosso lavoro di escape qui).

+0

almeno qualcosa con cui giocare, grazie. btw, ho pubblicato il mio codice corrente, non è flessibile (solo 1 tipo di delimitatori). –

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; 
     } 
    } 
} 
+0

Questo è un sacco di codice senza alcuna spiegazione o commenti.Puoi aggiungere qualche spiegazione? –