2009-07-02 7 views
6

¡Hola amigos! Necesito ayuda con expresiones regulares/etc. Necesito extraer claves virtuales de url, algún tipo de enrutador en la aplicación. Estos son los parametros:enrutador Php, extracción de claves de uri

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

salida debería ser algo así:

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

Entonces, la pregunta es: ¿Cómo puedo hacer esto en php?

P.S Las combinaciones de reglas pueden variar. Entonces, las teclas principales son las palabras con el símbolo ':'. por ejemplo, como este:

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

P.S. 2: Esta es una pieza de código que tuve antes. Está funcionando, pero no es flexible.

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

De hecho, esta versión del enrutador puede ser suficiente para mí, pero estoy interesado en cómo hacer la solución flexible. Por cierto, algunas pruebas: (30 veces de 10000 operaciones):

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 

Por lo tanto, es lo suficientemente rápido. Estoy probando en una computadora portátil común. Entonces, seguro, este se puede usar en un sitio web real.

¿Alguna otra solución?

+0

necesidad de utilizar preg_split –

Respuesta

1

No creo que esto sea posible con solo una expresión regular. Zend Framework funciona como su ejemplo. Eche un vistazo a su source code.

+0

sí, vi estas rutas en los carriles –

0

Me gustaría empezar por definir algunos patrones para cada elemento

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

Entonces construir una expresión regular

$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 Puede utilizar ese patrón en sus datos de destino, y el conjunto de partidos que get debe corresponderse con los nombres de los elementos en $ map array - por ej. $ name partido [1] está en $ mapa [1], etc.

+0

pero lo que si tengo 20-30 reglas diferentes? el problema es que no puedo hacer la solución universal. –

+0

tendrías que construir un patrón para cada regla y probarlas en secuencia. De lo contrario, no creo que tengas una forma fácil de averiguar a qué corresponde cada una de las coincidencias capturadas. –

1

Prueba esta sencilla solución:

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

El único inconveniente es que no se puede tener la validación suposición datos en este punto, por lo que tiene que hacerlo en otro lugar. También podría ser complicado si las reglas entran en conflicto con la sintaxis de la expresión regular (tendrías que hacer un gran trabajo de escape aquí).

+0

al menos algo para jugar, gracias. Por cierto, publiqué mi código actual, no es flexible (solo 1 tipo de delimitadores). –

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

Eso es mucho código sin ninguna explicación o comentario.¿Puedes agregar alguna explicación? –