2013-01-31 12 views
6

Napisałem już kontroler PHP, ale przepisuję mój kod, więc mogę mieć trasy JSON typu routera szkieletowego, które odwzorowują wzorce URI na kombinacje PHP Class :: method, lub bezpośrednio do dokumentów HTML, które są następnie dostarczane do klienta, tak jak poniżej:Rdzeń podobny do routera z dynamicznym regexem w PHP

{ 
    "/home" : "index.html", 
    "/podcasts": "podcasts.html", 
    "/podcasts/:param1/:param2": "SomeClass::someMethod" 
} 

Backbone dynamicznie tworzy regexes dopasować trasy przed adresów URL. Wziąłem zajrzeć do kodu szkieletowej, a ja ekstrakcji następujący kod (to nieco zmodyfikowany):

function _routeToRegExp (route) { 
    var optionalParam = /\((.*?)\)/g; 
    var namedParam = /(\(\?)?:\w+/g; 
    var splatParam = /\*\w+/g; 
    var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; 
    route = route.replace(escapeRegExp, '\\$&') 
       .replace(optionalParam, '(?:$1)?') 
       .replace(namedParam, function(match, optional){ 
       return optional ? match : '([^\/]+)'; 
       }) 
       .replace(splatParam, '(.*?)'); 
    return new RegExp('^' + route + '$'); 
} 

Kiedy mijam trasy jak /podcasts/:param1/:param2, do powyższego kodu, otrzymuję /^\/podcasts\/([^\/]+)\/([^\/]+)$/. Próbowałem napisać funkcję PHP, aby uzyskać dokładnie to samo wyrażenie regularne. Próbowałem:

$route = '/podcasts/:param1/:param2'; 
$a = preg_replace('/[\-{}\[\]+?.,\\\^$|#\s]/', '\\$&', $route); // escapeRegExp 
$b = preg_replace('/\((.*?)\)/', '(?:$1)?', $a);    // optionalParam 
$c = preg_replace('/(\(\?)?:\w+/', '([^\/]+)', $b);    // namedParam 
$d = preg_replace('/\*\w+/', '(.*?)', $c);      // splatParam 
$pattern = "/^{$d}$/"; 
echo "/^\/podcasts\/([^\/]+)\/([^\/]+)$/\n"; 
echo "{$pattern}\n"; 
$matches = array(); 
preg_match_all($pattern, '/podcasts/param1/param2', $matches); 
print_r($matches); 

moje wyjście jest:

/^\/podcasts\/([^\/]+)\/([^\/]+)$/ // The expected pattern 
/^/podcasts/([^\/]+)/([^\/]+)$/  // echo "{$pattern}\n"; 
Array         // print_r($matches); 
(
) 

Dlaczego wyjście regex inaczej? Potrafię sobie poradzić z resztą procesu mapowania i wszystkim, ale nie wiem, jak uzyskać dokładnie to samo wyrażenie w PHP, co w JavaScript. Jakieś sugestie?

+0

To było zbyt frustrujące, więc kolega pomógł mi napisać bardzo wygodną funkcję nodeJs, która zwraca to wyrażenie regularne za pośrednictwem interfejsu CLI. Jeśli znajdę sposób na zrobienie tego w PHP, na pewno opublikuję poprawną odpowiedź. :) –

+1

Łatwe rozwiązanie: użyj ~ do wytyczenia wyrażenia regularnego - tj. ~ Wzorca ~ zamiast/wzorca /. W ten sposób nie musisz uciekać od/znaków. Ułatwia to również czytanie wyrażenia regularnego. – ajshort

Odpowiedz

2

JS wersja nie w rzeczywistości uciec/znaki, wytwarza taką samą reprezentację ciąg jako PHP wersji /podcasts/([^/]+)/([^/]+) ale i to jest chyba to, co cię zadziałał, o console.log z regexp zwróconej przez _routeToRegExp pokazuje pełną reprezentację z ograniczająca/i wewnętrzne z nich uciekł:

//Firefox output 
RegExp /^\/podcasts\/([^\/]+)\/([^\/]+)$/ 

Zobacz http://jsfiddle.net/w6zP2/ na demo.

Co zauważył @ayshort w komentarzach i co robi szkielet jest tym samym rozwiązaniem: pomiń/escaping i użyj alternatywnej składni; Backbone robi to z jawne wywołanie konstruktora RegExp z new RegExp('^' + route + '$'), można zrobić coś podobnego z $pattern = "~^{$d}$~";

oraz PHP Fiddle http://phpfiddle.org/main/code/4de-jf3 że wydaje się działać zgodnie z oczekiwaniami.

Należy zauważyć, że w JavaScript $& w ciągu zastępowania wstawia dopasowany podciąg , nie tak w PHP . EscapeRegExp powinny być zmienione i może wyglądać następująco:

$a = preg_replace('/[\-{}\[\]+?.,\\\^$|#~\s]/', '\\\\$0', $route); 

co daje nam zaktualizowany kod:

$a = preg_replace('/[\-{}\[\]+?.,\\\^$|#~\s]/', '\\\\$0', $route); // escapeRegExp 
$b = preg_replace('/\((.*?)\)/', '(?:$1)?', $a);    // optionalParam 
$c = preg_replace('/(\(\?)?:\w+/', '([^\/]+)', $b);    // namedParam 
$d = preg_replace('/\*\w+/', '(.*?)', $c);      // splatParam 
$pattern = "~^{$d}$~"; 

echo "/^\/podcasts\/([^\/]+)\/([^\/]+)$/\n"; 
echo "{$pattern}\n"; 
$matches = array(); 
preg_match_all($pattern, '/podcasts/param1/param2', $matches); 
print_r($matches); 

Zobacz Określanie ciąg jako parametr w string.replace
Patrz zamiennik w preg_replace

+0

Próbowałem tego przykładu phpfiddle i działało idealnie! Wielkie dzięki, @nikoshr. Będę pracował nad kodem i opublikuję pełny przykład, gdy tylko skończę z kilkoma dalszymi testami i wszystkim. W każdym razie praca z funkcją nodeJs była dość niewygodna! : D –

+0

@ Óscar Palacios Cieszę się, że pomogło :) – nikoshr