Stringhe ed espressioni regolari
Termini che devi imparare
In questa sezione imparerai a verificare se due stringhe sono uguali oppure se una stringa esiste all'interno di un'altra.
Comparazione con == e ===
La via più comune per paragonare due variabili in PHP è usare l'operatore ==. Quando l'opratore testa l'equivalenza di due entità, prima riduce le entità ad un tipo comune. Questo causa conseguenze indesiderate. Per esempio il seguente codice dice che $a e $b sono uguali:
$a = 'Hello World';
$b = 0;
if($a == $b) {
	print "\$a e \$b sono uguali\n";
}
else {
	print "\$a e \$b sono diversi\n";
}
La ragione di tutto ciò è che $a è un tipo stringa e $b è un tipo intero, cosicchè il motore Zend di PHP ha necessariamente bisogno di convertirla in un intero. == è un operatore debole, infatti converte i tipi nei tipi più "facili" comunemente interi. La rappresentazione intera di 'Hello World' è 0 e così 0 = 0! == dovrebbe essere usato solo per comparare stringhe.
PHP dispone di un operatore di comparazione più forte (===). Laddove == era troppo debole, === adesso è troppo forte. Infatti questo operando necessita che i due tipi siano del tutto dello stesso tipo affinchè la comparazione possa avere un effetto aspettato. Il seguente codice dirà che $a e $b non sono uguali:
$a = 1;
$b = "1";
if( $a === $b ) {
	print "\$a e \$b sono uguali\n";
}
else {
	print "\$a e \$b sono diversi\n";
}
Questo succede perchè internamente $a e $b sono tipi diversi quindi differenti sebbene abbiano tutti e due lo stesso contenuto. Per questo motivo === potrebbe essere pericoloso quando non si sa per certezza se i tipi sono diversi
Consiglio
Puoi forzare una variabile a stringa facendo un cast di questo tipo:
if( (string)$a == (string)$b) {...}
Questo convertirà sia $a che $b in stringhe prima della conversione dell'operatore ==. Questo produrrà il risultato aspettato sebbene sia un po' strano. Usare strcmp generalmente è preferibile
strcmp & co.
Il modo migliore per comparare due variabili stringhe è quello di usare la funzione strcmp(). strcmp() prende due argomenti e li compara lessigraficamente (cioè in ordine di dizionario). strcmp() ritorna 0 se le due stringhe sono identiche. Il codice che prima ci dava dei problemi adesso darà l'output corretto:
$a = 1;
$b = "1";
if( strcmp($a,$b) == 0 ) {
	print "\$a e \$b sono uguali\n";
}
else {
	print "\$a e \$b sono diversi\n";
}
Se i due operandi non sono uguali strcmp ritornerà -1 se il primo operando appare prima del secondo operando nel vocabolario e 1 se il secondo operando appare prima del primo in un vocabolario.
strcasecmp() è identica a strcmp() fatta eccezione che strcasecmp() fa comparazioni case insensitive.
$a = 'hello';
if( strcasecmp( $a, 'HELLO' ) == 0) {
	print "\$a è uguale a HELLO\n";
}
Confrontare porzioni di stringhe
Fino ad adesso abbiamo visto come "matchare" (dall'inglese to match) stringhe esatte ma a volte (anzi molto spesso) avrai bisogno di trovare solo una parte di stringa. Quando ci riferiamo ad porzione di stringa la chiameremo sotto stringa (sub string in inglese). Una sub-string è una qualsiasi sotto stringa della stringa.
sub-string matching ad offset iniziale
Per matchare/trovare solamente la sotto stringa iniziale, PHP disponde di una famiglia di funzioni: strncmp(). strncmp() e strncasecmp() sono identiche a strcmp() e a strcasecmp() tranne per il fatto che entrambe hanno un terzo parametro, n, che ordina PHP di controllare solamente i primi n caratteri. Quindi
strncmp( 'figura1.gif', 'figura2.gif',6 )
tornerà 0 perchè le prime sei lettere degli argomenti sono uguali.
sub-string matching ad offset generico
Se, invece, devi determinare che una stringa sia o no contenuta in un'altra stringa data, possiamo usare la funzione strstr(). strstr() prende come primo argomento la stringa in cui cercare (chiamata soggetto), e come secondo argomento la stringa da cercare (chiamato pattern). Se strstr() trova la stringa , ritornerà la stringa cercata più tutta la stringa restante. Altrimenti ritornerà false.
Il seguente codice serve per determinare se la stringa $strign contiene o no la stringa 'PHP':
if( strstr( $string, 'PHP' ) !== FALSE ) {
	// fai qualcosa
}
Se vuoi cercare una sotto stringa non considerando il case puoi usare la funzione stristr()
if( stristr( $string, 'PhP' ) !== FALSE ) {
	// fai qualcosa
}
Se al posto della stringa vuoi avere la posizione del pattern all'interno della stringa allora potrai usare la funzione strpos(). La funzione strpost() lavora similarmente alla funzione strstr() tranne per due motivi: Di seguito un esempio di strpos() per trovare tutti gli offset della stringa PHP nella stringa soggetto
$offset = 0;
$match_pos = array();
while( ($offset = strpos( $string, 'PHP', $offset )) !== FALSE) {
	$match_pos = $offset++;
}
strpos() ha anche una sorella stripos() per ottenere la posizione ignorando il case.
Consiglio
Siccome il primo carattere di una stringa si trova all'offset 0, devi sempre usare === per controllare il match con strpos(). Se non sai il perchè leggi meglio questo capitolo.
Estrazione delle informazioni dalle stringhe
Quando le informazioni arrivano dall'esterno (come per esempio con i form), informazioni molto complesse sono contenute nelle nostre stringhe ed è necessario estrarle. Esempi possono essere la decomposizione dei numeri di telefono, delle emails, carte di credito ecc. Php ci offre ottime funzioni per l'estrazione di dati con formati fissati ma anche tutta la potenza delle espressioni regolari (che vedremo a breve).
sub string by offset
Per estrarre una sotto stringa dato l'offset, puoi usare la funzione substr(). substr() prende come primo argomento una stringa ( il soggetto), come secondo prende un offset che determina l'inizio della ricerca per la funzione e opzionalmente un terzo argomento che specifica la lunghezza della sotto stringa da estrarre.
Per esempio, se volessi ottenere tutta una stringa tranne il primo carattere:
$result = substr($string, 1);
o per ottenere i primi otto caratteri di una stringa
$result = substr($string, 0, 8);
Considera, adesso, il seguente codice che estrae la parte locale di una email (quella prima della @ per intenderci):
$emailLocale = substr( $email, 0 , strpos( $email, '@' ));
Se vuoi ottenere una sotto stringa partendo dalla fine, basta specificare un offset negativo:
$result = substr($email, -4);
Questo memorizzerà dentro $result gli ultimi 4 caratteri dell'email passata come soggetto.
Se hai necessità di accedere ai caratteri singoli di una stringa, puoi usare { e } per accedere agli offset desiderati come mostrato in questo esempio:
$len = strlen( $string );
for( $i = 0; $i < $len; $i++ ){
	if($i % 2) {
		$string{$i} = strtoupper( $string{$i} );
	}
}
Estrazione formattata
Se le informazioni sono troppo complesse per essere estratte semplicemente allora si possono usare le espressioni regolari. Ma se una certa tipologia di informazioni può essere rappresentata con l'uso di printf() allora puoi usare sscanf() per estrarre informazioni.
Per esempio, per estrapolare i dati da un ip con porta (Es.: 127.0.0.1:6137) puoi usare il formato "%d.%d.%d.%d:%d". Si può usare con sscanf() come segue:
$parti = sscanf( $string, "%d.%d.%d.%d:%d");
Se $string è uguale a 127.0.0.1:6137 allora $parti sarà costruita così:
Array {
	[0] => 127
	[1] => 0
	[2] => 0
	[3] => 1
	[4] => 6137
}
Il pattern di sscanf() deve essere assolutamente identico per funzionare. In effetti questo è un modo un po' fragile di eseguire una estrapolazione dei dati.
Modificare le stringhe
In questa sezione, imparerai a modificare le stringhe sostituendole con altre sia specificando un offset dal quale fare la sostituzione sia sostituendo semplicemente all'interno della stringa.
Modificare sotto stringhe dato l'offset
Per rimpiazzare una sotto stringa in una stringa soggetto, useremo la funzione substr_replace(). Il suo primo argomento è il soggetto, il secondo è la stringa rimpiazzo, il terzo argomento è l'offset di partenza e, opzionale, quarto argomento lunghezza per il rimpiazzo. Per meglio illustrare consideriamo il seguente esempio che sostituisce tutte le cifre di una carta di credito, tranne le ultime 4, con delle X.
$len = strlen( $ccNum );
$newnum = substr_replace( $ccNum, str_repeat( 'X', $len-4), 0, $len-4);
Primo, troviamo la lunghezza della carta di credito in questione, e poi sostituiamo le prime $len - 4 caratteri con un numero uguale di X.
Sostituire sotto stringhe
Un altro task comune è quello di sostituire semplicemente tutte le occorrenze di una stringa all'interno di un soggetto. La funzione migliore per fare ciò è str_replace(). str_replace() prende come primo argomente il pattern da ricercare e sostituire, come secondo ciò che va sostituito e come terzo la stringa soggetto in cui deve essere effettuato lo scambio. Prendiamo come esempio una funzione che sostituisce :) con un link ad una immagine (come viene fatto nel mio guestbook ;) )
$new_subject = str_replace( ':)' , '<img src = "/smiley.jpg" />', $soggetto);
Spesso avrai bisogno di fare sostituzioni case insensitive: potrai usare str_ireplace().
Sia str_replace() che str_ireplace accettano anche degli array per tutti i loro parametri. Se i primi due sono array, allora tutte le sostituzioni verranno fatte in una sola chiamata. Se il terzo argomento è un array allora le sostituzioni verranno effettuate su tutti i valori dell'array.
$emoticons = array(	':)' => '<img src = "/smiley.jpg" />',
			';)' => '<img src = "/wink.jpg" />',
			':(' => '<img src = "/sad.jpg" />');
$new_subject = str_replace( array_keys($emoticons), array_values($emoticons), $soggetto );
Espressioni regolari
Le funzioni più potenti per la manipolazione di stringhe sono le espressioni regolari. Le espressioni regolari hanno un robusto linguaggio per specificare pattern per l'estrazione o la sostituzione di parti di testo.
Esistono due tipi di espressioni regolari: PCRE e POSIX. PCRE significa Perl Compatible Regular Expression e vengono chiamate così perchè sono compatibili con gli script fatti in perl.Le espressioni regolari POSIX supportano, invece, lo standard POSIX sulle espressioni regolari. Le funzioni POSIX per le espressioni regolari (famiglia ereg_ e split()) sono più lenti rispetto alle PCRE e di solito il loro uso non è incoraggiato.
PCRE: sintassi di base
Un pattern per una espressione regolare consiste in una stringa di testo con meta caratteri. Questi metacaratteri definiscono il tipo e il numero di caratteri che possono combaciare con un particolare pattern.
Il set base di metacaratteri è la classe di caratteri riassunta nella tabella seguente
MetacaratteriCorrispettivo (Match)
\dnumeri\digit [0-9]
\DTutto tranne numeri\digit
\wQualsiasi carattere alfanumerico o underscore (_)
\WQualsiasi carattere che non sia alfanumerico o underscore
\sQualsiasi whitespace (spazio, tab, newline ecc.)
\SQualsiasi NON whitespace
.Qualsiasi carattere tranne il newline
Questo set di metacaratteri fa il match con solo un carattere. Affinchè questi pattern siano utili dovremo avere la possibilità di enumerarli. Le PCRE supportano questa possibilità. Di seguito un riassunto:
MetacaratteriCorrispettivo (Match)
?Occorre 0 o 1 volta
*Occorre 0 o più volte
+Occorre 1 o più volte
{n}Occorre n volte
{,n}Occorre al più n volte
{m,}Occorre al meno m volte
{m,n}Occorre tra m ed n volte
Mettendo questi elementi tutti insieme si possono formare numerosi pattern come per esempio un CAP americano formato da XXXXX-XXXX
if(preg_match( "/\d{5}-\d{4}/", $soggetto )) {
	//Trovato un CAP americano
}
Il primo argomento è il pattern di ricerca mentre il secondo è il soggetto su cui effettuare la ricerca. Da notare che il pattern dell'espressione regolare è incluso tra due slash (/).
A differenza di sscanf(), il match di preg_ vale in tutto il soggetto ovunque il pattern si trovi. Se vuoi che il pattern valga solo all'inizio allora dovrai utilizzare ^. Potrai trovare la fine con $. Quindi, se vuoi verificare che il soggetto sia solo ed esclusivamente un Codice di avviamento postale americano il codice di prima diventa:
if(preg_match( "^/\d{5}-\d{4}$/", $soggetto )) {
	//Trovato un CAP americano
}
Se vuoi che la ricerca dia esito positivo, però, solamente perchè il CAP inizi con alcuni numeri allora potrai aggiungere un selezionatore come
[2-9]
per dire che i cap validi sono quelli che iniziano con un numero che va da 2 a 9.
Estrarre informazioni con le espressioni regolari
Di solito dovrai fare di più che verificare l'esistenza di un determinato pattern in una stringa. A volte dovrai estrarre il MATCH dalla stringa per poterlo elaborare, memorizzare o verificare. Se vuoi catturare dei pattern dovrai semplicemente includerli in delle parentesi tonde:
/(\d{5})-(\d{4})/
Dopo aver specificato ciò che si vuole catturare basterà passare alla funzione preg un array al fine di memorizzare i pattern. L'ordine viene considerato contando i pattern da sinistra verso destra. Esempio:
$string = "Il mio ZIP code è 21797-2046";
if(preg_match( "/(\d{5})-(\d{4})/", $string, $array )) {
	print_r( $array );
}
L'output sarà
Array {
	[0] => 21797-2046
	[1] => 21797
	[2] => 2046
}
Consiglio
preg_match controlla solamente la prima occorrenza del pattern nella stringa. Se vuoi che il match venga eseguito più volte nella stringa dovrai eseguire preg_match_all
Sostituire pattern con espressioni regolari
Le espressioni regolari ti permettono di sostituire il pattern con qualsiasi cosa tu voglia. Fare sostituzioni con le espressioni regolari è come con str_replace() tranne per il fatto che con le espressioni regolari la sostituzione non avviene con una string prefissata ma con il pattern stesso.
Per effettuare una sostituzione di un pattern devi usare preg_replace(). Il suo primo argomento è una espressione regolare che dovrebbe individuare il pattern all'interno della stringa. Il secondo argomento è ciò che deve andare al posto del pattern che può essere una stringa letterale o un argomento del pattern stesso individuato da \n (con n numero del pattern individuato). Il terzo argomento è ovviamente il soggetto.
$soggetonuovo = preg_replace("/(\S+)@(\S+).(\S+)/", '\1 at \2 dot \3', $soggetto);
Questo codice convertirà una stringa tipo demenziale@altervista.org in demenziale at altervista dot org.
Divisione di stringa in componenti
PHP offre 3 funzioni per la divisione di stringhe in parti: explode(), split() e preg_split(). explode() è la più semplice delle tre. Permette di dividere una stringa con un delimitatore statico. Esempio:
$info = array();
$file = file( "/etc/passwd" );
foreach( $file as $linea ) {
	$info[] = explode( ":", $linea );
}
Non avendo espressioni regolari al suo interno, explode() è la più veloce delle tre. Se possibile dovresti preferirla rispetto a preg_split() o a split().
split() è una espressione regolare di POSIX, più lenta di preg_split().
preg_split() attraverso le espressioni regolari è molto potente e, per esempio, permette di dividere in base ai whitespaces, una qualsiasi stringa in questo semplice modo:
$parti = preg_replace( "/\s+/", $soggetto );
P H P

D E V E L O P M E N T