Skip to main content

Validation class that uses PHP's native filter validation functions.

<?php

/**
 * FormValidator
 *
 * Validates arrays or properties by setting up simple arrays.
 * Note that some of the regexes are for dutch input!
 *
 * ::Examples::
 *
 * $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');
 *
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 *
 * @version 1.0
 */
class FormValidator
{
    // Note that some of the regexes are for dutch input!
    public static $regexes = array(
            'date'        => '^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}$',
            '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 .= 'new FormValidator().showMessage();';
        }

        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.
     *
     * @param string $field
     * @param string $type
     */
    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.
     *
     * @param string $var
     * @param string $type
     *
     * @return bool true if valid value, otherwise false.
     */
    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;
    }
}