Guide: PHP- & MySQL-innføring: Kapittel 6

Ta imot data fra brukeren

For å lage dynamiske websider i PHP er man nødt til å ta imot og behandle data fra brukeren. Bare slik kan man tilpasse nettsiden til brukeren.

Regulære uttrykk

Regulære uttrykk er et svært bredt emne, og det kan ta flere år å virkelig mestre dem. Man bruker regulære uttrykk til å finne strenger som passer til et gitt mønster. Det regulære uttrykket oppgir dette mønsteret, og er rett og slett en tekst-streng med såkalte meta-tegn som kan matche ting.

PHP har støtte for to forskjellige typer regulære uttrykk. De første, de såkalte PCRE/Perl-kompatible funksjonene er de vi kommer til å bruker her i denne guiden, fordi dem som er mest utbredt i bruk, og fordi de er kraftigere. De andre regulære uttrykkene i PHP følger utvidet POSIX-standard, og er enklere, men støtter ikke så veldig avanserte teknikker.

Grunnleggende

Alle de PERL-kompatible funksjonene begynner med preg_. I dag skal vi konsentrere oss om preg_match(), som sjekker om den oppgitte strengen har minst et treff til felles med det regulære uttrykket. De andre funksjonene har nøyaktig samme syntaks for det regulære uttrykket, så når du har lært det grunnleggende i dag, kan du eksperimentere med disse.

Vi begynner med noe så enkelt som å validere et telefonnummer. Det er svært enkelt å bruke preg_match() til å godkjenne et norsk, 8-sifret telefonnummer;

<?php
$nummer
[] = "12345678";
$nummer[] = "81000100";
$nummer[] = "+44 0230 2012 9021";
$nummer[] = "12 34 56 78";

// matcher strenger som kun inneholder et 8-sifret tall
$regex "/^\d{8}$/";

foreach (
$nummer as $n)
{
    echo 
$n.": ".preg_match($regex$n)."<br/>\n";
}
?>

Vi tester mot fire forskjellige telefonnummer, alle gyldige, men noen er ikke norske. Vårt første naive forsøk matcher de norske nummerene veldig greit, men sliter med nummer som inneholder mellomrom eller et internasjonalt prefix.

12345678: 1
81000100: 1
+44 0230 2012 9021: 0
12 34 56 78: 0

Uttrykket vi bruker her; /^\d{8}$/, kan virke skremmende i begynnelsen. Uttrykket begynner og avsluttes med en skråstrek, og dette er alltid med i alle Perl-kompatible regulære uttrykk. \d står for desimal-tall, dvs. alle siffer fra 0 til 9. I klammeparenteser bakom finner du tallet 8, som oppgir hvor mange tall det må være. Hatten i begynnelsen indikerer betyr at "det neste tegnet må være helt på begynnelsen av strengen vi sammenligner mot", og dollartegnet på slutten betyr at "forrige tegn må være helt på slutten av strengen vi sammenligner med". Effekten blir at dette uttrykket kun godkjenner en streng som kun består av 8 siffer i rekkefølge.

Hva med mellomrommene?

Hva om vi skal utvide eksempelet ovenfor til å også godkjenne mellomrom? Den første tanken vil være å se på mønsteret ovenfor, og tenke at norske telefonnummer gjerne skrives i grupper på to og to siffer. Da får man følgende regulære uttrykk;

$regex = "/^(\d{2} ?){3}\d{2}$/";

Dette uttrykket godkjenner tre av numrene i eksempelet ovenfor. Dette regulære uttrykket sier at et norsk telefonummer består av grupper på to og to tall, evt. etterfulgt av et mellomrom. Spørsmålstegnet angir at tegnet foran det kan forekomme en gang eller ikke i det hele tatt. Grupper på to etterfulgt av et mellomrom kan skje tre ganger, og til slutt kommer det en gruppe på to tall. Dette regulære uttrykket godkjenner derimot ikke "810 00 100", som også er en vanlig måte å skrive norske nummer.

Hva om vi fjernet mellomrommene helt? Det er svært enkelt å gjøre med regulære uttrykk;

<?php
$nummer
[] = "12345678";
$nummer[] = "81000100";
$nummer[] = "+44 0230 2012 9021";
$nummer[] = "12 34 56 78";

// matcher strenger som kun inneholder et 8-sifret tall
$regexFjern "/\s*/";
$regexMatch "/^\d{8}$/";

foreach (
$nummer as $n)
{
    
$t preg_replace($regexFjern''$n);
    echo 
$n." (redusert til $t): ".preg_match($regexMatch$t)."<br/>\n";
}
?>

Vi bruker her funksjonen preg_replace() for å fjerne mellomrom (\s er et metategn i regulære uttrykk som betyr "alle typer mellomrom, slik som tab, enter og space"). Deretter bruker vi vår originale sammenligningsmetode som ser etter åtte sammenhengende tall. Nå godkjenner vi alle norske siffer.

Slik ser forresten resultatet ut;

12345678 (redusert til 12345678): 1
81000100 (redusert til 81000100): 1
+44 0230 2012 9021 (redusert til +44023020129021): 0
12 34 56 78 (redusert til 12345678): 1

Internasjonale nummer

Kanskje du ikke trenger å gjenkjenne internasjonale nummer, men vi forsøker å lage en slik regex likevel. Utenlandsnummer begynner med enten et plusstegn eller to nuller, etterfulgt av en landskode fra et til tre siffer. Deretter antar vi at alle land har telefonnummer som er mellom fem og tolv siffer langt. Vi legger også til noen ekstra telefonnummer å teste mot, i tillegg til et som ikke skal bli godkjent.

<?php
$nummer
[] = "12345678";
$nummer[] = "81000100";
$nummer[] = "+44 0230 2012 9021";
$nummer[] = "+1 800 234 1234";
$nummer[] = "001 800 234 1234";
$nummer[] = "12 34 56 78";
$nummer[] = "Vegard";

// matcher strenger som kun inneholder et 8-sifret tall
$regexFjern "/\s*/";
$regexMatch "/^((\+|00)(\d{1,3}))?\d{5,12}$/";

foreach (
$nummer as $n)
{
    
$t preg_replace($regexFjern''$n);
    echo 
$n." (redusert til $t): ".preg_match($regexMatch$t)."<br/>\n";
}
?>

Vi fjerner fortsatt mellomrom med det samme regulære uttrykket som tidligere, men har nå lagt inn et lengre og mer komplisert uttrykk i tillegg. Her er nedbrytingen;

((\+|00)(\d{1,3}))? sier at vi kan begynne med en sekvens som består av + eller 00, etterfulgt av et til tre siffer. \d{5,12} sier at her må det være mellom fem og tolv tall i rekkefølge. Resultatet fra skriptet blir;

12345678 (redusert til 12345678): 1
81000100 (redusert til 81000100): 1
+44 0230 2012 9021 (redusert til +44023020129021): 1
+1 800 234 1234 (redusert til +18002341234): 1
001 800 234 1234 (redusert til 0018002341234): 1
12 34 56 78 (redusert til 12345678): 1
Vegard (redusert til Vegard): 0

Her er en oppgave til deg. Utvid det regulære uttrykket over til å også godkjenne telefonnummer på formen "(47) 12 34 56 68", som er en mye brukt måte å oppgi internasjonale nummer på i utlandet. Å lage tilsvarende valideringer for e-post-adresser og URL-er er fullt mulig, om enn noe mer avansert.

Du må også være svært forsiktig når du bruker regulære uttrykk for å avvise input fra brukeren, og gjør deg selv helt sikker på at ditt regulære uttrykk godkjenner alt som kan være gyldig input.

Validere e-post

Den følgende funksjonen er en relativt robust måte å validere e-postadresser på, og har med alle de godkjente bokstavene i brukernavn og domenenavn. Den er tatt direkte fra en kommentar på PHP.net, som nok en gang viser at det lønner seg å lese PHP-manualen. Men merk også at denne funksjonen ikke godkjenner e-postadresser med nasjonale bokstaver, slik som de særnorske bokstavene, og at den derfor ikke godkjenner e-postadresser på IDN-domener.

<?
function is_email($addr
{
    
$p '/^[a-z0-9!#$%&*+-=?^_`{|}~]+';
    
$p.= '(\.[a-z0-9!#$%&*+-=?^_`{|}~]+)*';
    
$p.= '@([-a-z0-9]+\.)+([a-z]{2,3}';
    
$p.= '|info|arpa|aero|coop|name|museum)$/ix';
    return 
preg_match($p$addr);
}
?>