Easiest Form validation library for PHP? [closed]

I need a simple php library which can be used to easily pass rules and field names to, and then validation could be easily performed. There should also be an easy way to retrieve the errors.

Any suggestions?


I wrote a simple class of my own, combining some regexes i collected over the years with PHP's sanatize and filter functions.

<?
/**
 * Pork Formvalidator. validates fields by regexes and can sanatize them. Uses PHP filter_var built-in functions and extra regexes 
 * @package pork
 */


/**
 * Pork.FormValidator
 * Validates arrays or properties by setting up simple arrays
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2009
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{4}[-/][0-9]{1,2}[-/][0-9]{1,2}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanatations = $sanatations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "alert('there are errors in the form');"; // or your nice validation here
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanatizes an array of items according to the $this->sanatations
     * sanatations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanatations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanatize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanatations) === false && !array_key_exists($key, $this->sanatations)) continue;
            $items[$key] = self::sanatizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanatize a single var according to $type.
     * Allows for static calling to allow simple sanatization
     */
    public static function sanatizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       



}

Now this requires mootools for some of the javascript you see here, but you can easily change that to your favorite javascript framework. All it does is look up the element, and add the 'unvalidated' CSS class to it.

Usage is as simple as i always ever wanted:

Example:

$validations = array(
    'name' => 'anything',
    'email' => 'email',
    'alias' => 'anything',
    'pwd'=>'anything',
    'gsm' => 'phone',
    'birthdate' => 'date');
$required = array('name', 'email', 'alias', 'pwd');
$sanatize = array('alias');

$validator = new FormValidator($validations, $required, $sanatize);

if($validator->validate($_POST))
{
    $_POST = $validator->sanatize($_POST);
    // now do your saving, $_POST has been sanatized.
    die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
}
else
{
    die($validator->getScript());
}

To validate just one element:

$validated = new FormValidator()->validate('blah@bla.', 'email');

To sanatize just one element:

$sanatized = new FormValidator()->sanatize('<b>blah</b>', 'string');

The coolest thing about this class is that you can send your form with an ajax or iframe target and execute the resulting script. No need to refresh the page or re-send the same form data back to the browser :) Also, if the script needs changing, there's no difficult overdesigned framework to analyze, just change it any way you want :)

Oh yeah, feel free to use this anywhere you want. No licenses


The answer from SchizoDuckie above was awesome. I've used his code in project I'm working on with the permission of the author. One issue I had using this code was that if a required field was not submitted then it would not register an error. I've modified the code to cover this scenario. I've also removed the code to generate HTML and javascript as my project demands separation of UI from logic per MVC pattern. The modified code simply returns JSON encoded result. I repost the modified code here in case it is of some use to others.

<?
/**
* Pork Formvalidator. validates fields by regexes and can sanatize them. Uses PHP       filter_var built-in functions and extra regexes 
* @package pork
*/


/**
* Pork.FormValidator
* Validates arrays or properties by setting up simple arrays
* 
* @package pork
* @author SchizoDuckie
* @copyright SchizoDuckie 2009
* @version 1.0
* @access public
*/
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{4}[-/][0-9]{1,2}[-/][0-9]{1,2}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$",
            'username' => "^[\w]{3,32}\$"
);

private $validations, $sanatations, $mandatories, $equal, $errors, $corrects, $fields;


public function __construct($validations=array(), $mandatories = array(), $sanatations = array(), $equal=array())
{
    $this->validations = $validations;
    $this->sanatations = $sanatations;
    $this->mandatories = $mandatories;
    $this->equal = $equal;
    $this->errors = array();
    $this->corrects = array();
}

/**
 * Validates an array of items (if needed) and returns true or false
 *
 * JP modofied this function so that it checks fields even if they are not submitted.
 * for example the original code did not check for a mandatory field if it was not submitted.
 * Also the types of non mandatory fields were not checked.
 */
public function validate($items)
{
    $this->fields = $items;
    $havefailures = false;

    //Check for mandatories
    foreach($this->mandatories as $key=>$val)
    {
        if(!array_key_exists($val,$items))
        {
            $havefailures = true;
            $this->addError($val);
        }
    }

    //Check for equal fields
    foreach($this->equal as $key=>$val)
    {
        //check that the equals field exists
        if(!array_key_exists($key,$items))
        {
            $havefailures = true;
            $this->addError($val);
        }

        //check that the field it's supposed to equal exists
        if(!array_key_exists($val,$items))
        {
            $havefailures = true;
            $this->addError($val);
        }

        //Check that the two fields are equal
        if($items[$key] != $items[$val])
        {
            $havefailures = true;
            $this->addError($key);
        }
    }

    foreach($this->validations as $key=>$val)
    {
            //An empty value or one that is not in the list of validations or one that is not in our list of mandatories
            if(!array_key_exists($key,$items)) 
            {
                    $this->addError($key, $val);
                    continue;
            }

            $result = self::validateItem($items[$key], $val);

            if($result === false) {
                    $havefailures = true;
                    $this->addError($key, $val);
            }
            else
            {
                    $this->corrects[] = $key;
            }
    }

    return(!$havefailures);
}

/* JP
 * Returns a JSON encoded array containing the names of fields with errors and those without.
 */
public function getJSON() {

    $errors = array();

    $correct = array();

    if(!empty($this->errors))
    {            
        foreach($this->errors as $key=>$val) { $errors[$key] = $val; }            
    }

    if(!empty($this->corrects))
    {
        foreach($this->corrects as $key=>$val) { $correct[$key] = $val; }                
    }

    $output = array('errors' => $errors, 'correct' => $correct);

    return json_encode($output);
}



/**
 *
 * Sanatizes an array of items according to the $this->sanatations
 * sanatations will be standard of type string, but can also be specified.
 * For ease of use, this syntax is accepted:
 * $sanatations = array('fieldname', 'otherfieldname'=>'float');
 */
public function sanatize($items)
{
    foreach($items as $key=>$val)
    {
            if(array_search($key, $this->sanatations) === false && !array_key_exists($key, $this->sanatations)) continue;
            $items[$key] = self::sanatizeItem($val, $this->validations[$key]);
    }
    return($items);
}


/**
 *
 * Adds an error to the errors array.
 */ 
private function addError($field, $type='string')
{
    $this->errors[$field] = $type;
}

/**
 *
 * Sanatize a single var according to $type.
 * Allows for static calling to allow simple sanatization
 */
public static function sanatizeItem($var, $type)
{
    $flags = NULL;
    switch($type)
    {
            case 'url':
                    $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                    $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                    $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                    $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                    $var = substr($var, 0, 254);
                    $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                    $filter = FILTER_SANITIZE_STRING;
                    $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

    }
    $output = filter_var($var, $filter, $flags);            
    return($output);
}

/** 
 *
 * Validates a single var according to $type.
 * Allows for static calling to allow simple validation.
 *
 */
public static function validateItem($var, $type)
{
    if(array_key_exists($type, self::$regexes))
    {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
    }
    $filter = false;
    switch($type)
    {
            case 'email':
                    $var = substr($var, 0, 254);
                    $filter = FILTER_VALIDATE_EMAIL;        
            break;
            case 'int':
                    $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                    $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                    $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                    $filter = FILTER_VALIDATE_URL;
            break;
    }
    return ($filter === false) ? false : filter_var($var, $filter) !== false ? true :     false;
}           
}
?>

If you want to program some on your own and you have PHP 5.2.0 or higher. Then you can look into the filter functions.


There is also this library on github: https://github.com/blackbelt/php-validation


Zend Forms Which can be used without the entire Zend framework