wiki.phpfreakz.nl
Aanmelden Artikel Overleg Bewerk Geschiedenis Go to the site toolbox

Invoer validatie

Inhoud

Invoer Valideren

Je kunt in een formulier nog zo goed aangeven wat de gebruiker in moet voeren: je weet nooit zeker of het dat al dan niet bewust fout of niet gedaan wordt.

Daarom dien je altijd te controleren of de gebruikerinvoer er wel zo uit ziet als dat je dat in je script bedoeld had. Dit artikel beoogt om daartoe wat handgrepen aan te reiken:

a) controle of het soort invoer wel is wat je bedoelt: getal / string etc.

b) een aantal (regular expressions) om te controleren of het formaat deugt.

Hoe verder?

Ik spring nu even over de functies als ctype_digit() heen om direct wat regular expressions te dumpen.

Voel je vrij om de uitleg daarvan aan te vullen. Idem voor het toevoegen of aanpassen van de regular expressions.


Type controle

Bijvoorbeeld met ctype_digit en aanverwante functies

to do....

Regular expressions

Sterk wachtwoord

Als je de lengte wilt verhogen verander dan bij "\S{6,}" de 6 met wat jij wilt. Aangeraden wordt minimaal 6 of 8 te gebruiken.

De code eist

  • minimaal een hoofdletter
  • minimaal een kleine letter
  • minimaal een cijfer
  • minimaal een ander teken dan een letter of cijfer.

Er zijn geen beperkingen ten aanzien van welk ander teken dat dan zou zijn. Een spatie of single quote kan dus ook. Dat zou afhankelijk van je toepassing tot problemen kunnen leiden. Dat zou niet het geval zijn als een md5-hash Md5 opgeslagen zou worden in de database.

/**
 * Look if the password is strong.
 * Must be at least six characters and contains an combination of alpha digits and nummbers and at least 1 other character.
 *
 * @author Sebastiaan Stok <s.stok@rollerscapes.net>
 * @author Richard JeXuS.net (Regex optimization)
 *
 * @param string $psPassword
 * @param array $paCommonWords An array of words to disallow, like username of birthday 
 * @return bool
 */
function strongPassword($psPassword, array $paCommonWords = null)
{
	if ($paCommonWords !== null)
	{
		$psCommonWords = implode('|', array_map('preg_quote', $paCommonWords));
	}
 
	/*
	 * Strong password with the following requirements.
	 * - At least 6 characters long.
	 * - At least 1 uppercase, AND at least 1 lowercase
	 * - At least 1 digit OR at least 1 alphanumeric.
	 * - No spaces
	 */
	if (!preg_match('~^(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z])(?=\D*\d)(?=[A-Za-z0-9]*[^A-Za-z0-9])\S{6,}$~', $psPassword))
	{
		return false;
	}
	// Look for common words, like for example is same as the username
	elseif ($paCommonWords !== null && preg_match('{' . $psCommonWords . '}i', $psPassword))
	{
		return false;
	}
 
	return true;
}

Is het een Nederlandse Postcode

begint met een cijfer groter dan 0 gevolgd door 3 cijfers. optioneel volgen er 1 of meer spaties. Er wordt geëindigd met een tweetal letters (hoofd- of kleine)

$bAntwoord = preg_match('#^[1-9][0-9]{3}\h*[A-Z]{2}$#i', $sInvoer);
  • voor oudere php-versies (< 5.2.4) kun je de \h ook vervangen door [ ]
  • Wil je precies 1 spatie, dan vervalt het sterretje na de \h of [ ]
  • zijn hoofdletters verplicht, laat dan de i weg na het #
  • denk ook na, of je doelgroep 100% zeker altijd uit Nederland komt, om te voorkomen dat mensen met een Belgische of Duitse postcode hun adres niet in kunnen voeren
  • Niet alle lettercobminaties zijn toegestaan, bijvoorbeeld 'SA', 'SD' en 'SS'. Deze check negeert deze eis. Tot 2005 waren ook de letters F, I, O, Q, U en Y niet ingebruik. Bij gebrek aan combinaties zijn die tegenwoordig wel in gebruik.

Is het een datum in het formaat dd-mm-yyyy?

Het eerste cijfer van de dag is 0,1,2 of 3 Het eerste cijfer van de maand is 0 of 1 Aan het jaartal kun je ook nog eisen stellen, zie onder

$bAntwoord = preg_match('^[0-3][0-9]-[0-1][0-9]-[0-9]{4}$', $sInvoer);

Merk op dat 39-19-2008 dus ook voldoet! Een controle slag met checkdate() kan dat opvangen. Zodra je weet dat het formaat inderdaad xx-xx-xxxx is, kun je splitsen op de streepjes of op de lengte en de losse onderdelen van je datum (?) aan checkdate() voeren om te horen of dat inderdaad een bestaande datum is en niet 31 feb 2001.

$bAntwoord = preg_match('^([0-3][0-9])-([0-1][0-9])-([0-9]{4})$', $sInvoer, $aM) && checkdate($aM[2], $aM[1], $aM[3]);


Varianten:

  • voorloop-nul is optioneel (1-8-2008)
$bAntwoord = preg_match('#^[0-3]?[0-9]-[0-1]?[0-9]-[0-9]{4}$#', $sInvoer);
  • jaartal begint verplicht met 19 of 20
$bAntwoord = preg_match('#^[0-3]?[0-9]-[0-1]?[0-9]-(19|20)[0-9]{2}$#', $sInvoer);

Is de tijd geldig?

Een tijd controle is eigenlijk makkelijker dan het misschien lijkt.

Een tijd bestaat uit een getal tussen 0 en 23 (al dan niet met voorloop-nul) gevolgd door een dubbele punt, waarna de minuten een 2 cijferig getal tussen 00 en 59 zijn.

Dat accepteert alle 24 uurs tijden.

$bAntwoord = preg_match('#^(?:[01]?\d|2[0-3]):[0-5]\d$#', $sInvoer);
  • ^ vanaf het begin van de string
  • [01]? er mág een 0 of 1 staan
  • \d na die 0 of 1 volgt een cijfer tussen 0 en 9
  • | in plaats van de vorige twee regels mag ook:
  • 2[0-3] het cijfer 2 gevolgd door een 0, 1, 2, of 3 (feitelijk dus de getallen 20 t/m 23
  •  : dan een dubbele punt
  • [0-5] een cijfer tussen 0 en 5
  • \d gevolgd door een cijfer tussen 0 en 9
  • $ geeft het einde van de string aan.


Voor AM/PM tijden kun je het volgende gebruiken:

$bAntwoord = preg_match('#^(?:1[0-2]|0?[1-9]):[0-5]\d\h*[aApP]\.?m\.?$#', $sInvoer);
  • ^ vanaf het begin van de string
  • 1 er moet een 1 staan
  • na die 1 volgt een cijfer tussen 0 en 2
  • | in plaats van de vorige twee regels mag ook:
  • 0? optioneel een 0
  • dan een cijfer tussen 1 en 9
  •  : dan een dubbele punt
  • [0-5] een cijfer tussen 0 en 5
  • \d gevolgd door een cijfer tussen 0 en 9
  • \h daarna een spatie of tab
  • [aApP] een 'a' of 'p'
  • \.?m\.? een optionele '.', dan een 'm' en nogmaals een optionele '.'
  • $ geeft het einde van de string aan.

Is het een geldig email adres?

hoewel op een lokale server ook adressen als "admin" of "pietje" -wat kort is voor admin@localhost of pietje@localhost- gebruikt kunnen worden, voldoen publieke adressen aan een algemeen formaat. Zo begint het met een letter of cijfer, zit er een @ in, is er een domeinnaam, gevolgd door een punt en een top level-domain. Daar tussen kunnen ook nog verschillende levels zitten. (x@phpfreakz.co.uk of y@wiki.phpfreakz.nl)

$bAntwoord = preg_match('#^[a-z0-9][a-z0-9_.\-]*@([a-z0-9]+\.)*[a-z0-9][a-z0-9\-]+\.([a-z]{2,6})$#i', $sInvoer);

beperkingen:

  • domeinnamen met letters met accenten worden niet geaccepteerd: köln.de is een geldige domeinnaam, die niet door deze check heen komt.
  • RFC specificeert ook de karakters ! # $ % & ' * + / = ? ^ ` { | } ~ als toegestaan. Deze tekens worden echter zelden gebruikt - mede omdat vele validators ze niet accepteren -. Daarnaast is het mogelijk om niet-toegestane tekens in het stuk voor de @ te plaatsen, indien dat stuk tussen "" staat. "php freakz"@phpfreakz.nl is dus valide, ondanks de spatie voor het @-teken.

Let er ook op dat momenteel een toplevel domain tussen 2 en 6 letters kan bevatten (.nl, .com, .name en .museum zijn voorbeelden.).

Oudere scripts controleren vaak slechts op {2,3} of als je geluk hebt {2,4} omdat de langere varianten op dat moment nog niet ingevoerd waren. Mocht er ooit nog een langer TLD mogelijk worden, dan dient deze check dus aangepast te worden.

Je kunt ook de redenatie volgen dat jan@department.office.com vergeet om .com toe te voegen. @department.office is een geldig adres, aangezien office uit 6 letters bestaat. Dat zou je kunnen oplossen door alle mogelijke topleveldomains van meer dan 2 tekens expliciet te noemen:

(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)

maar aangezien die lijst af en toe bijgewerkt wordt, is hij nooit uitputtend. Zo bestaat tegenwoordig ook het TLD .pro, wat ten tijde van het kopieren van bovenstaan lijstje nog niet zo was. Bovendien verdwijnen er -zelden- ook TLD's. .NATO is bijvoorbeeld opgeheven, omdat de nato de voorkeur gaf aan .nato.int.

Vraag je dus terdege af, of je dwingend wilt zijn in je controle, of dat je het bij een waarschuwing wilt laten, dat het adres mogelijk niet juist is. Uiteindelijk weet de gebruiker zelf het best hoe zijn mailadres luidt.

Tekens die in elk geval zeker niet in het adres thuis horen, zijn puntkomma's en enters. Die zouden tot misbruik van een mailform kunnen leiden!

NB: Vanaf PHP 5.2.0 beschikt PHP over enkele filterfuncties. Hiermee zou je efficiënter bovenstaande controle kunnen doen:

$bAntwoord = filter_var($sInvoer, FILTER_VALIDATE_EMAIL);

ware het niet dat die functie zo geschreven is, dat wiki@localhost ook geaccepteerd wordt als adres. Dat is ook een geldig adres, maar doorgaans wens je een controle of het een publiek bereikbare domeinnaam is. Grofweg doet de filterfunctie dus hier niet veel meer dan controleren of er een @ in de string staat en er letters en cijfers in de string zitten.

 TODO: onderstaande aanroep zou al beter zijn. Maar moet nog gecontroleerd door wiki schrijvers.
 iets als
$bAntwoord = filter_var($sInvoer, FILTER_VALIDATE_EMAIL) &&
                  preg_match('#\.[a-z]{2,6}$#i', $sInvoer);

Is het een geldig sofinummer (burger service nummer)?

Dit wordt gecontroleerd middels de "11 proef": een bepaalde berekening van de losse cijfers uit dit nummer is altijd deelbaar door 11.

/**
 * Checks whether or not the sofinummer provided passes the test of 11.
 * @version 2.0
 * @param $sofinummer The sofinummer to be checked.
 * @return boolean
 * @author Ivo Peters
 * @author Berry Langerak, al had Ivo hem als eerste goed :)
 **/
function isSofinummer( $sofinummer ) {
  $snummer = trim( $sofinummer ); 
  // lijst met nummers die qua check kloppen, maar toch niet geldig zijn
  $aInvalid = array( '111111110', 
                    '999999990', 
                    '000000000' );
  if( strlen( $snummer ) != 9 || !ctype_digit( $snummer ) || in_array( $snummer, $aInvalid ) ) {
    return false;
  }
  for( $i = 9, $som = -$snummer % 10; $i > 1; $i-- ) {
    $som += $i * $snummer{( 9 - $i )};
  }
  return ( $som % 11 == 0 );
}

Bij een sofinummer (Burger Service Nummer) is het de bedoeling dat het laatste cijfer uit de reeks wordt vermenigvuldigd met -1, in plaats van 1. Daarmee wijkt deze elf-proef dus af van bijvoorbeeld de elf-proef voor bankrekeningnummers.

Merk op dat een sofinummer met een 0 kan beginnen. Geef de parameter van isSofinummer() dus door als string, aangezien anders de 0 weg kan vallen en je met 8 in plaats van 9 tekens zit.

Is het een geldig bankrekeningnummer? (11 proef)

Voor andere cijferreeksen waar de 11 proef van toepassing is, bijvoorbeeld Nederlandse bankrekeningnummers, kun je de volgende gebruiken:

/**
 * Checks whether or not the accountnumber provided passes the test of 11.
 * @version 2.0
 * @param $sofinummer The accountnumber to be checked.
 * @return boolean
 * @author Ivo Peters
 * @author Berry Langerak
 **/
function isValidAccount( $account ) {
  $anummer = trim( $account ); 
  $invalid = array( '111111110', '999999990', '000000000', '123456789' );
  if( strlen( $anummer ) != 9 || !ctype_digit( $anummer ) || in_array( $anummer, $invalid ) ) {
    return false;
  }
  for( $i = 9, $som = 0; $i > 0; $i-- ) {
    $som += $i * $snummer{( 9 - $i )};
  }
  return ( $som % 11 == 0 );
}

Overigens lijkt het er op, dat rekeningnummers in principe uit 10 posities kunnen bestaan, maar het eerste cijfer vaak (altijd?) 0 is. In dat geval gaat de berekening nog steeds op dezelfde wijze, alleen moet dan elke 9 in bovenstaande functie vervangen worden door 10. (bron: Clieop03 documentatie van Interpay)

Geldig BTW/VAT nummer

Een BTW nummer bestaat uit een land-code gevolgd door een bepaalde reeks nummers.

Een BTW-nummer van een eenmanszaak is in Nederland gelijk aan het BSN (tip: 11 proef) van de eigenaar, voorafgegaan door NL en gevolgd door B01 (of een hoger getal). Het BTW-nummer van een andere bedrijfsvorm dan eenmanszaak (BV, vereniging etc) voldoet aan het zelfde formaat. NB: Onderstaande function controleert niet de 11-proef van een Nederlands btw-nummer

Een geldig btw nummer is: NL 1535.50.909.B02

/**
 * Look if the given input could be a legal VAT-number.
 * Accepts input with or without '.' between the numbers, must contain a County-code
 *
 * Note: This only checks the syntax, NOT IF THE VAT REALLY EXITS OR IS ACTIVE!
 *
 * @author Sebastiaan Stok <s.stok@rollerscapes.net>
 *
 * @param string $psVatInput
 * @return bool
 */
function isVAT($psVatInput)
{
	$psVatInput = trim($psVatInput);
	$psVatInput = str_replace('.', '', $psVatInput);
	$aVatMatch = array();
 
	// Common syntax
	if (!preg_match('/^([a-z]{2})[ ]*(.+)$/is', $psVatInput, $aVatMatch))
	{
		return false;
	}
 
	$aVatMatch[1] = strtoupper($aVatMatch[1]);
	$aVatRegexes	= array(
		'AT'	=> 'U[0-9]{8}',
		'BE'	=> '0[0-9]{9}',
		'BG'	=> '[0-9]{9,10}',
		'CY'	=> '[0-9]{8}[A-Za-z]',
		'CZ'	=> '[0-9]{8,10}',
		'DE'	=> '[0-9]{9}',
		'DK'	=> '[0-9]{2} ?[0-9]{2} ?[0-9]{2} ?[0-9]{2}',
		'EE'	=> '[0-9]{9}',
		'EL'	=> '[0-9]{9}',
		'ES'	=> '([A-Za-z0-9][0-9]{7}[A-Za-z0-9])',
		'FI'	=> '[0-9]{8}',
		'FR'	=> '[A-Za-z0-9]{2} ?[0-9]{9}',
		'GB'	=> '([0-9]{3} ?[0-9]{4} ?[0-9]{2}|[0-9]{3} ?[0-9]{4} ?[0-9]{2} ?[0-9]{3}|GD[0-9]{3}|HA[0-9]{3})',
		'HU'	=> '[0-8]{8}',
		'IE'	=> '[0-9][A-Za-z0-9+*][0-9]{5}[A-Za-z]',
		'IT'	=> '[0-9]{11}',
		'LT'	=> '([0-9]{9}|[0-9]{12})',
		'LU'	=> '[0-9]{8}',
		'LV'	=> '[0-9]{11}',
		'MT'	=> '[0-9]{8}',
		'NL'	=> '[0-9]{9}B[0-9]{2}',
		'PL'	=> '[0-9]{10}',
		'PT'	=> '[0-9]{9}',
		'RO'	=> '[0-9]{2,10}',
		'SE'	=> '[0-9]{12}',
		'SI'	=> '[0-9]{8}',
		'SK'	=> '[0-9]{10}',
	);
 
	if (!isset($aVatRegexes[$aVatMatch[1]]) || !preg_match('/^([a-z]{2})[ ]*'.$aVatRegexes[$aVatMatch[1]].'$/is', $psVatInput))
	{
		return false;
	}
 
	return true;
}

Geldig BTW/VAT nummer (specifieke controles)

Bovenstaande functie controleert slechts de opbouw van een BTWnummer, maar niet of het nummer nog aan aanvullende eisen voldoet.

Voor het Nederlandse BTWnummer geldt, dat het nummer tussen "NL" en "B" moet voldoen aan dezelfde 11-proef als een Burger Service Nummer (zie boven).

Voor Belgische nummers zou moeten gelden dat de eerst 7 cijfers gedeeld moeten worden door 97. Het "restgetal" afgetrokken van 97 moet gelijk zijn aan de laatste 2 cijfers.

/**
 * Check if the number might be a belgian VAT-number.
 * Accepts input with or without '.' and spaces between the numbers,
 * spaces at begin and end of string are ignored 
 * it must contain a County-code (BE) at the start (case insensitive)
 * after the country code 9 or 10 digits follow.
 * if 10 digits are given, the first must be zero
 *
 * Note: This only checks the syntax, NOT IF THE VAT REALLY EXITS OR IS ACTIVE!
 *
 * @author Ivo Peters
 *
 * @param string $psVatInput
 * @return bool
 */
function isBelgianVat($psVatInput)
{
  // remove spaces and dots
  $sVatnr = str_replace(array(' ', '.'),'',($psVatInput));
 
  if(preg_match('#^BE0?[0-9]{9}$#i', $sVatnr))
  {
   $iVatnr = str_ireplace('BE','', $sVatnr);
 
   $bResult = 97 - (floor($iVatnr / 100) % 97) == $iVatnr % 100;
  }
  else 
  {
    $bResult = false;
  }
   return $bResult;
}

Voor nummers van Groot-Britannië geldt een mengvorm van de Nederlandse en Belgische check:

de eerste 7 cijfers worden met 8, 7, ... en 2 vermenigvuldigd en het resultaat wordt opgeteld. 
97 - modulus van die som en 97 is weer gelijk aan het getal gevormd door de laatste 2 cijfers:
/**
 * Check if the number might be a GB VAT-number.
 * Accepts input with or without '.' and spaces between the numbers,
 * spaces at begin and end of string are ignored 
 * it must contain a County-code (GB) at the start (case insensitive)
 * after the country code 9 digits follow.
 *
 * Note: This only checks the syntax, NOT IF THE VAT REALLY EXITS OR IS ACTIVE!
 *
 * @author Ivo Peters
 *
 * @param string $psVatInput
 * @return bool
 */
function isGBVat($psVatInput)
{
  // remove spaces and dots
  $sVatnr = str_replace(array(' ', '.'),'',($psVatInput));
 
  if(preg_match('#^GB[0-9]{9}$#i', $sVatnr))
  {
   $sVatnr = str_ireplace('GB','', $sVatnr);
   $iSum = 0;
   for($i=0;$i<7;$i++)
   {
     $iSum += substr($sVatnr,$i,1) * (8 - $i);
     echo substr($sVatnr,$i,1) .' * '. (8 - $i) .' = '. substr($sVatnr,$i,1) * (8 - $i); 
     echo  PHP_EOL;
   }
 
   $bResult = 97 - $iSum % 97 == $sVatnr % 100;
  }
  else 
  {
    $bResult = false;
  }
   return $bResult;
}
 NB: mogelijk zijn er nog varianten mogelijk op de combinatie GB+9cijfers

Is het een IP-adres?

Een IP adres (ipv4) bestaat uit 4 delen gescheiden door een punt.

Elk deel bestaat uit een getal tussen 0 en 255, waarbij voorloop nullen toegestaan zijn.

IPv4

$bAntwoord = preg_match('#\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b#', $ip);

IPv6

$bAntwoord = preg_match('/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/', $psIPAdress);

Met de filter-functies sinds PHP 5.2.0 wordt dit:

$bAntwoord = filter_var($ip, FILTER_VALIDATE_IP);

Is het een geldig web URL?

De invoer is een geldig web-adres als het begint met http of https. Gevolgd door een hostname of IP-adres (IPv6 is ook ondersteund!) gevolgd door een optionele '/' en bestandslocatie en een optionele query-string (?iets=dit&dit=iets) en optionele anchar-point (#)

$bAntwoord = preg_match('!http[s]?://([\w\d]+([:][\w\d]+)?[@])?((([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6})|(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)|(\[((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))\]))(:[\d]+)?(/([-+_~.\d\w:]|[%][a-f\d]{2})*)*([?](&?([-+_~.\d\w]|[%][a-f\d]{2})=?)*)?([#]([-+_~.\d\w:]|[%][a-f\d]{2})*)?!is', $psAdres);

Met de filter_var functie die sinds PHP 5.2.0 beschikbaar is:

$bAntwoord = filter_var($psAdres_, FILTER_VALIDATE_IP);

nb: niet getest of er beperkingen aan deze functie zitten.

Enkele test voorbeelden:


IP-Adressen

IPv6-Adressen (werkt nog niet in (alle) webbrowsers)

  • http://[::1]
  • https://[::1]
  • http://[::1]/forum.php?forum=16&iid=1226508
  • http://gebruikersnaam@[::1]
  • http://gebruikersnaam:wachtwoord@[::1] (deze URL werkt niet in Internet Explorer)
  • http://[::1]/forum.php?forum=16&iid=1226508#5342
  • http://[::1]:80/forum.php?forum=16&iid=1226508#5342

Voorbeelden met fouten

Natuurlijk zijn meer test voorbeelden welkom!

update

per december 2009 accepteert het .eu domein ook tekens als ä en ó in een domeinnaam. Dat was al langer het geval voor enkele Europese landen tld's. In januari 2010 is daar bij gekomen dat ook aan enkele Arabische landen een top level domain in het Arabische schrift is toegekend.

Daarmee kunnen bovenstaande expressies (mogelijk) niet uit de voeten.

Is het een Nederlands telefoonnummer

Een telefoonnummer

  • begint met een 0
  • bestaat uit 10 cijfers

als je zo ver wilt opsplitsen:

  • het deel "netnummer" bestaat uit 2 tot 4 cijfer
  • het netnummer begint met een 0, het 2e cijfer is geen nul.
  • het abonneenummer bestaat uit 8 tot 6 cijfers en begint niet met een 0.

vraag je ook af of je wilt accepteren dat iemand zijn nummer met spaties er in opgeeft. Die kun je namelijk eenvoudig vervangen op het moment dat je het nummer verwerkt.

Onderstaand voorbeeld accepteert een - tussen netnummer en abonneenummer.

$bAntwoord = if(preg_match('#^0[1-9][0-9]{0,2}-?[1-9][0-9]{5,7}$#', $sInvoer);

Tekortkoming: een 4-cijferig netnummer in combinatie met een 8-cijferig abonneenummer wordt doorgelaten.

Een str_replace of preg_replace om alle niet cijfers te vervangen en vervolgens met strlen() de lengte bepalen, kan die oplossen.

Maar dat zou ook in de regular expressie mogelijk moeten zijn. NB: er ciruleren ook oplossingen die gebruik maken van een overzicht van alle bestaande netnnummers. Bedenk dat zo'n lijst aan verandering onderhevig is, en dus zo maar achterhaald kan zijn.

Is het een Nederlands mobiel nummer

Een mobiel telefoonnummer begint in Nederland doorgaans met 06 en de andere regels zoals die voor een vast telefoonnummer gelden, zijn ook van toepassing: totaal 10 cijfers en het eerste cijfer na 06 is niet 0.

Bovendien gelden er nog extra's, zoals dat de reeks 06-7(6) niet bedoeld is voor gsm gebruik, maar voor inbelnummers voor internettoegang. Ook is de reeks 06-9 momenteel(!) niet ingebruik. Maar bedenk, voordat je daarop filtert, dat tot voor kort ook de reeks 06-8 nog niet uitgegeven was. Als jouw applicatie weigert om die nummers nú te accepteren, kan een bezoeker mogelijk volgend jaar zijn geldige nummer niet in jouw formulier kwijt kan.

Is het een Bedrag ?

Een bedrag is eigenlijk een float. Echter word 5,95 niet als geldige float gezien. 5.95 daarentegen weer wel. De oplossing is simpel, de validatie echter niet. De oplossing is het in een string te zetten. De validatie zal getallen als in het lijstje hieronder moeten toelaten.

  • 5
  • 5,95
  • 5.95
  • 5,95e+5
  • 5.95E-6
  • -5,95
  • etc.

De onderstaande regex zal dit alles valideren:

$bAntwoord = preg_match('~^[+-]?(?:\d*[.,]\d+|\d+[.,]\d*|\d+)(?:[eE][+-]?\d+)?$~', $sInvoer);

Is het een geldige berekening?

Naar aanleiding van: http://www.phpfreakz.nl/forum.php?forum=1&iid=1284709

Een triviaal probleem met een absoluut niet triviale oplossing. Maakt gebruik van recursie wat mogelijk is met PCRE.

$regex = '{
	\A		# het absolute begin van de tekst
	\h*		# optionele horizontale whitespace
	(		# begin van groep 1 (deze wordt recursief aangeroepen)
		(?:
			\(		# letterlijk een (
 
			\h*
			[-+]?		# optioneel als prefix een + of -
			\h*
 
			# een getal, zoals in de wiki bij Invoervalidatie beschreven
			(?: \d* \. \d+ | \d+ \. \d* | \d+) (?: [eE] [+-]? \d+ )?
 
			(?:
				\h*
				[-+*/]		# een willekeurige operator
				\h*
				(?1)		# recursieve aanroep naar patroon 1
			)?
 
			\h*
			\)		# het sluithaakje
 
			|		# of: alleen een getal
 
			\h*
			[-+]?
			\h*
 
			(?: \d* \. \d+ | \d+ \. \d* | \d+) (?: [eE] [+-]? \d+ )?
		)
 
		# en natuurlijk de rest
		(?:
			\h*
			[-+*/]
			\h*
			(?1)
		)?
	)
	\h*
 
	\z		# het absolute einde van de string
}x';
 
$calculations = array(
	'123',
	' 123 + 123',
	' 123 + 456 * ( 789 )',
	' ( 123 + 456 ) * 789',
	'- 123',
	'123+456',
	'123+456*-789',
	'(123+456)*789'
);
 
echo '<table>';
foreach($calculations as $calculation) {
	echo '
	<tr>
		<td>', htmlspecialchars($calculation), '</td>
		<td>', (preg_match($regex, $calculation) ? 'Ja' : 'Nee'), '</td>
	</tr>';
}
 
echo '</table>';

Site Toolbox:

Persoonlijke hulpmiddelen
De laatste wijziging op deze pagina vond plaats op 24 jan 2010 21:39. - Deze pagina werd 17.746 maal bekeken. - Disclaimers - Over PFZWIKI