Have I followed best practices for PHP development, or can this class be improved?GitHub<?php namespace azi;/** * Class Validator * * @package azi * @author  Azi Baloch <http://www.azibaloch.com> * @version 1.0 * @license The MIT License (MIT) * */class Validator {    /**     * RegExp patterns     *     * @var array     */    private $expressions = [ ];    /**     * Custom RegExp error messages     *     * @var array     */    private $error_messages = [ ];    /**     * holds validation errors     *     * @var array     */    private $validation_errors = [ ];    /**     * @var array     */    private static $errors = [ ];    /**     * @var null     */    private static $instance = null;    /**     * @var string     */    private static $session_data_key = form_validation_errors;    /**     * @var array     */    private $builtin_rules = [];    /**     *  Class Constructor     */    public function __construct() {        // load built-in expressions        $this->expressions = array(            'alpha'     => '#^([a-zA-Z\s])+$#',            'num'       => '#^([0-9])+$#',            'alpha-num' => '#^([a-zA-Z0-9\s])$#',        );        $this->builtin_rules = array(            array(                'id' => 'email',                'exp' => '#^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$#',                'message' => 'Please enter a valid email address'            )        );        foreach($this->builtin_rules as $rule) {            $rule = (object) $rule;            $this->registerExpression($rule->id, $rule->exp, $rule->message);        }        static::$instance = $this;    }    /**     * @param $key     * @param $expression     * @param null $message     *     * @return bool     * @throws \Exception     */    public function registerExpression( $key, $expression, $message = null ) {        if ( ! isset( $this->expressions[ $key ] ) ) {            $this->expressions[ $key ] = $expression;            if ( $message ) {                $this->error_messages[ $key ] = $message;            }            return true;        }        throw new \Exception( Expression key already exists );    }    /**     * @param $key     * @param $newExpression     * @param null $message     *     * @return bool     * @throws \Exception     */    public function updateExpression( $key, $newExpression, $message = null ) {        if ( $this->expressions[ $key ] ) {            $this->expressions[ $key ] = $newExpression;            if ( $message ) {                $this->error_messages[ $key ] = $message;            }            return true;        }        throw new \Exception( Expression dose not exists );    }    /**     * @return bool     */    public function passed() {        if ( count( $this->validation_errors ) < 1 ) {            return true;        }        return false;    }    /**     * retrieve error messages after validation     *     * @return array     */    public function getErrors() {        return $this->validation_errors;    }    /**     * Get error message of a field     *     * @param $fieldKey     *     * @return mixed     */    public static function error( $fieldKey ) {        if(!session_id()) {            session_start();        }        if(isset($_SESSION[static::$session_data_key])) {            if(count($_SESSION[static::$session_data_key]) > 0) {                self::$errors = $_SESSION[ static::$session_data_key ];                unset($_SESSION[static::$session_data_key]);            }        } else {            if(!is_null(static::$instance)) {                self::$errors = static::$instance->validation_errors;            }        }        if ( isset( self::$errors[ $fieldKey ][ 'message' ] ) ) {            return self::$errors[ $fieldKey ][ 'message' ];        }        return false;    }    /**     * @param array $fields the array of form fields - ( $_POST , $_GET, $_REQUEST )     * @param $rules     *     * @return Validator $this     */    public function validate( $fields, $rules ) {        $return = [ ];        foreach ( $fields as $key => $field ) {            if ( ! array_key_exists( $key, $rules ) ) {                continue;            }            $r       = $rules[ $key ];            $matches = [ ];            $not     = false;            if ( preg_match( '#if:#', $r ) ) {                preg_match( #if:(.*)\\[\\!(.*)\\]:(.*)\\[!(.*)\\]\\((.*)\\)#, $r, $matches );                if ( count( $matches ) < 1 ) {                    preg_match( #if:(.*)\\[(.*)\\]\\((.*)\\)#, $r, $matches );                } else {                    $not = true;                }                $r = end( $matches );            }            if ( ! strpos( $r, | ) ) {                $r .= |IGNORE_ME5;            }            $theRules = explode( |, $r );            foreach ( $theRules as $theRule ) {                if ( $theRule == IGNORE_ME5 ) {                    continue;                }                $customMessage = [ ];                if ( strpos( $theRule, -- ) ) {                    $rcm     = explode( --, $theRule ); // custom message for current rule                    $theRule = $rcm[ 0 ];                    if ( strpos( $rcm[ 0 ], : ) ) {                        $rcm[ 0 ] = explode( :, $rcm[ 0 ] )[ 0 ];                    }                    $customMessage[ $rcm[ 0 ] ] = $rcm[ 1 ];                }                if ( count( $matches ) > 0 ) {                    if ( $not ) {                        if ( $fields[ $matches[ 1 ] ] == $matches[ 2 ] && $fields[ $matches[ 3 ] ] == $matches[ 4 ] ) {                            continue;                        }                    } else {                        if ( $fields[ $matches[ 1 ] ] != $matches[ 2 ] ) {                            continue;                        }                    }                }                if ( strtolower( $theRule ) == required ) {                    if ( ! empty( $customMessage[ 'required' ] ) ) {                        $theMessage = $customMessage[ 'required' ];                    } else {                        $theMessage = $this->keyToLabel( $key ) . ' is required';                    }                    if ( $field ==  ) {                        $return[ $key ] = [                            'error'   => 'required',                            'message' => $theMessage                        ];                        continue;                    }                }                if ( strtolower( $theRule ) == alpha ) {                    if ( ! preg_match( $this->expressions[ 'alpha' ], $field ) ) {                        if ( ! empty( $customMessage[ 'alpha' ] ) ) {                            $theMessage = $customMessage[ 'alpha' ];                        } else {                            $theMessage = $this->keyToLabel( $key ) . ' must not contain numbers and special characters';                        }                        $return[ $key ] = [                            'error'   => 'alpha',                            'message' => $theMessage                        ];                        continue;                    }                }                if ( strtolower( $theRule ) == num ) {                    if ( ! empty( $customMessage[ 'num' ] ) ) {                        $theMessage = $customMessage[ 'num' ];                    } else {                        $theMessage = $this->keyToLabel( $key ) . ' may only contain numbers';                    }                    if ( ! preg_match( $this->expressions[ 'num' ], $field ) ) {                        $return[ $key ] = [                            'error'   => 'num',                            'message' => $theMessage                        ];                        continue;                    }                }                if ( strtolower( $theRule ) == alpha-num ) {                    if ( ! empty( $customMessage[ 'alpha-num' ] ) ) {                        $theMessage = $customMessage[ 'alpha-num' ];                    } else {                        $theMessage = $this->keyToLabel( $key ) . ' may only contain alpha numeric characters';                    }                    if ( ! preg_match( $this->expressions[ 'alpha-num' ], $field ) ) {                        $return[ $key ] = [                            'error'   => 'alpha-num',                            'message' => $theMessage                        ];                        continue;                    }                }                if ( strpos( $theRule, ':' ) ) {                    $theRule = explode( :, $theRule );                    $length = $theRule[1];                    $theRule = $theRule[0];                    if ( strtolower( $theRule ) == min ) {                        if ( ! empty( $customMessage[ 'min' ] ) ) {                            $theMessage = $customMessage[ 'min' ];                        } else {                            $theMessage = $this->keyToLabel( $key ) . ' must be at least ' . $length .  characters long;                        }                        if ( strlen( $field ) < $length ) {                            $return[ $key ] = [                                'error'   => 'min',                                'message' => $theMessage                            ];                            continue;                        }                    }                    if ( strtolower( $theRule ) == max ) {                        if ( ! empty( $customMessage[ 'max' ] ) ) {                            $theMessage = $customMessage[ 'max' ];                        } else {                            $theMessage = $this->keyToLabel( $key ) . ' must be less than ' . $length .  characters;                        }                        if ( strlen( $field ) > $length ) {                            $return[ $key ] = [                                'error'   => 'max',                                'message' => $theMessage                            ];                            continue;                        }                    }                }                /* Custom Expressions */                if ( array_key_exists( $theRule, $this->expressions ) ) {                    if ( ! preg_match( $this->expressions[ $theRule ], $field ) ) {                        if(isset($customMessage[$theRule])) {                            $error_message = $customMessage[$theRule];                        } else if ( array_key_exists( $theRule, $this->error_messages ) ) {                            $error_message = $this->error_messages[ $theRule ];                        } else {                            $error_message = $this->keyToLabel( $key ) . ' dose\'t match the required pattern';                        }                        $return[ $key ] = [                            'error'   => $theRule,                            'message' => $error_message                        ];                        continue;                    }                }            }        }        $this->validation_errors = $return;        return $this;    }    /**     * Convert an array key to Label eg. full_name to Full Name     *     * @param $key     *     * @return string     */    private function keyToLabel( $key ) {        return ucwords( str_replace( [ '-', '_', '+' ],  , $key ) );    }    public function goBackWithErrors(){        if(!session_id()) {            session_start();        }        $_SESSION[static::$session_data_key] = $this->validation_errors;        header('Location: ' . $_SERVER['HTTP_REFERER']);        exit;    }}
Server side form validation library
php;beginner;validation;library
Look at Symfony 2 Validation and you will find many useful things.About the code split validate method to small prices: it should not take more than one screen.Use protected instead of private if you are not 100% sure it must be private.In your case, $session_data_key must be constant.If you are providing a library, it must be unit tested and when you will start do it you will find many other architectural problems.