dandico23/DjampMVC

View on GitHub
lib/Validator.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

namespace lib;

use lib\CustomException;

class Validator
{
    /**
     * @param array $data - list with the values to be validated
     * @param array $rules - the rules to be applied to each list element
     *      The rules should be separated by |
     *      Example:
     *           $rules = [
     *              'email' => 'required|email',
     *              'age' => 'required|numeric',
     *              'expire_date' => 'date|after:tomorrow',
     *              'punctuation' => 'digits_between:1,10|not_in:4,5',
     *              'color' => 'string|in:red,green,blue',
     *              'phone' => 'size:8'
     *           ];
     *      Options:
     *          - required - field must exist in $data
     *          - email - field must be in mail format
     *          - numeric - field must be a number
     *          - accepted - The field under validation must be yes, on, 1, or true
     *          - after:date - The field under validation must be a value after a given date.
     *                    The dates will be passed into the strtotime PHP function
     *          - after_or_equal:date - Similar to after, but considering equal
     *          - before:date - Similar to previous 2 arguments
     *          - alpha - The field under validation must be entirely alphabetic characters.
     *          - alpha_numeric - The field under validation may have alpha-numeric characters
     *          - between:min,max - The field under validation must have a size between the given min
     *                    and max (equal not included). Strings and arrays are evaluated based in size
     *          - boolean - The field under validation must be able to be cast as a boolean.
     *                      Accepted input are true, false, 1, 0, "1", and "0".
     *          - date - The field under validation must be a valid date according to
     *                   the strtotime PHP function
     *          - date_equals:date - The field under validation must be equal to the given date
     *                              The dates will be passed into the PHP strtotime function.
     *          - date_format:format - The field under validation must match the given format,
     *                                 function \DateTime::createFromFormat is used
     *          - digits:value - The field under validation must be numeric and must have an exact length of value.
     *          - digits_between:min,max - The field under validation must have a length between
     *                              the given min and max (equal not included).
     *          - in:foo,bar - The field under validation must be included in the given list of values
     *          - not_in:foo,bar - The field under validation must not be included in the given list of values
     *          - integer - The field under validation must be an integer
     *          - string - The field under validation must be a string
     *          - max:value -  must be less than or equal a maximum value.
     *                      Strings and arrays are evaluated based in size
     *          - min:value -  must be higher than or equal a minimum value.
     *                      Strings and arrays are evaluated based in size
     *          - regex:pattern - The field under validation must match the given regular expression.
     *          - size:value - The field under validation must have a size matching the given value,
     *                    represented by the number of chars if integer or string and the count function for arrays
     * @return array $result - return if the data is valid, if negative case returns a message explaining why is invalid
     */
    public function validate($data, $rules)
    {
        if (empty($data)) {
            throw new CustomException('Parâmetros não fornecidos', 500);
        }

        foreach ($rules as $field => $field_rules) {
            // Set as null if field does not exist in data array
            if (isset($data[$field])) {
                $value = $data[$field];
            } else {
                $value = null;
            }
            
            $separeted_rules = explode("|", $field_rules);
            foreach ($separeted_rules as $rule) {
                // Does not validate subsequent rules if field is optional (and is not set) or nullable (and equal to null)
                if (($rule === 'optional' && !array_key_exists($field, $data)) || ($rule === 'nullable' && $value === null)) {
                    break;
                }

                $result = $this->validateField($field, $value, $rule, $data);
                if (!$result['valid']) {
                    throw new CustomException($result[INVALID_MESSAGE], 422);
                }
            }
        }
        // If code reached here, all $results are valid, so return the last one
        return $result;
    }

    public function validateField($field, $value, $rule, $data)
    {
        $to_return = true;

        if ($rule == 'email') {
            $to_return = $this->validateEmail($value);
        } elseif ($rule == 'required') {
            $to_return = $this->validateRequired($data, $field);
        } elseif ($rule == 'numeric') {
            $to_return = $this->validateNumeric($value, $field);
        } elseif ($rule == 'accepted') {
            $to_return = $this->validateAccepted($value, $field);
        } elseif ($rule == 'alpha') {
            $to_return = $this->validateAlphabetic($value, $field);
        } elseif ($rule == 'alpha_numeric') {
            $to_return = $this->validateAlphaNumeric($value, $field);
        } elseif ($rule == 'array') {
            $to_return = $this->validateArray($value, $field);
        } elseif ($rule == 'boolean') {
            $to_return = $this->validateBoolean($value, $field);
        } elseif ($rule == 'integer') {
            $to_return = $this->validateInteger($value, $field);
        } elseif ($rule == 'string') {
            $to_return = $this->validateString($value, $field);
        } elseif ($rule == 'date') {
            $to_return = $this->validateDate($value, $field);
        } elseif (strpos($rule, 'size') !== false) {
            $to_return = $this->validateSize($rule, $value, $field);
        } elseif (strpos($rule, 'regex') !== false) {
            $to_return = $this->validateRegex($rule, $value, $field);
        } elseif (strpos($rule, 'date_equals') !== false) {
            $to_return = $this->validateDateEquals($rule, $value, $field);
        } elseif (strpos($rule, 'date_format') !== false) {
            $to_return = $this->validateDateFormat($rule, $value, $field);
        } elseif (strpos($rule, 'only_date_format') !== false) {
            $to_return = $this->validateOnlyDateFormat($rule, $value, $field);
        } elseif (strpos($rule, 'date_age_minor') !== false) {
            $to_return = $this->validateAgeMinor($value, $field);
        } elseif (strpos($rule, 'after_or_equal') !== false) {
            $to_return = $this->validateAfterOrEqualDate($rule, $value, $field);
        } elseif (strpos($rule, 'after') !== false) {
            $to_return = $this->validateAfterDate($rule, $value, $field);
        } elseif (strpos($rule, 'before_or_equal') !== false) {
            $to_return = $this->validateBeforeOrEqualDate($rule, $value, $field);
        } elseif (strpos($rule, 'before') !== false) {
            $to_return = $this->validateBeforeDate($rule, $value, $field);
        } elseif (strpos($rule, 'max') !== false) {
            $to_return = $this->validateMax($rule, $value, $field);
        } elseif (strpos($rule, 'min') !== false) {
            $to_return = $this->validateMin($rule, $value, $field);
        } elseif (strpos($rule, 'digits_between') !== false) {
            $to_return = $this->validateDigitsBetween($rule, $value, $field);
        } elseif (strpos($rule, 'between') !== false) {
            $to_return = $this->validateBetween($rule, $value, $field);
        } elseif (strpos($rule, 'digits') !== false) {
            $to_return = $this->validateDigits($rule, $value, $field);
        } elseif (strpos($rule, 'not_in') !== false) {
            $to_return = $this->validateNotIn($rule, $value, $field);
        } elseif (strpos($rule, 'in') !== false) {
            $to_return = $this->validateIn($rule, $value, $field);
        }

        if ($to_return === true) {
            return array("valid" => true, "invalid_message" => "");
        } else {
            return $to_return;
        }
    }

    public function validateRegex($rule, $value, $field)
    {
        $pattern = explode(":", $rule)[1];
        if (!preg_match($pattern, $value)) {
            $errMessage = "Field " . $field . " does not match pattern";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateSize($rule, $value, $field)
    {
        $size = explode(":", $rule)[1];
        if (is_array($value)) {
            $valid = (count($value) == $size);
        } else {
            $valid = (strlen((string)$value) == $size);
        }

        if (!$valid) {
            $errMessage = "O campo " . $field . " deve ter o tamanho " . $size . ".";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }
    
    public function validateDigits($rule, $value, $field)
    {
        $size = explode(":", $rule)[1];
        if (!(is_numeric($value) && strlen((string)$value) == $size)) {
            $errMessage = "Field " . $field . " must contain only numbers and must have size " . $size;
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateMax($rule, $value, $field)
    {
        $max = explode(":", $rule)[1];
        if (!($value <= $max)) {
            $errMessage = "Field " . $field . " exceeds maximum allowed value";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateMin($rule, $value, $field)
    {
        $min = explode(":", $rule)[1];
        if (!($value >= $min)) {
            $errMessage = "Field " . $field . " smaller than minimum allowed value";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateDigitsBetween($rule, $value, $field)
    {
        $min_max = explode(":", $rule)[1];
        $split = explode(",", $min_max);
        $min = $split[0];
        $max = $split[1];
        $length = strlen((string)$value);

        if (!($length > $min && $length < $max)) {
            $errMessage = "O tamanho do campo " . $field . " não está dentro do esperado.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateBetween($rule, $value, $field)
    {
        $min_max = explode(":", $rule)[1];
        $split = explode(",", $min_max);
        $min = $split[0];
        $max = $split[1];

        if (!(strlen($value) > $min && strlen($value) < $max)) {
            $errMessage = "O número de caracteres do campo " . $field . " deve ser entre " . $min . " e " . $max . ".";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateBoolean($value, $field)
    {
        $valid = ($value === true || $value === false || $value === 1
        || $value === 0 || $value === '1' || $value === '0');
        if (!$valid) {
            $errMessage = "O campo " . $field . " precisa ser dado como verdadeiro / falso.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateInteger($value, $field)
    {
        if (!is_int($value)) {
            $errMessage = "O campo " . $field . " precisa ser um número inteiro.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateString($value, $field)
    {
        if (!is_string($value)) {
            $errMessage = "O campo " . $field . " precisa ser uma frase (string).";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateAccepted($value, $field)
    {
        if (!($value == 'yes' || $value == 'on' || $value == 1 || $value === true)) {
            $errMessage = "Field " . $field . " must be yes, on, 1 or true";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateNumeric($value, $field)
    {
        if (!(is_numeric($value))) {
            $errMessage = "O campo " . $field . " precisa ser um número.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateEmail($value)
    {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $errMessage = "Formato de email inválido.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateRequired($data, $field)
    {
        if (!array_key_exists($field, $data)) {
            $errMessage = "O campo " . $field . " é obrigatório e precisa ser informado.";
            
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateDate($value, $field)
    {
        if (!strtotime($value)) {
            $errMessage = "O campo " . $field . " precisa ser um tipo válido de data.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateAgeMinor($value, $field)
    {
        // 31556926 is the amount of seconds in a year
        $age = floor((time() - strtotime($value)) / 31556926);
        if ($age >= 18) {
            $errMessage = "O campo " . $field . " informa usuário tem mais de 18 anos de idade.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateDateFormat($rule, $value, $field)
    {
        $format = explode(":", $rule)[1];
        if (!(\DateTime::createFromFormat($format, $value)) && strlen($value) != 4) {
            $errMessage = "O campo " . $field . " não apresenta um formato válido de data.";

            return array("valid" => false, "invalid_message" => $errMessage);
        } elseif (strlen($value) == 4) {// Creates DATE with only year
            $value = \DateTime::createFromFormat("Y", $value);
        } elseif (time() < strtotime($value)) {
            $errMessage = "Não é possível inserir uma data superior à data atual.";

            return array("valid" => false, "invalid_message" => $errMessage);
        }
        
        return true;
    }

    public function validateOnlyDateFormat($rule, $value, $field)
    {
        $format = explode(":", $rule)[1];
        if (!(\DateTime::createFromFormat($format, $value)) && strlen($value) != 4) {
            $errMessage = "O campo " . $field . " não apresenta um formato válido de data.";

            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateArray($value, $field)
    {
        if (!is_array($value)) {
            $errMessage = "O campo " . $field . " precisa ser um vetor de dados (array).";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateAlphaNumeric($value, $field)
    {
        if (!ctype_alnum($value)) {
            $errMessage = "O campo " . $field . " deve conter apenas caracteres alfa-numéricos.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateAlphabetic($value, $field)
    {
        $reg = '/^[A-Za-záàâãéèêíïóôõöúçñÁÀÂÃÉÈÍÏÓÔÕÖÚÇÑ ]+$/';
        if (!preg_match($reg, $value)) {
            $errMessage = "O campo " . $field . " deve conter apenas caracteres alfabéticos.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }


    public function validateNotIn($rule, $value, $field)
    {
        $not_allowed_values = explode(":", $rule)[1];
        $not_allowed_values = explode(",", $not_allowed_values);

        if (in_array($value, $not_allowed_values)) {
            $errMessage = "O campo " . $field . " não está contido nos valores aceitos.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateIn($rule, $value, $field)
    {
        $accepted_values = explode(":", $rule)[1];
        $accepted_values = explode(",", $accepted_values);

        if (!in_array($value, $accepted_values)) {
            $errMessage = "O valor do campo " . $field . " não é aceito.";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateDateEquals($rule, $value, $field)
    {
        $given_date = explode(":", $rule)[1];
        $value = strtotime($value);
        $given_date = strtotime($given_date);

        if (!($value && $given_date && $value == $given_date)) {
            $errMessage = "Field " . $field . " value not equal given date";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateAfterDate($rule, $value, $field)
    {
        $given_date = explode(":", $rule)[1];
        $given_date = strtotime($given_date);
        $value = strtotime($value);

        if (!($value && $given_date && $value > $given_date)) {
            $errMessage = "Field " . $field . " value not after given date";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateAfterOrEqualDate($rule, $value, $field)
    {
        $given_date = explode(":", $rule)[1];
        $given_date = strtotime($given_date);
        $value = strtotime($value);
        if (! ($value && $given_date && $value >= $given_date)) {
            $errMessage = "Field " . $field . " value not after or equal given date";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }
    

    public function validateBeforeDate($rule, $value, $field)
    {
        $given_date = explode(":", $rule)[1];
        $given_date = strtotime($given_date);
        $value = strtotime($value);

        if (!($value && $given_date && $value < $given_date)) {
            $errMessage = "Field " . $field . " value not before given date";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }

    public function validateBeforeOrEqualDate($rule, $value, $field)
    {
        $given_date = explode(":", $rule)[1];
        $given_date = strtotime($given_date);
        $value = strtotime($value);

        if (!($value && $given_date && $value <= $given_date)) {
            $errMessage = "Field " . $field . " value not before or equal given date";
            return array("valid" => false, "invalid_message" => $errMessage);
        }
        return true;
    }
}