Nella prima parte della guida abbiamo visto come creare alcune semplici regole che sfruttano il mod_rewrite per la riscrittura degli URL.
Prima di continuare è bene approfondire un poco il concetto delle Regular Expression, che sono largamente utilizzate (per non dire che sono alla base) nel processo di riscrittura delle URL
Il discorso sulle RegEx però non è limitato al solo mod_rewrite, anche se qui vedremo come applicarlo alla riscrittura degli URL. Quindi tenete presente che per illustrare alcune caratteristiche devo un po' "esulare" dal campo di mod_rewrite.
6.1. cosa sono le RegExLe Regular Expression (espressioni regolari, o più brevemente RegEx) sono uno strumento molto potente per effettuare ricerche e sostituzioni di porzioni di testo.
Vediamo di capire bene di cosa stiamo parlando usando un esempio.
Supponiamo di avere un documento di testo molto lungo (10, 100, 1000 pagine) e di voler cercare una parola all'interno del documento. Basta aprire il programma apposito (word, blocco note, wordpad, write o quello che serve), andare nella funzione di ricerca e immettere il testo, ad esempio
la mia casa e schiacciare su cerca. Fin qui tutto facile.
Ora però supponiamo di non ricordarci se all'interno del testo si parli della mia, della sua o della loro casa. Come fare? La soluzione banale è quella di cercare prima il testo
la mia casa poi
la sua casa e poi
la loro casa fino a trovare quello desiderato.
Anche se sicuramente il risultato ottenuto è quello voluto, un lavoro del genere, magari in casi in cui le alternative non sono solo 3 ma 1000 richiederebbe per mille volte di aprire la casella di ricerca e scrivere a mano cosa cercare.
Le RegEx ci vengono in aiuto proprio in questo caso. Se il programma supporta la ricerca con RegEx possiamo specificare come chiave di ricerca una cosa del tipo
la casa dove al posto di
ci può essere qualsiasi cosa. In questo modo il programma ci evidenzierebbe
la MIA casa,
la SUA casa,
la LORO casa.
In pratica le RegEx fanno qualcosa che per gli umani è comune: fanno una specie di ricerca per "testi" simili. Sono quindi più flessibili di una normale ricerca di testo all'interno di un documento
6.2. le basi delle RegExOra vediamo come si crea una espressione regolare.
Prima di tutto tenete ben presente una cosa: una espressione regolare è una stringa di testo, all'interno della quali si trovano determinati caratteri o combinazioni di caratteri che hanno significato particolare. Ad esempio il simbolo . (punto) indica un qualsiasi carattere. Quindi se noi mettiamo in un'espressione regolare
caffet.iera il motore di ricerca ci evidenzierà parole del tipo
caffetTiera,
caffetZiera,
caffetRiera ecc ecc.
Sorge subito la domanda: e se io voglio cercare il punto come faccio? OGNI CARATTERE SPECIALE DELLE REGEX, DEVE ESSERE PRECEDUTO DAL SIMBOLO BACKSLASH (\) SE SI VUOLE CHE PERDA IL SUO SIGNIFICATO SPECIALE. Questo vuol dire che se io voglio cercare
ciao. (attenzione al punto finale) non devo specificare come regola
ciao. bensì
ciao\., questo perchè il simbolo \ nelle regex ha anch'esso un significato particolare ed è quello di dire al motore di ricerca di non considerare il significato speciale del carattere che segue.
Detto questo vediamo quali sono i caratteri speciali delle regex (almeno i più importanti)
0. i caratteri letterali non immessi in alcuna delle seguenti "combinazioni speciali" mantengono il loro valore. Quindi
ciao indica al motore di ricerca di cercare esattamente la scritta
ciao, come se non si trattasse di una regex
1. parentesi quadrate [ ]: indicano un elenco di caratteri (chiamata CLASSE DI CARATTERI).
Esempi: [abc] indica un qualsiasi carattere tra a, b, c. [abd] indica un qualsiasi carattere tra a, b, d (quindi non C perchè non rientra nell'elenco). ATTENZIONE: le RegEx sono Case Sensitive, questo vuol dire che A è diverso da a. Di conseguenza nella regex [abc] non rientrano i caratteri e,f,g... e tantomeno A,B,C perchè sono maiuscoli
2. simbolo di intervallo -: serve congiuntamente alle parentesi quadre per indicare un intervallo di caratteri. Esempio: [a-e] indica in qualsiasi carattere compreso tra a ed e (quindi a b c d e).
Combinando parentesi quadre e simbolo di intervallo possiamo creare classi di caratteri complesse come le seguenti: [a-fG-L0-7] indica di accettare solo i seguenti caratteri a b c d e f G H I H K L 0 1 2 3 4 5 6 7 (prestare attenzione alle lettere tra G ed L che sono maiuscole).
Classi importanti:
- [a-zA-Z] indica tutte le lettere dalla A alla Z sia maiuscole che minuscole
- [0-9] indica tutte le cifre, da 0 a 9
3. simbolo "qualsiasi carattere" . (punto): se in una regex specificate un punto, come visto in precedenza, al posto del punto ci potrà essere un qualsiasi carattere. Diciamo che il punto è il carattere jolly e sta a significare qualsiasi carattere.
4. simbolo di inizio riga ^: indica che la parola identificata dalla regEx non deve avere nulla prima. Quindi se specifichiamo
^cavallo questa regex identifica
cavallo e non
caciocavallo perchè la seconda ha qualcosa "prima" di cavallo.
Il simbolo ^ ha un duplice utilizzo, e serve anche per indicare quali caratteri escludere da una classe di caratteri (solo se specificato come primo carattere dopo una parentesi quadra aperta).
5. simbolo di fine riga $: indica che la parola identificata dalla regex non deve avere nulla dopo. Quindi se specifichiamo
cacio$ questa regex identifica la parola
cacio e non
caciocavallo perchè la seconda ha qualcosa "dopo" cacio
6. lo spazio \s: per indicare lo spazio si usa la combinazione \s
Per vedere altri caratteri procediamo con esempi, in modo che il tutto sia più chiaro. Vediamo una serie di RegEx e che genere di parole identificano (verrano spiegati via via i caratteri speciali inseriti)
pappa -> identifica la sola stringa pappa. Non identifica PAPPA, Pappa, PaPpA
[a-z0-9] -> identifica le seguenti stringhe di testo a0 a1 a2 b3 c7 f9. Non identifica le seguenti stringhe abc2 ciao4 bubu7
bell. -> identifica bello, bella, belle, belli, bellu, belll, bellL. bellZ, bell0, bell5, bell. In questo caso il punto, essendo un carattere speciale delle RegEx assume il valore di jolly, quindi identifica qualsiasi carattere (e di conseguenza anche se stesso).
bell\. -> identifica la sola stringa bell. Qui il punto viene privato del suo valore di jolly e quindi corrisponde solo a se stesso
[^a-f]ciao -> esclude dall'identificazione le seguenti stringhe aciao, bciao, cciao, dciao, eciao, fciao.
\[ciao\] -> identifica la stringa
[ciao] (quindi con le parentesi quadre]
6.3. backreferences Questa è la parte più interessante delle RegEx, e anche quella che rende le RegEx così efficaci.
Se ci si potesse limitare solo all'identificazione di stringhe tramite pattern non sarebbero tutto sto granch'è, invece grazie alle backreferences possiamo fare di più.
Le backreferences altro non sono che "pezzi" del pattern di identificazione, racchiusi da parentesi tonde, che vengono rese disponibili sotto forma di variabile.
Sembra complicato ma non lo è. Vediamo un esempio "extra" mod_rewrite per capire come funziona la cosa
Testo: tanto va la gatta
Regola ^({a-z]{5,5})\s([a-z]{2,2})\s([a-z]{2,2})\s([a-z]{5,5})$ $4 $3 $2 $1
Cosa combina questa regolona?
Tralasciando l'inutilità della cosa questa regola prende la stringa
tanto va la gatta e la riscrivere come
gatta la va tanto.
Analizziamo passo passo la regola per capire come funziona la cosa.
Il pattern di identificazione è quello contenuto tra il carattere ^ e il carattere $ (che come indicato in precedenza indicano l'inizio e la fine del pattern). Poi la regola è composta da diversi pezzi, ognuno racchiuso in parentesi tonde. Il primo "pezzo" che incontriamo è ({a-z]{5,5}). Questa prima parte della regola identifica una stringa di testo composta da caratteri compresi tra "a" e "z" minuscole. La stringa deve avere esattamente la lunghezza di 5 caratteri (vedere ub seguito come indicare le ripetizioni). Poi abbiamo una combinazione \s che è una combinazione speciale. Questo vuol dire il carattere spazio.
Fino a questo punto la regola identifica una stringa formata da 5 caratteri alfabetici seguiti da uno spazio.
Proseguendo troviamo di nuovo un "pezzo" contenuto tra parentesi tonde. Questa volta abbiamo una stringa di caratteri di lunghezza 2.
Quindi di nuovo uno spazio e così via fino alla fine.
Ora, per come sono fatte le espressioni regolari, ogni blocco contenuto nelle parentesi tonde si trasforma in una variabile, quindi la prima stringa di 5 caratteri identificata dal pattern ({a-z]{5,5})
viene trattata come variabile e automaticamente le viene assegnato il nome $1. A sua volta il blocco ([a-z]{2,2}) diventa la variabile $2 e così via... Ogni blocco contenuto in parentesi tonde diventa una variabile e le viene assegnato il nome $1 $2 $3 ecc ecc fino a $9.
Grazie a questo stratagemma possia sfruttare le RegEx per operazioni come quella vista sopra.
Bisogna però tener presente che all'interno del singolo pattern ci possono essere al massimo 9 "variabili".
6.4. ripetizioni Per concludere questa divagazione sulle RegEx vediamo cosa sono i codici tra parentesi tonde inseriti nella regola precedente.
Supponiamo di voler identificare tramite RegEx una parola di 5 lettere per poi trattarla come variabile sfruttando il metodo delle backreferences. Se ci fossero solo le classi di caratteri dovremmo fare una cosa del genere
([a-z][A-Z][a-z][A-Z][a-z][A-Z][a-z][A-Z][a-z][A-Z])
ovviamente questo mentodo va bene quando la parola è piuttosto breve, ma se volessimo identificare una parola di 20 lettere? Dovremmo scrivere per venti volte [a-z][A-Z], il che diventa parecchio ripetitivo e inutile.
Come avrete intuito non poteva certo mancare il modo di dire "considera questa classe di caratteri per tot volte".
Per assolvere a questo compito abbiamo diversi metodi:
il carattere * indica 0 o più ripetizioni. Quindi se scriviamo [a-z]* intendiamo dire al programma di considerare una parola formata da soli caratteri alfabetifi minuscoli la cui lunghezza è non superiore a 10 caratteri.
il carattere + indica 1 o più ripetizioni. Quindi se scriviamo [a-z]+ intendiamo dire al programma di considerare una parola formata da soli caratteri alfabetifi minuscoli la cui lunghezza è compresa tra 1 e 10.
il codice {m,M} permette invece di indicare con precisione il numero minimo (m) e massimo (M) di ripetizioni. Se scriviamo [a-z]{3,10} vogliamo solo parole composte da lettere alfabetiche minuscole di lunghezza compresa tra 3 e 10 caratteri
Nella prossima parte concludo il discorso su mod_rewrite con qualche ulteriore esempio di riscrittura degli URL e qualche nota conclusiva