http://www.zaachi.com/cs/items/jednoducha-kontrola-promennych.html

Jednoduchá kontrola proměnných

Publikováno: 16.11.2008 17:50:26

Formuláře jsou prakticky nejjednodušším způsobem jak lze získat data od uživatele. S touto jednoduchostí jde ruku v ruce nebezpečí těchto dat. Uživatelům nemůžeme nikdy věřit a proto je nutné všechny tyto vstupy zabezpečit a data ověřovat. Nestačí například napsat, že v daném formulářovém inputu musí být zadán platný email, protože se vždy najde někdo, kdo se pokusí zadat libovolný řetězec, a nejen to. Špatně ošetřený vstup může vést k chybám jako SQL injection nebo XSS. Takový formulář je součástí téměř každého webu a pokud vás už přestalo bavit u každého takového formuláře neustále dokola provádět kontroly, můžete si vytvořit několik jednoduchých funkcí, které se o kontrolu budou starat. Ovšem nemusí se jednat pouze o formuláře, ale také bezpochyby o kontrolu proměnných ve skriptu. V tomto příkladu si vytvoříme třídu, v níž bude mít každá metoda na starosti kontrolu jiné hodnoty.

Kontrola délky řetězce
int check_length( mixed $var, int $min, int $max );

Začneme něčím jednodušším, a to metodou pro kontrolu délky řetězce. Vstupní hodnoty budou tří: kontrolovaný řetězec, minimální délka a nakonec maximální délka. Minimální délku bychom mohli dát jako nepovinný parametr, který by měl výchozí hodnotu 0, ale to už nechám na vás.

V metodě se nebudeme omezovat pouze pro kontrolu délky řetězce, ale bude umožňovat i kontrolu velikosti pole. Tím zabijeme dvě mouchy jednou ranou:

function check_length( $var, $min, $max ){
    if( !is_int( $min ) || !is_int( $max ) )
        return 0;
        
    switch( gettype( $var ) ){
        case 'string' : {
            return ( strlen( trim( $var ) ) 
< $min || strlen( trim( $var ) ) > $max ? 0 : 1 );
            //break;   
        }
        case 'array' : {
            //recursive count
            $count = count( $var, COUNT_RECURSIVE );
            return ( $count 
< $min || $count > $max ? 0 : 1 );
            //break;
        }
        default: return 0;   
    }  
}

Metoda vrací jedničku nebo nulu podle výsledku kontroly.

Kontrola data
int check_date( string $date [, string $splitting ]);

Další metoda na kterou se podíváme a která bude určitě taky často využívána je kontrola datumu. Datum je většinou zadáváno ve tvaru ROK-MĚSÍC-DEN. Tento vstup bude předpokládat i naše metoda a takto zadané datum bude jedním ze vstupních parametrů. Druhý parametru bude oddělovací znak, který je v datumu použit.

Metoda nejprve, pomocí funkce explode, rozdělí datum na jeho jednotlivé částí a potom kontroluje, zda se jedná o platné datum:

function check_date( $date, $splitting = '-' ){
    if( trim( $date ) == NULL )
        return 0;
    list( $year, $month, $day ) = explode( $splitting, $date );

    if( intval( $year ) == 0 || intval( $month ) == 0 || intval( $day ) == 0 )
        return 0;
    
    return ( checkdate( $month, $day, $year ) ? 1 : 0 );
}

Kontrola hesla
int check_password( strin $password_1, string $password_2 [, int $min, int $max ] );

Pokud máme hotovou metodu pro kontrolu délky řetězce, můžeme vytvořit další metodu, a to pro kontrolu hesla. Hesla jsou kontrolována dost často a všechno co většinou potřebujeme je kontrolovat maximální a minimální počet znaků, které uživatel zadal.

Metoda bude tedy velmi jednoduchá. Pro kontrolu délky využijeme metodu, která kontroluje délku řetězce, zde stačí kontrolovat pouze jedno zadání hesla, neboť potom provedeme kontrolu na shodnost, a tím pádem ošetříme případ, kdy by nebylo další zadání hesla v povoleném rozsahu:

function check_password( $pass1, $pass2, $min = 5, $max = 1000 ){
       if( self::check_length($pass1, $min, $max ) == 0 )
            return 0;

       if( $pass1 != $pass2 )
            return 0;
              
       return 1;
}

Kontrola IP adresy
int check_ip_address( string $address );

Občas, i když asi ne často, budeme potřebovat provést kontrolu IP adresy. Adresu budeme požadovat ve tvaru: 127.0.0.1 a všechny ostatní případy budou vyhodnoceny jako FALSE.

Dále budeme požadovat aby všechny 4 čísla IP adresy byly v rozmezí 0-255 a poslední podmínka bude, aby čísla které jsou zadány byli 4. Na začátku metody ošetříme ještě případ, kdy uživatel zadal na konci IP adresy znak „.“:

function check_ip_address( $ip ){
    $ip = trim( $ip);
    if( empty( $ip ) )
        return 0;
    $ip = ( substr( $ip, -1, 1 ) == '.' ? substr( $ip, 0, -1 ) : $ip );
    
    if( count( $ip = explode('.', $ip ) ) != 4 )
        return 0;
    
    foreach( $ip  as $value ){
        if( !is_numeric( $value ) || $value 
< 0 || $value > 255 )
            return 0;
    }
    return 1;
}

Kontrola URL adresy
int check_url( string $url [, int $check_domain ] );

Co dost často provádíme je kontrola URL adresy. U URL adresy je většinou nejpodstatnější kontrolovat protokol, který byl zadán, protože uživatelé často do takového pole zadají jenom nesmyslnou spleť znaků. V poli si tedy nadefinujeme, které všechny protokoly budou povolené (v tomto případě pouze http, https a ftp, ale seznam si můžete rozšířit podle sebe ). Jako jeden ze vstupních parametrů bude tedy zadaná URL adresa. Metoda nám umožní navíc i kontrolu pravosti zadané URL adresy. Tento příklad ovšem většinou nebude fungovat pod systémem windows, proto je zde tato kontrola určena druhým, nepovinným parametrem:

function check_url( $url_addres, $check_domain = 0 ){
    if( trim( $url_addres ) == NULL )
        return 0;
    $scheme = array('http', 'https', 'ftp');
    $url = parse_url( trim( $url_addres ) );
    if( in_array( $url['scheme'], $scheme) == 0 )
        return 0;
    if( $url['host'] == NULL )
        return 0;
    
    if( $check_domain == 0 )
        return 1;        
        
    if ( getmxrr ( $url['host'], $MX ) )
		return $MX;
	else 
		return 0;
}

Kontrola telefonního čísla
int check_phone( string $pnumber, [, int $length ] );

Kontrolu telefonního čísla musíme většinou provádět na základě nějaké vstupní masky (formátu v jakém je telefonní číslo přijatelné ). V našem případě bude telefon požadován ve tvaru: +xxx xxx xxx xxx. První parametr bude potom telefonní číslo. Pro jistotu si vytvoříme i druhý, nepovinný parametr, který určí maximální délku telefonního čísla.

function check_phone( $pnumber, $length = 12 ){
    //phone number format: +xxx xxx xxx xxx
    $pnumber = trim( $pnumber );
    if( substr( $pnumber, 0, 1 ) != '+') 
        return 0;

    $replace = array( ' ', '+' );
    $replacent = array('', '');
    $pnumber = str_replace( $replace, $replacent, $pnumber);
    
    if( strlen( $pnumber ) != $length ){
        return 0;   
    }
    
    return 1;
}

Navíc bychom mohli ještě překontrolovat zda je celé telefonní číslo zadáno jako číselná hodnota.

Kontrola PSČ
int check_zipcode( string $zip_code [, int $max_length ] );

Kontrola PSČ může být taky často užitečná. Psč bude moci být v našem případě zadána pouze jako číselná hodnota doplněna maximálně mezerou. Vstupní parametry jsou tedy hodnota PSC a nepovinný parametr je délka PSC bez zadaných mezer. Mezery před kontrolou délky ořežeme:

function check_zipcode( $zip_string, $length = 5 ){
    if( $zip_string == NULL || !is_int( $length ) )
        return 0;
    
    $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
    $zip_string = trim( $zip_string );    
    
    for( $i = 0; $i 
< strlen( $zip_string ); $i++ ){
        if( !in_array( $zip_string[ $i ], $chars ) && ord(" ") != ord( $zip_string[ $i ] ) )
            return 0;
    }

    if( $length == 0 )
        return 1;
    $zip_string = str_replace(' ', '', $zip_string );
    
    return ( strlen( $zip_string ) != $length ? 0 : 1 );
}

Kontrola emailu
int check_email( string $email [, int $check_domain ] );

Velmi důležitá metoda je kontrola emailu. Metoda nám umožňuje, stejně jako metoda pro kontrolu URL adresy, kontrolovat navíc doménu, ovšem tato funkce zase zřejmě nebude fungovat pod systémem Windows a proto je tato funkce opět omezena nepovinným parametrem.

function check_email( $var, $check_domain = 0 ){
    if( empty( $var ) )
        return 0;
    $return = ( !eregi("^[_a-z0-9-]+(.[_a-z0-9-]+)*@[a-z0-9-]+(.[a-z0-9-]+)*(.[a-z]{2,3})$", $var ) ? 0 : 1 );
    
    if( $check_domain == 0 )
        return $return;
        
     list ( $username, $domain ) = split ("@",$var );
		if ( getmxrr ( $domain, $MX ) )
			return $MX;
		else 
			return 0;
}

Kontrola rodného čísla
check_personal_identification_number( string $pin );

Poslední metodu pro kontrolu, kterou si vytvoříme a která může být užitečná je kontrola rodného čísla. Maska rodného čísla je už trochu složitější. Musíme zde kontrolovat všechna zadaná čísla, která se liší u muže a ženy.

Metoda má jediný vstupní parametr, kterým je právě rodné číslo, které je přesně určené:

function check_personal_idenfication_number( $number ){
    //516111/458
    $len = strlen( strval( intval( str_replace('/', '', $number ) ) ) );
    if( $len 
< 9 || $len > 10 )
        return 0;
        
    $pin['year'] = substr( $number, 0, 2);
    $pin['month'] = intval( substr( $number, 2, 2 ) );
    $pin['day'] = intval( substr( $number, 4, 2 ) );
    $pin['ending'] = substr( $number, 6, 3);
    $pin['check_number'] = substr($number, 9, 1);
    
    //year
    if( $len == 9 ){
        if( $pin['year'] > 53 )
            return 0;
        else
            $pin['year'] = 19 . $pin['year'];
    }
    else if( $len == 10 ){
        $pin['year'] = ( date('y') >= $pin['year'] ? 20 . $pin['year'] : 19 . $pin['year'] );   
    }
        
    //month
    if( $pin['month'] > 50  && $pin['month'] 
< 63 )
        $pin['month'] -= 50;
    if( $pin['month'] 
< 1 || $pin['month'] > 12 )
        return 0;
        
    //check days
    if( $pin['day'] >  cal_days_in_month(CAL_GREGORIAN, $pin['month'], $pin['year'] ) )
        return 0;

    return 1;
}

Tím máme hotovy všechny základní funkce, potřebné pro kontrolu dat.

Strip slashes

Navíc si uvedeme ještě jednu metodu, která se bude starat o ořezání magic quotes, a to podle nastavené direktivy Magic Quotes GPC v php.ini.

Metoda uvede vždy všechny vstupní pole na stejné hodnoty a to s ořezanými Magic Quotes:

function _stripslashes( & $array ){
    $magic_quotes_gpc = get_magic_quotes_gpc();
    if( is_array( $array ) ){
        while ( list ( $key, $value ) = each ( $array ) ) {
            $array[ $key ] = trim( htmlspecialchars( $magic_quotes_gpc == 1 ? stripslashes( $value ) : $value ) ); 
        }
    }
    else{
        $array = trim( htmlspecialchars( $magic_quotes_gpc == 1 ? stripslashes( $array ) : $array ) );
    }
}

Jako vstupní parametr je pole předané jako reference. Změna se tedy provádí přímo na tomto poli.

Na závěr se zase podívejme co jsme vytvořili:

class check_variable{
    function __construct(){}    
    
    function check_phone( $pnumber, $length = 12 ){
        //phone number format: +xxx xxx xxx xxx
        $pnumber = trim( $pnumber );
        if( substr( $pnumber, 0, 1 ) != '+') 
            return 0;
            
        //$replace = array( 0x20, 0x43 );
        $replace = array( ' ', '+' );
        $replacent = array('', '');
        $pnumber = str_replace( $replace, $replacent, $pnumber);
        
        if( strlen( $pnumber ) != $length ){
            return 0;   
        }
        
        return 1;
    }
        
    function _stripslashes( & $array ){
        $magic_quotes_gpc = get_magic_quotes_gpc();
        if( is_array( $array ) ){
            while ( list ( $key, $value ) = each ( $array ) ) {
                $array[ $key ] = trim( htmlspecialchars( $magic_quotes_gpc == 1 ? stripslashes( $value ) : $value ) ); 
            }
        }
        else{
            $array = trim( htmlspecialchars( $magic_quotes_gpc == 1 ? stripslashes( $array ) : $array ) );
        }
    }
    
    function check_email( $var, $check_domain = 0 ){
        if( empty( $var ) )
            return 0;
        $return = ( !eregi("^[_a-z0-9-]+(.[_a-z0-9-]+)*@[a-z0-9-]+(.[a-z0-9-]+)*(.[a-z]{2,3})$", $var ) ? 0 : 1 );
        
        if( $check_domain == 0 )
            return $return;
            
         list ( $username, $domain ) = split ("@",$var );
		    if ( getmxrr ( $domain, $MX ) )
			    return $MX;
		    else 
			    return 0;
    }
    
    function check_length( $var, $min, $max ){
        if( !is_int( $min ) || !is_int( $max ) )
            return 0;
            
        switch( gettype( $var ) ){
            case 'string' : {
                return ( strlen( trim( $var ) ) 
< $min || strlen( trim( $var ) ) > $max ? 0 : 1 );
                //break;   
            }
            case 'array' : {
                //recursive count
                $count = count( $var, COUNT_RECURSIVE );
                return ( $count 
< $min || $count > $max ? 0 : 1 );
                //break;
            }
            default: return 0;   
        }  
    } 
        
    function check_date( $date, $splitting = '-' ){
        if( trim( $date ) == NULL )
            return 0;
        list( $year, $month, $day ) = explode( $splitting, $date );

        if( intval( $year ) == 0 || intval( $month ) == 0 || intval( $day ) == 0 )
            return 0;
        
        return ( checkdate( $month, $day, $year ) ? 1 : 0 );
    }
        
    function check_url( $url_addres, $check_domain = 0 ){
        if( trim( $url_addres ) == NULL )
            return 0;
        $scheme = array('http', 'https', 'ftp');
        $url = parse_url( trim( $url_addres ) );
        if( in_array( $url['scheme'], $scheme) == 0 )
            return 0;
        if( $url['host'] == NULL )
            return 0;
        
        if( $check_domain == 0 )
            return 1;        
            
        if ( getmxrr ( $url['host'], $MX ) )
		    return $MX;
	    else 
		    return 0;
    }
        
    function check_password( $pass1, $pass2, $min = 5, $max = 1000 ){
           if( $pass1 != $pass2 )
                return 0;
           
           if( self::check_length($pass1, $min, $max ) == 0 )
                return 0;
           
           return 1;
    }
        
    function check_personal_idenfication_number( $number ){
        //516111/458
        $len = strlen( strval( intval( str_replace('/', '', $number ) ) ) );
        if( $len 
< 9 || $len > 10 )
            return 0;
            
        $pin['year'] = substr( $number, 0, 2);
        $pin['month'] = intval( substr( $number, 2, 2 ) );
        $pin['day'] = intval( substr( $number, 4, 2 ) );
        $pin['ending'] = substr( $number, 6, 3);
        $pin['check_number'] = substr($number, 9, 1);
        
        //year
        if( $len == 9 ){
            if( $pin['year'] > 53 )
                return 0;
            else
                $pin['year'] = 19 . $pin['year'];
        }
        else if( $len == 10 ){
            $pin['year'] = ( date('y') >= $pin['year'] ? 20 . $pin['year'] : 19 . $pin['year'] );   
        }
            
        //month
        if( $pin['month'] > 50  && $pin['month'] 
< 63 )
            $pin['month'] -= 50;
        if( $pin['month'] 
< 1 || $pin['month'] > 12 )
            return 0;
            
        //check days
        if( $pin['day'] >  cal_days_in_month(CAL_GREGORIAN, $pin['month'], $pin['year'] ) )
            return 0;

        return 1;
    }
        
    function check_ip_address( $ip ){
        $ip = trim( $ip);
        if( empty( $ip ) )
            return 0;
        $ip = ( substr( $ip, -1, 1 ) == '.' ? substr( $ip, 0, -1 ) : $ip );
        
        if( count( $ip = explode('.', $ip ) ) != 4 )
            return 0;
        
        foreach( $ip  as $value ){
            if( !is_numeric( $value ) || $value 
< 0 || $value > 255 )
                return 0;
        }
        return 1;
    }
        
    function check_zipcode( $zip_string, $length = 0 ){
        if( $zip_string == NULL || !is_int( $length ) )
            return 0;
        
        $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
        $zip_string = trim( $zip_string );    
        
        for( $i = 0; $i 
< strlen( $zip_string ); $i++ ){
            if( !in_array( $zip_string[ $i ], $chars ) && ord(" ") != ord( $zip_string[ $i ] ) )
                return 0;
        }

        if( $length == 0 )
            return 1;
        $zip_string = str_replace(' ', '', $zip_string );
        
        return ( strlen( $zip_string ) != $length ? 0 : 1 );
    }
}