Validarea datelor cu o clasa PHP

Peste tot pe internet poţi găsi tot felul de tutoriale/scripturi care îţi arată cum sa faci o clasă PHP care sa valideze datele venite de la utilizatori. De aceea m-am gândit că poate ar trebui să încerc şi eu să fac un astfel de tutorial/script. (Ultimul update: 05.11.2014) Te rog ca, orice întrebare, nelămurire sau propunere de schimbare a scriptului să o pui într-un comentariu. Nu există întrebări stupide, toate ascund în spatele lor un sâmbure de adevăr. De aceea, nu te sfii să îmi scrii un comentariu.

Filosofia din spatele acestui script

Am dorit să realizez o clasă care să permită validarea oricăror tipuri de date, indiferent că acestea vin dintr-un URL (date de tip GET), dintr-un formular (date de tip POST) ori pur şi simplu dintr-o variabilă.

Cineva ar putea spune că m-am întins mai mult decât îmi permite plapuma, dar m-am gândit că doar prin aşa ceva aş putea să aduc puţin mai mult decât celelalte tutoriale/scripturi.

De asemenea, aş dori ca acest script să fie oarecum “modular”, cu alte cuvinte să conţină doar elementele de care site-ul are nevoie în validare. Spre exemplu, de ce să oferi scriptului o validare pentru CNP, dacă nu ai nevoie de aşa ceva? De aceea, dacă vei folosi scriptul rezultat, îţi vei putea alege singur “modulele” necesare validări, eliminând aşadar pericolul de a face un script prea mare. Pe parcursul tutorialului/scriptului, vei vedea ce vreau să spun.

Trebuie să menţionez că acest tutorial/script este o entitate “vie”, adică ar putea suferi modificări pe parcurs, în funcţie de eventualele comentarii pe care le-ar putea primi.

Aşadar, să trecem la treabă.

Cum va funcţiona validarea

Presupunând că avem un script final (Validation.php), validarea va funcţiona astfel:

Întâi va trebui să ne asigurăm că avem clasa inclusă (nu voi sta aici să discut despre metodele de includere a clasei).

<?php
include('Validation.php');
?>

Apoi, vom instanţia clasa pentru a-i putea folosi metodele. Poate că nu vom folosi un constructor pentru această clasă, dar este bine să fim pregătiţi:

<?php
$validation = new Validation();
?>

Acum, după instanţierea clasei, va trebui să spunem ce câmpuri dorim să validăm şi ce reguli de validare va avea fiecare câmp.

Ţinând cont că dorim ca acest script să valideze orice tip de date, fie ele de tip GET, de tip POST sau variabile, forma de adăugare a unui câmp va avea modelul:

$validation->field(‘nume_variabila’, ‘sursa_variabila_sau_valoare’, ‘regula1[argumente_optionale]<*mesaj de eroare optional*>|regula2[argumente_optionale]<*mesaj de eroare optional*>’,’cum_va_fi_afisat_campul_in_caz_de_eroare’);

unde:

  • ‘nume_variabila’ reprezinta felul cum se numeşte variabila. Astfel, dacă avem ceva de genul <input type=”text” name=”username” />, numele variabilei va fi ‘username’.
  • ‘sursa_variabila_sau_valoare’ reprezintă sursa de unde vine variabila. Astfel, dacă avem o variabilă venită dintr-un URL: http://siteultau.ro/index.php?page=1, vom putea transmite câmpul ‘page’, punând drept sursă ‘_GET’. În cazul câmpurilor de tip POST, vom pune drept sursă ‘_POST’. În cazul câmpurilor care sunt de fapt variabile putem pune direct valoarea. Astfel, dacă avem o variabila $text, nume_variabila va fi ‘text’, iar sursa_variabila_sau_valoare va fi $text.
  • ‘regula1’ şi ‘regula2’ reprezintă denumirea regulilor ce vor fi aplicate câmpurilor. Astfel, dacă dorim ca unui câmp să-i fie eliminate spaţiile goale din stânga şi din dreapta şi să fie un câmp obligatoriu, vom scrie ceva de genul: ‘trim|required’. Unora dintre aceste reguli li se vor putea ataşa argumente suplimentare (între paranteze pătrate []) şi un mesaj de eroare care să fie diferit de mesajul originar (mesajul urmând să fie scris între <* şi *>)

Aşadar, pentru variabile de tip POST vom avea:

<?php
$validation->field('username','_POST','trim|required|alpha');
$validation->field('email','_POST','trim|required|email');
?>

Pentru variabile de tip GET singura diferenţă se va regăsi la _POST, acolo având de fapt _GET:

<?php
$validation->field('username','_GET','trim|required|alpha');
?>

Pentru variabile simple vom avea:

<?php
$number = 345607808954;
$validation->field('number',$number,'trim|integer';
?>

După declararea câmpurilor, ne rămâne validarea şi eventuala afişare a erorilor

Astfel, vom avea metoda validate() care va returna true în cazul în care validarea s-a încheiat fără erori şi false în cazul în care unele câmpuri nu au respectat regulile de validare:

<?php
if($validation->validate())
{
  echo 'Validare OK';
}
else
{
  echo 'Validarea s-a incheiat cu erori';
}
?>

Erorile de validare vor putea fi preluate prin metoda errors(), care va returna erorile intr-un array:

<?php
$errors = $validation->errors();
?>

În cazul în care vrem să afişăm erorile unui anumit câmp, le putem accesa sub formă de array, oferind ca parametru al metodei numele variabilei:

<?php
$validation->errors('username');
?>

Accesarea valorilor câmpurilor o putem face folosindu-ne de metoda get_value(‘nume_camp’).

Acestei metode i-am ataşat mai mulţi parametri opţionali, care să ne permită administrarea mai uşoară a valorilor. Astfel, metoda arată aşa:

$validation->get_value(‘nume_camp’,’aplicare_filter_sanitize_string’,’tipul_in_care_returnez_valoarea’))

unde:

  • ‘nume_camp’ este numele câmpului.
  • ‘aplicare_filter_sanitize_string’ (default este TRUE) este o setare care aplica automat sanitize string.
  • ‘tipul_in_care_returnez_valoarea’ (default este ‘single’) este încercarea mea de validare a unor câmpuri ce pot avea mai multe valori (cum este în cazul elementelor de tip select multiple). Astfel, în mod normal se va returna valoarea, dar în cazul câmpurilor ce pot avea mai multe valori, putem seta ca tip de returnare ‘array’.

Pe lângă metodele prezentate mai sus am mai realizat încă una, care ne va permite să ataşăm mesaje suplimentare de eroare în cazul în care mai facem validări ce nu ţin neapărat de datele primite (ca de exemplu, după ce am primit datele dorim să verificăm în baza de date informaţii şi, în cazul în care nu ne plac rezultatele, sa adăugăm un mesaj de eroare care să atenţioneze utilizatorul în această privinţă).

Metoda se numeşte append_error() şi are forma următoare:

$validation->append_error(‘username’, ‘exists’, ‘Username-ul există deja în baza de date’);

În exemplul de mai sus am adăugat o eroare prin care atenţionez utilizatorul asupra unei erori, folosindu-mă de câmpul ‘username’ căruia îi ataşez un identificator de eroare ce poartă numele ‘exists’ cu un mesaj aferent.

Scriptul de bază

Voi scrie mai jos scriptul de bază şi voi explica liniile:

<?php
/**
* Class Validation
*/
class Validation
{
/**
* @var array
*/
private $_error_messages = array();

Dorim să păstrăm într-un singur loc mesajele de eroare. Am putea la fel de bine să definim toate erorile aici, dar am ales sa adaug elemente array-ului la fiecare metodă de validare în parte.

/**
* @var array
*/
private $_errors = array();

Trebuie să păstrăm de asemenea toate erorile într-un loc.

/**
* @var array
*/
private $_error_container = array(
'start'=> '<div class="error">',
'end' =>'</div>');

Erorile vor fi afişate în conainere HTML, în acest caz în cadrul elementului <div class=”error”></div>

/**
* @var array
*/
protected $_fields = array();

În $_fields vom păstra câmpurile ce vor fi oferite clasei Validation

/**
* @param $start
* @param $end
*/
public function set_error_container($start, $end)
{
$this->_error_container['start'] = $start;
$this->_error_container['end'] = $end;
}

Vom permite utilizatorului să îşi definească singur containerele pentru erori acesta folosindu-se de metoda set_error_container(). Exemplu: $validation->set_error_container(‘<li class=”error”>’,'</li>’);

/**
 * @param $variable_name
 * @param $type_val
 * @param $rules
 * @param string $element_name
 */
public function field($variable_name, $type_val, $rules, $element_name='')
{
    if(empty($element_name)) $element_name = $variable_name;
    if($type_val=='_GET' && isset($_GET[$variable_name]))
    {
        $get_value = $_GET[$variable_name];
        unset($_GET[$variable_name]);
    }
    elseif($type_val=='_POST' && isset($_POST[$variable_name]))
    {
        $get_value = $_POST[$variable_name];
        unset($_POST[$variable_name]);
    }
    elseif(!empty($type_val) && $type_val!='_GET' && $type_val!='_POST')
    {
        $get_value = $type_val;
        unset($type_val);
    }
    else
    {
        $get_value = '';
    }
    $get_rules = $this->_get_rules($rules);
    is_array($get_value) ? $value = $get_value : $value[] = $get_value;
    $this->_fields[$variable_name] = array('element_name'=>$element_name,'value'=>$value,'rules'=>$get_rules);
}

Metoda field() va permite adăugarea de câmpuri în array-ul $_fields. În funcţie de parametrii oferiţi, metoda va ştii cum să acceseze valorile date câmpurilor.

/**
 * 
 * @param string $rules
 * @return array $rules
 */
private function _get_rules($rules)
{
    $rules_arr = explode('|',$rules);
    $return_rules = array();
    foreach($rules_arr as $rule)
    {
        $args = '';
        if(strpos($rule,'['))
        {
            $args = substr($rule, strpos($rule,'['), strpos($rule,']'));
            $rule = str_replace($args,'',$rule);
            $args = str_replace(array('[',']'),'',$args);
        }
        $custom_message = '';
        if(strpos($rule,'<*'))
        {
            $custom_message = substr($rule, strpos($rule,'<*'), strpos($rule,'*>'));
            $rule = str_replace($custom_message,'',$rule);
            $custom_message = str_replace(array('<*','*>'),'',$custom_message);
        }
        $return_rules[$rule] = array('args'=>$args,'custom_message'=>$custom_message);
    }
    return $return_rules;
}

Metoda _get_rules() este apelată de metoda field(), rostul acesteia fiind acela de a adăuga regulile scrise sub formă de string într-un array care se va adăuga la fiecare câmp din $_fields având drept cheie string-ul ‘rules’.

/**
 *
 */
public function validate()
{
    if(empty($this->_fields))
    {
        echo 'Nothing to validate!';
        return FALSE;
    }
    foreach($this->_fields as $key => $field)
    {
        foreach($field['rules'] as $rule=>$params)
        {
            if(!isset($this->_errors[$key]['required']))
            {
                if(method_exists($this, '_validate_'.$rule))
                {
                    foreach($this->_fields[$key]['value'] as $subkey=>$value)
                    {
                        $this->{'_validate_'.$rule}($key,$subkey,$params);
                    }
                }
                else
                {
                    echo 'Validation rule '.$rule.' does not exist.';
                }
            }
        }
    }
    if(empty($this->_errors))
    {
        return true;
    }
    else
    {
        return false;
    }
}

Metoda validate() pur şi simplu ia fiecare câmp în parte şi îi aplică metoda specifică fiecărei reguli de validare, returnând false în cazul în care cel puţin o regulă nu a fost întrunită sau true dacă toate regulile au fost respectate. După cum poţi vedea, în funcţie de regulă, numele metodei care se va ocupa de regulă va avea formatul _validate_regula($denumire_camp, $valoare_initiala, $alti_parametri).

/**
 * @param $key
 * @param bool $escaped
 * @return mixed
 */
public function get_value($key, $escaped = TRUE, $return_type = 'single')
{
    if(!empty($this->_fields) && isset($this->_fields[$key]['value']))
    {
        $value = $this->_fields[$key]['value'];
        $rules = array_keys($this->_fields[$key]['rules']);
    }
    if(isset($value))
    {
        if($escaped)
        {
            foreach($value as $key=>$escape_value)
            {
                if(in_array('email',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_EMAIL);
                }
                elseif(in_array('float',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_NUMBER_FLOAT);
                }
                elseif(in_array('integer',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_NUMBER_INT);
                }
                elseif(in_array('url',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_URL);
                }
                else
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_STRING);
                }
            }
        }
        switch ($return_type)
        {
            case 'single':
                return $value[0];
                break;

            case 'array':
            default:
                return $value;
                break;
        }
    }
    else
    {
        switch ($return_type)
        {
            case 'single':
                return '';
                break;

            case 'array':
            default:
                return array();
                break;
        }
    }
}

Metoda get_value() returnează valoarea câmpului cerut.

/**
 * @param null $key
 * @param null $container_start
 * @param null $container_end
 * @return bool|string
 */
public function errors($key = NULL,$container_start = NULL, $container_end = NULL)
{
    if(isset($container_start) && isset($container_end))
    {
        set_error_container($container_start, $container_end);
    }
    $errors = array();
    if(!isset($key))
    {
        foreach($this->_errors as $variable)
        {
            foreach($variable as $error)
            {
                $errors[] = $error;
            }
        }
    }
    else
    {
        if(array_key_exists($key, $this->_errors))
        {
            $errors = $this->_errors[$key];
        }
    }
    if(isset($errors))
    {
        $returned = '';
        foreach($errors as $error)
        {
            $returned .= $this->_error_container['start'];
            $returned .= $error;
            $returned .= $this->_error_container['end'];
        }
        return $returned;
    }
    else
    {
        return true;
    }
}

Metoda errors() returnează erorarea/erorile de validare în cadrul containerelor HTML.

/**
 * @param $key
 * @param $error
 */
private function _write_error($key,$rule_name,$params)
{
    if(isset($this->_fields[$key]['rules'][$rule_name]['custom_message']) && !empty($this->_fields[$key]['rules'][$rule_name]['custom_message']))
    {
        $error_message = $this->_fields[$key]['rules'][$rule_name]['custom_message'];            
    }
    else
    {
        $error_message = vsprintf($this->_error_messages[$rule_name],$params);
    }
    $this->_errors[$key][$rule_name] = $error_message;
}

Metoda _write_error() va fi folosită de către metodele vare validează valoarea câmpurilor pentru a scrie mesajele de eroare.

/**
 * @param $key
 * @param $error
 */
public function append_error($key,$error_id = 'appended', $error_mess = 'There was another error.')
{
    $this->_errors[$key][$error_id] = $error_mess;
}

Metoda append_error() va permite adăugarea de erori ce nu au neapărată legătură cu validarea ci vor fi scrise din afara clasei de către aplicaţie.

Regulile de validare

Acum sa trecem la regulile de validare

TRIM – Scurtare extremităţi

Exemple utilizare:

$validation->field('username','_POST','trim');
$validation->field('username','_POST','trim[aeiou]');

Cod:

/**
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_trim($key,$subkey,$params)
{
    if(strlen($params['args'])==0)
    {
        $params['args']=" \t\n\r\0\x0B";
    }
    $this->_fields[$key]['value'][$subkey] = trim($this->_fields[$key]['value'][$subkey],$params['args']);
    return true;
}

Trim nu este la rigoare o regulă de validare, ci este o etapă în validarea unui câmp. Astfel, dacă consideri că este neapărat necesar ca înainte de validare (sau, cine ştie, poate după validare) să elimini spaţiile sau orice alte caractere de la extremităţi, este o idee bună să faci asta folosindu-te de regula ‘trim’. Ai putea întreba atunci “de ce ai pus în denumirea metodei sintagma validate dacă nu se face o validare?”. Am făcut acest lucru pentru a permite artificiul pe care l-am folosit la metoda validate(), acela de a chema metodele de validare fără prea multă bătaie de cap.

REQUIRED – Câmp obligatoriu

Exemple utilizare:

$validation->field('username','_POST','required');

Cod:

/**
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_required($key,$subkey,$params)
{
    $this->_error_messages['required'] = 'Campul %s este obligatoriu';
    if(strlen($this->_fields[$key]['value'][$subkey])==0)
    {
        $this->_write_error($key, 'required', array($this->_fields[$key]['element_name']));
    }
    return true;
}

Prima regulă de validare, pe formatul căreia vor apărea şi celelalte, este ‘required’. Astfel, dacă printre reguli va apărea ‘required’ metoda validate() va chema metoda _validate_required(), oferindu-i acestei metode parametrii necesari pentru identificarea câmpului ce urmează a fi validat, dar şi eventuali parametri suplimentari ($args). În cazul _validate_required() nu avem parametri suplimentari, dar nu ştim cum va evolua clasa de validare, aşa ca vom păstra un format ce va permite ulterior adăugarea de parametri.

Trebuie văzut aici faptul că nu am făcut întâi un trim, permiţând astfel ca câmpurile să poată avea şi spaţii goale ca valoare. Astfel, un câmp care va fi “required” poate avea ca valoare ‘ ‘. Este important de menţionat aici că, dacă dorim să ne asigurăm că se introduce o valoare, trebuie ca la regulile câmpului să facem întâi un ‘trim’ şi apoi un ‘required’: $validation->field(‘username’,’_POST’,’trim|required’);. Dacă am face invers: ‘required|trim’, atunci un câmp la care s-ar pune spaţii goale ar trece de examenul validării. Asta s-ar întâmpla pentru că întâi ar avea loc regula required, care ar fi considerat validă valoarea ‘ ‘, pentru ca apoi să se facă un trim care ar duce la o valoare vidă (care, în condiţiile acestea, nu ar mai fi respectat regula required).

LENGTH – Lungime string

Exemple utilizare:

$validation->field('username','_POST','length[6]'); // numar exact de caractere: 6
$validation->field('username','_POST','length[-6]'); // numar de caractere: intre 0 si 6 caractere, inclusiv 0 si inclusiv 6 caractere
$validation->field('username','_POST','length[3-13]'); // numar de caractere: intre 3 si 13 caractere inclusiv 3 si inclusiv 13 caractere
$validation->field('username','_POST','length[6+]'); // numar de caractere: mai mult de 6 caractere si nu 6 caractere

Cod:

/**
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_length($key,$subkey,$params)
{
    $args = $params['args'];
    if(strlen($args)==0) $args = '0';
    $this->_error_messages['str_between'] = 'Campul %s trebuie sa contina intre %s si %s caractere';
    $this->_error_messages['str_morethan'] = 'Campul %s trebuie sa contina mai mult de %s caractere';
    $this->_error_messages['str_exact'] = 'Campul %s trebuie sa contina exact %s caractere';
    if(strpos($args,'-')!==FALSE)
    {
        $type = 'between';
        $args_arr = explode('-',$this->_fields[$key]['rules']['length']);
        if(empty($args_arr[0]))
        {
            $args_arr[0]='0';
        }
    }
    elseif(substr($this->_fields[$key]['rules']['length'],-1)=='+')
    {
        $value = intval(rtrim($this->_fields[$key]['rules']['length'],'+'));
        $type = 'morethan';
    }
    else
    {
        $value = intval($this->_fields[$key]['rules']['length']);
        $type = 'exact';
    }
    $element_value_size = strlen($this->_fields[$key]['value'][$subkey]);
    switch($type)
    {
        // if the value<required value or value>required value then error
        case 'between':
            if($element_value_size<$args_arr[0] || $element_value_size>$args_arr[1])
            {
                $this->_write_error($key, 'str_between', array($this->_fields[$key]['element_name'], $args_arr[0],$args_arr[1]));
            }
            break;
        // if value<=required value then error
        case 'morethan':
            if($element_value_size<=$value)
            {
                $this->_write_error($key, 'str_morethan', array($this->_fields[$key]['element_name'], $value));
            }
            break;
        // if value does not equal required value then error
        case 'exact':
            if($element_value_size!=$value)
            {
                $this->_write_error($key, 'str_exact', array($this->_fields[$key]['element_name'], $value));
            }
            break;
    }
    return true;
}

Length ne permite sa stabilim o lungime de caractere pe care o putem accepta ca regula de validare pentru stringuri.

ALPHA – Caractere alfabetice (si nu numai)

Exemple utilizate:

$validation->field('username'),'_POST','alpha'); // doar caractere alfabetice
$validation->field('username'),'_POST','alpha[._-]'); // caractere alfabetice si/sau semne de punctuatie punct, linie de unire, cratima
$validation->field('firstname','_POST','alpha[- ]'); // caractere alfabetice, cratima sau spatiu

Cod:

/**
 * _validate_alpha ($key,$subkey,$params)
 * Validates alphabetical characters, also allowing the characters that are mentioned in $args
 *  
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_alpha($key,$subkey,$params)
{
    $this->_error_messages['alpha'] = 'Campul %s trebuie sa contina doar litere';
    $allowed_chars = array();
    if(strlen($params['args'])>0)
    {
        $allowed_chars = str_split($params['args']);
    }
    $validated = true;
    $value = str_split($this->_fields[$key]['value'][$subkey]);
    foreach($value as $char)
    {
        if(!ctype_alpha($char) && !in_array($char, $allowed_chars))
        {
            $validated = false;
        }
    }
    if($validated===false)
    {
        $this->_write_error($key, 'alpha', array($this->_fields[$key]['element_name']));
    }
    return true;
}

Aceasta validare ar fi putut fi simplă, totul putându-se termina după un…

ctype_alpha=$this->_fields[$key]['value'][$subkey];

Cu toate acestea, mereu mi-am dat seama că sunt momente când vrei sa accepţi doar caractere alfabetice pentru un anumit input, dar in acelasi timp ai dori sa ai in conţinutul stringului introdus de utilizatori şi caractere precum “-” sau “_” etc.

De aceea, acest validate acceptă ca parametri suplimentari caractere ce nu sunt neapărat litere. În cazul în care în string-ul analizat există altceva decât ce i s-a permis să existe, funcţia va returna un mesaj de eroare.

ALPHANUMERIC – Caractere alfanumerice (şi nu numai)

Exemple utilizate:

$validation->field('username'),'_POST','alphanumeric'); // doar caractere alfanumerice
$validation->field('username'),'_POST','alphanumeric[._-]'); // caractere alfanumerice si/sau semne de punctuatie punct, linie de unire, cratima
$validation->field('firstname','_POST','alphanumeric[- ]'); // caractere alfanumerice, cratima sau spatiu

Cod:

/**
 * _validate_alphanumeric ($key,$subkey,$params)
 * Validates alphanumeric characters, also allowing the characters that are mentioned in $args
 *  
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_alphanumeric($key,$subkey,$params)
{
    $this->_error_messages['alphanumeric'] = 'Campul %s trebuie sa contina doar litere si/sau cifre';
    $allowed_chars = array();
    if(strlen($params['args'])>0)
    {
        $allowed_chars = str_split($params['args']);
    }
    $validated = true;
    $value = str_split($this->_fields[$key]['value'][$subkey]);
    foreach($value as $char)
    {
        if(!ctype_alnum($char) && !in_array($char, $allowed_chars))
        {
            $validated = false;
        }
    }
    if($validated===false)
    {
        $this->_write_error($key, 'alphanumeric', array($this->_fields[$key]['element_name']));
    }
    return true;
}

Regula de validare alphanumeric funcţionează pe acelaşi principiu ca regula alpha, aceasta permiţând însă by default, pe lângă caracterele alfabetice, şi caractere numerice.

NUMERIC – Caractere numerice (şi nu numai)

Exemple utilizate:

$validation->field('number'),'_POST','numeric'); // doar caractere numerice
$validation->field('phone'),'_POST','numeric[. -]'); // caractere numerice si/sau semne de punctuatie punct, cratima, spatiu
$validation->field('mobile','_POST','numeric[.- ]<*Nu ai numar de mobil?*>'); // caractere numerice, cratima,punct sau spatiu, impreuna cu un mesaj personalizat de eroare

Cod:

/**
 * _validate_numeric ($key,$subkey,$params)
 * Validates numeric characters
 *  
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_numeric($key,$subkey,$params)
{
    $this->_error_messages['numeric'] = 'Campul %s trebuie sa contina doar cifre';
    $allowed_chars = array();
    if(strlen($params['args'])>0)
    {
        $allowed_chars = str_split($params['args']);
    }
    $validated = true;
    $value = str_split($this->_fields[$key]['value'][$subkey]);
    foreach($value as $char)
    {
        if(!ctype_digit($char) && !in_array($char, $allowed_chars))
        {
            $validated = false;
        }
    }
    if($validated===false)
    {
        $this->_write_error($key, 'numeric', array($this->_fields[$key]['element_name']));
    }
    return true;
}

Regula de validare numeric funcţionează pe acelaşi principiu ca regulile alpha şi alphanumeric.

INTEGER – Numere întregi (şi nu numai)

Regulile INTEGER şi FLOAT pot primi ca parametri limite de comparaţie. Cu alte cuvinte în regula de validare poţi stabili ca numerele acceptate să nu fie mai mari, mai mici sau să fie egale cu limitele dorite de utilizator. Astfel, numărul poate fi negativ sau pozitiv, în funcţie de dorinţele utilizatorului stabilite în cadrul parametrilor. Vezi exemplele de mai jos:

Exemple utilizate:

$validation->field('apples'),'_POST','integer[<=50]'); // doar un numar intreg care poate fi mai mic sau egal cu 50 
$validation->field('apples'),'_POST','integer[>=5&<98]'); // doar un numar intreg care poate fi mai mare sau egal cu 5 si mai mic decat 98
$validation->field('apples','_POST','integer[=41]<*Nu accept decat 41*>'); // doar un numar intreg egal cu 41. in cazul nerespectarii regulii, se ofera mesaj de eroare personalizat

Cod:

/**
 * Validate integers, whether we talk about negative or positive numbers. Also, if arguments provided, it verifies if the value is lesser, equal, or bigger than limits
 * @param $key
 * @param $subkey
 * @return bool
 */
private function _validate_integer($key,$subkey,$params)
{
    $this->_error_messages['integer'] = 'Campul %s trebuie sa fie un numar intreg';
    if(!filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_INT)) {
        $this->_write_error($key,'integer', array($this->_fields[$key]['element_name']));
    }

    if(strlen($params['args'])>0)
    {
        $this->_compare_values($key, $subkey, $params['args']);
    }
    return true;
}

FLOAT – Numere cu virgulă mobilă (şi nu numai)

La fel ca în cazul INTEGER, regula FLOAT poate primi ca parametri limite de comparaţie. Cu alte cuvinte în regula de validare poţi stabili ca numerele acceptate să nu fie mai mari, mai mici sau să fie egale cu limitele dorite de utilizator. Astfel, numărul poate fi negativ sau pozitiv, în funcţie de dorinţele utilizatorului stabilite în cadrul parametrilor. Vezi exemplele de mai jos:

Exemple utilizate:

$validation->field('price'),'_POST','float[<=5.12]'); // doar un numar float care poate fi mai mic sau egal cu 5.12 
$validation->field('price'),'_POST','integer[>=5.12&<9.8]'); // doar un numar float care poate fi mai mare sau egal cu 5.12 si mai mic decat 9.8
$validation->field('price','_POST','integer[=4.19]<*Nu accept decat 4.19?*>'); // doar un numar float egal cu 4.19. in cazul nerespectarii regulii, se ofera mesaj de eroare personalizat

Cod:

/**
 * Validate floats, whether we talk about negative or positive numbers. Also, if arguments provided, it verifies if the value is lesser, equal, or bigger than limits
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_float($key,$subkey,$params)
{
    $this->_error_messages['float'] = 'Campul %s trebuie sa fie un numar cu virgula mobila';
    if(!filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_FLOAT)) {
        $this->_write_error($key,'float', array($this->_fields[$key]['element_name']));
    }

    if(strlen($params['args'])>0)
    {
        $this->_compare_values($key, $subkey, $params['args']);
    }
    return true;
}

Bineînţeles (după cum poate că şi vezi) cele două reguli, INTEGER şi FLOAT se bazează pe o funcţie numită _compare_values(), regulă pe care ţi-o indic mai jos:

/**
 * Compare values to assert if one value is bigger, equal or smaller than another value.
 * @param type $key
 * @param type $subkey
 * @param type $args
 * @return boolean
 */
private function _compare_values($key,$subkey,$args)
{
    $this->_error_messages['not_lesser_than'] = 'Campul %s trebuie sa contina o valoare mai mica decat %s';
    $this->_error_messages['not_lesser_equal_than'] = 'Campul %s trebuie sa contina o valoare mai mica sau egala cu %s';
    $this->_error_messages['not_equal_with'] = 'Campul %s trebuie sa contina o valoare egala cu %s';
    $this->_error_messages['not_bigger_equal_than'] = 'Campul %s trebuie sa contina o valoare mai mare sau egala cu %s';
    $this->_error_messages['not_bigger_than'] = 'Campul %s trebuie sa contina o valoare mai mare decat %s';
    $args_arr = explode('&', $args);
    $value = $this->_fields[$key]['value'][$subkey];
    foreach($args_arr as $arg)
    {
        preg_match_all('/^(<|<=|=|>=|>)([0-9]+)(\.([0-9]+))*/',$arg,$matches);
        $to_compare_with = $matches[2][0];
        $operator = $matches[1][0];
        if(($operator=='<') && !($value<$to_compare_with))
        {
            $this->_write_error($key, 'not_lesser_than', array($this->_fields[$key]['element_name'], $to_compare_with));
        }
        if(($operator=='<=') && !($value<=$to_compare_with))
        {
            $this->_write_error($key, 'not_lesser_equal_than', array($this->_fields[$key]['element_name'], $to_compare_with));
        }
        if(($operator=='=') && ($value!=$to_compare_with))
        {
            $this->_write_error($key, 'not_equal_with', array($this->_fields[$key]['element_name'], $to_compare_with));
        }
        if(($operator=='>=') && !($value>=$to_compare_with))
        {
            $this->_write_error($key, 'not_bigger_equal_than', array($this->_fields[$key]['element_name'], $to_compare_with));
        }
        if(($operator=='>') && !($value>$to_compare_with))
        {
            $this->_write_error($key, 'not_bigger_than', array($this->_fields[$key]['element_name'], $to_compare_with));
        }
    }
    return true;
}

Pe lângă aceste reguli, mai există câteva care cred ca nu mai merită explicaţii, acestea neacceptând parametri suplimentari ci doar mesaje de eroare personalizate:

EMAIL

URL

IP

BOOLEAN

Versiune finală

Mai jos vei găsi codul clasei în totalitatea sa. Cu toate acestea, cel puţin pentru moment, te sfătuiesc să analizezi cu grijă maximă tot ce găseşti mai jos deoarece codul de mai jos nu a fost verificat decât pănă la metoda _validate_required():

<?php

/**
 * Class Validation
 */
class Validation
{
    /**
     * @var array
     */
    private $_error_messages = array();
    /**
     * @var array
     */
    private $_errors = array();
    /**
     * @var array
     */
    private $_error_container = array(
        'start'=> '<div class="error">',
        'end' =>'</div>');
    /**
     * @var array
     */
    protected $_fields = array();

    /**
     * @param $start
     * @param $end
     */
    public function set_error_container($start, $end)
    {
        $this->_error_container['start'] = $start;
        $this->_error_container['end'] = $end;
    }

    /**
     * @param $variable_name
     * @param $type_val
     * @param $rules
     * @param string $element_name
     */
    public function field($variable_name, $type_val, $rules, $element_name='')
    {
        if(empty($element_name)) $element_name = $variable_name;
        if($type_val=='_GET' && isset($_GET[$variable_name]))
        {
            $get_value = $_GET[$variable_name];
            unset($_GET[$variable_name]);
        }
        elseif($type_val=='_POST' && isset($_POST[$variable_name]))
        {
            $get_value = $_POST[$variable_name];
            unset($_POST[$variable_name]);
        }
        elseif(!empty($type_val) && $type_val!='_GET' && $type_val!='_POST')
        {
            $get_value = $type_val;
            unset($type_val);
        }
        else
        {
            $get_value = '';
        }
        $get_rules = $this->_get_rules($rules);
        is_array($get_value) ? $value = $get_value : $value[] = $get_value;
        $this->_fields[$variable_name] = array('element_name'=>$element_name,'value'=>$value,'rules'=>$get_rules);
    }
    
    /**
     * 
     * @param string $rules
     * @return array $rules
     */
    private function _get_rules($rules)
    {
        $rules_arr = explode('|',$rules);
        $return_rules = array();
        foreach($rules_arr as $rule)
        {
            $args = '';
            if(strpos($rule,'['))
            {
                $args = substr($rule, strpos($rule,'['), strpos($rule,']'));
                $rule = str_replace($args,'',$rule);
                $args = str_replace(array('[',']'),'',$args);
            }
            $custom_message = '';
            if(strpos($rule,'<*'))
            {
                $custom_message = substr($rule, strpos($rule,'<*'), strpos($rule,'*>'));
                $rule = str_replace($custom_message,'',$rule);
                $custom_message = str_replace(array('<*','*>'),'',$custom_message);
            }
            $return_rules[$rule] = array('args'=>$args,'custom_message'=>$custom_message);
        }
        return $return_rules;
    }

    /**
     *
     */
    public function validate()
    {
        if(empty($this->_fields))
        {
            echo 'Nothing to validate!';
            return FALSE;
        }
        foreach($this->_fields as $key => $field)
        {
            foreach($field['rules'] as $rule=>$params)
            {
                if(!isset($this->_errors[$key]['required']))
                {
                    if(method_exists($this, '_validate_'.$rule))
                    {
                        foreach($this->_fields[$key]['value'] as $subkey=>$value)
                        {
                            $this->{'_validate_'.$rule}($key,$subkey,$params);
                        }
                    }
                    else
                    {
                        echo 'Validation rule '.$rule.' does not exist.';
                    }
                }
            }
        }
        if(empty($this->_errors))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
/**
 * @param $key
 * @param bool $escaped
 * @return mixed
 */
public function get_value($key, $escaped = TRUE, $return_type = 'single')
{
    if(!empty($this->_fields) && isset($this->_fields[$key]['value']))
    {
        $value = $this->_fields[$key]['value'];
        $rules = array_keys($this->_fields[$key]['rules']);
    }
    if(isset($value))
    {
        if($escaped)
        {
            foreach($value as $key=>$escape_value)
            {
                if(in_array('email',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_EMAIL);
                }
                elseif(in_array('float',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_NUMBER_FLOAT);
                }
                elseif(in_array('integer',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_NUMBER_INT);
                }
                elseif(in_array('url',$rules))
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_URL);
                }
                else
                {
                    $value[$key] = filter_var($escape_value, FILTER_SANITIZE_STRING);
                }
            }
        }
        switch ($return_type)
        {
            case 'single':
                return $value[0];
                break;

            case 'array':
            default:
                return $value;
                break;
        }
    }
    else
    {
        switch ($return_type)
        {
            case 'single':
                return '';
                break;

            case 'array':
            default:
                return array();
                break;
        }
    }
}

    /**
     * @param null $key
     * @param null $container_start
     * @param null $container_end
     * @return bool|string
     */
    public function errors($key = NULL,$container_start = NULL, $container_end = NULL)
    {
        if(isset($container_start) && isset($container_end))
        {
            set_error_container($container_start, $container_end);
        }
        $errors = array();
        if(!isset($key))
        {
            foreach($this->_errors as $variable)
            {
                foreach($variable as $error)
                {
                    $errors[] = $error;
                }
            }
        }
        else
        {
            if(array_key_exists($key, $this->_errors))
            {
                $errors = $this->_errors[$key];
            }
        }
        if(isset($errors))
        {
            $returned = '';
            foreach($errors as $error)
            {
                $returned .= $this->_error_container['start'];
                $returned .= $error;
                $returned .= $this->_error_container['end'];
            }
            return $returned;
        }
        else
        {
            return true;
        }
    }
    
    /**
     * @param $key
     * @param $error
     */
    private function _write_error($key,$rule_name,$params)
    {
        if(isset($this->_fields[$key]['rules'][$rule_name]['custom_message']) && !empty($this->_fields[$key]['rules'][$rule_name]['custom_message']))
        {
            $error_message = $this->_fields[$key]['rules'][$rule_name]['custom_message'];            
        }
        else
        {
            $error_message = vsprintf($this->_error_messages[$rule_name],$params);
        }
        $this->_errors[$key][$rule_name] = $error_message;
    }
    
    /**
     * @param $key
     * @param $error
     */
    public function append_error($key,$error_id = 'appended', $error_mess = 'There was another error.')
    {
        $this->_errors[$key][$error_id] = $error_mess;
    }

    /**
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_trim($key,$subkey,$params)
    {
        if(strlen($params['args'])==0)
        {
            $params['args']=" \t\n\r\0\x0B";
        }
        $this->_fields[$key]['value'][$subkey] = trim($this->_fields[$key]['value'][$subkey],$params['args']);
        return true;
    }

/**
 * @param $key
 * @param $subkey
 * @param $params
 * @return bool
 */
private function _validate_required($key,$subkey,$params)
{
    $this->_error_messages['required'] = 'Campul %s este obligatoriu';
    if(strlen($this->_fields[$key]['value'][$subkey])==0)
    {
        $this->_write_error($key, 'required', array($this->_fields[$key]['element_name']));
    }
    return true;
}

    /**
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_length($key,$subkey,$params)
    {
        $args = $params['args'];
        if(strlen($args)==0) $args = '0';
        $this->_error_messages['str_between'] = 'Campul %s trebuie sa contina intre %s si %s caractere';
        $this->_error_messages['str_morethan'] = 'Campul %s trebuie sa contina mai mult de %s caractere';
        $this->_error_messages['str_exact'] = 'Campul %s trebuie sa contina exact %s caractere';
        if(strpos($args,'-')!==FALSE)
        {
            $type = 'between';
            $args_arr = explode('-',$this->_fields[$key]['rules']['length']);
            if(empty($args_arr[0]))
            {
                $args_arr[0]='0';
            }
        }
        elseif(substr($this->_fields[$key]['rules']['length'],-1)=='+')
        {
            $value = intval(rtrim($this->_fields[$key]['rules']['length'],'+'));
            $type = 'morethan';
        }
        else
        {
            $value = intval($this->_fields[$key]['rules']['length']);
            $type = 'exact';
        }
        $element_value_size = strlen($this->_fields[$key]['value'][$subkey]);
        switch($type)
        {
            // if the value<required value or value>required value then error
            case 'between':
                if($element_value_size<$args_arr[0] || $element_value_size>$args_arr[1])
                {
                    $this->_write_error($key, 'str_between', array($this->_fields[$key]['element_name'], $args_arr[0],$args_arr[1]));
                }
                break;
            // if value<=required value then error
            case 'morethan':
                if($element_value_size<=$value)
                {
                    $this->_write_error($key, 'str_morethan', array($this->_fields[$key]['element_name'], $value));
                }
                break;
            // if value does not equal required value then error
            case 'exact':
                if($element_value_size!=$value)
                {
                    $this->_write_error($key, 'str_exact', array($this->_fields[$key]['element_name'], $value));
                }
                break;
        }
        return true;
    }
    
    
    /**
     * _validate_alpha ($key,$subkey,$params)
     * Validates alphabetical characters, also allowing the characters that are mentioned in $args
     *  
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_alpha($key,$subkey,$params)
    {
        $this->_error_messages['alpha'] = 'Campul %s trebuie sa contina doar litere';
        $allowed_chars = array();
        if(strlen($params['args'])>0)
        {
            $allowed_chars = str_split($params['args']);
        }
        $validated = true;
        $value = str_split($this->_fields[$key]['value'][$subkey]);
        foreach($value as $char)
        {
            if(!ctype_alpha($char) && !in_array($char, $allowed_chars))
            {
                $validated = false;
            }
        }
        if($validated===false)
        {
            $this->_write_error($key, 'alpha', array($this->_fields[$key]['element_name']));
        }
        return true;
    }
    
    /**
     * _validate_alphanumeric ($key,$subkey,$params)
     * Validates alphanumeric characters, also allowing the characters that are mentioned in $args
     *  
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_alphanumeric($key,$subkey,$params)
    {
        $this->_error_messages['alphanumeric'] = 'Campul %s trebuie sa contina doar litere si/sau cifre';
        $allowed_chars = array();
        if(strlen($params['args'])>0)
        {
            $allowed_chars = str_split($params['args']);
        }
        $validated = true;
        $value = str_split($this->_fields[$key]['value'][$subkey]);
        foreach($value as $char)
        {
            if(!ctype_alnum($char) && !in_array($char, $allowed_chars))
            {
                $validated = false;
            }
        }
        if($validated===false)
        {
            $this->_write_error($key, 'alphanumeric', array($this->_fields[$key]['element_name']));
        }
        return true;
    }
    
    /**
     * _validate_numeric ($key,$subkey,$params)
     * Validates numeric characters
     *  
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_numeric($key,$subkey,$params)
    {
        $this->_error_messages['numeric'] = 'Campul %s trebuie sa contina doar cifre';
        $allowed_chars = array();
        if(strlen($params['args'])>0)
        {
            $allowed_chars = str_split($params['args']);
        }
        $validated = true;
        $value = str_split($this->_fields[$key]['value'][$subkey]);
        foreach($value as $char)
        {
            if(!ctype_digit($char) && !in_array($char, $allowed_chars))
            {
                $validated = false;
            }
        }
        if($validated===false)
        {
            $this->_write_error($key, 'numeric', array($this->_fields[$key]['element_name']));
        }
        return true;
    }

    /**
     * Validate integers, whether we talk about negative or positive numbers. Also, if arguments provided, it verifies if the value is lesser, equal, or bigger than limits
     * @param $key
     * @param $subkey
     * @return bool
     */
    private function _validate_integer($key,$subkey,$params)
    {
        $this->_error_messages['integer'] = 'Campul %s trebuie sa fie un numar intreg';
        if(!filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_INT)) {
            $this->_write_error($key,'integer', array($this->_fields[$key]['element_name']));
        }
        
        if(strlen($params['args'])>0)
        {
            $this->_compare_values($key, $subkey, $params['args']);
        }
        return true;
    }
    
    /**
     * Validate floats, whether we talk about negative or positive numbers. Also, if arguments provided, it verifies if the value is lesser, equal, or bigger than limits
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_float($key,$subkey,$params)
    {
        $this->_error_messages['float'] = 'Campul %s trebuie sa fie un numar cu virgula mobila';
        if(!filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_FLOAT)) {
            $this->_write_error($key,'float', array($this->_fields[$key]['element_name']));
        }
        
        if(strlen($params['args'])>0)
        {
            $this->_compare_values($key, $subkey, $params['args']);
        }
        return true;
    }
    
    /**
     * Compare values to assert if one value is bigger, equal or smaller than another value.
     * @param type $key
     * @param type $subkey
     * @param type $args
     * @return boolean
     */
    private function _compare_values($key,$subkey,$args)
    {
        $this->_error_messages['not_lesser_than'] = 'Campul %s trebuie sa contina o valoare mai mica decat %s';
        $this->_error_messages['not_lesser_equal_than'] = 'Campul %s trebuie sa contina o valoare mai mica sau egala cu %s';
        $this->_error_messages['not_equal_with'] = 'Campul %s trebuie sa contina o valoare egala cu %s';
        $this->_error_messages['not_bigger_equal_than'] = 'Campul %s trebuie sa contina o valoare mai mare sau egala cu %s';
        $this->_error_messages['not_bigger_than'] = 'Campul %s trebuie sa contina o valoare mai mare decat %s';
        $args_arr = explode('&', $args);
        $value = $this->_fields[$key]['value'][$subkey];
        foreach($args_arr as $arg)
        {
            preg_match_all('/^(<|<=|=|>=|>)([0-9]+)(\.([0-9]+))*/',$arg,$matches);
            $to_compare_with = $matches[2][0];
            $operator = $matches[1][0];
            if(($operator=='<') && !($value<$to_compare_with))
            {
                $this->_write_error($key, 'not_lesser_than', array($this->_fields[$key]['element_name'], $to_compare_with));
            }
            if(($operator=='<=') && !($value<=$to_compare_with))
            {
                $this->_write_error($key, 'not_lesser_equal_than', array($this->_fields[$key]['element_name'], $to_compare_with));
            }
            if(($operator=='=') && ($value!=$to_compare_with))
            {
                $this->_write_error($key, 'not_equal_with', array($this->_fields[$key]['element_name'], $to_compare_with));
            }
            if(($operator=='>=') && !($value>=$to_compare_with))
            {
                $this->_write_error($key, 'not_bigger_equal_than', array($this->_fields[$key]['element_name'], $to_compare_with));
            }
            if(($operator=='>') && !($value>$to_compare_with))
            {
                $this->_write_error($key, 'not_bigger_than', array($this->_fields[$key]['element_name'], $to_compare_with));
            }
        }
        return true;
    }
    
    /**
     * Validate an email address.
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_email($key,$subkey,$params)
    {
        $this->_error_messages['email'] = 'Campul %s trebuie sa contina o adresa de email valida';
        if(filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_EMAIL) === false) {
            $this->_write_error($key,'email', array($this->_fields[$key]['element_name']));
        }
        return true;
    }
    
    /**
     * Validate an URL.
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_url($key,$subkey,$params)
    {
        $this->_error_messages['url'] = 'Campul %s trebuie sa contina o adresa URL valida';
        if(filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_URL)) {
            $this->_write_error($key,'url', array($this->_fields[$key]['element_name']));
        }
        return true;
    }
    
    /**
     * Validate an ip IP address.
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_ip($key,$subkey,$params)
    {
        $this->_error_messages['ip'] = 'Campul %s trebuie sa contina o adresa IP';
        if((filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) && (filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)) {
            $this->_write_error($key,'ip', array($this->_fields[$key]['element_name']));
        }
        return true;
    }
    
    /**
     * Validate a boolean.
     * @param $key
     * @param $subkey
     * @param $params
     * @return bool
     */
    private function _validate_boolean($key,$subkey,$params)
    {
        $this->_error_messages['boolean'] = 'Campul %s trebuie sa contina o valoare de tip boolean (1,0,true,false)';
        if(filter_var($this->_fields[$key]['value'][$subkey], FILTER_VALIDATE_BOOLEAN)) {
            $this->_write_error($key,'boolean', array($this->_fields[$key]['element_name']));
        }
        return true;
    }
}

 

2 comments

Lasă un răspuns

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.