<?php
/**
 * File Name :			validator.class.php
 * Class : 				Validator
 * Contributor :		Everplex
 * Description :		Revised full file for php 8, the whole thing was deprecated.
 * Date : 				2024-08-18		
 * 		
 * All the server side validation functions
 * 
 */
 
 
class Validator {

    private array $input = [];
    private array $fields = [];
    private array $errors = [];

    public function __construct(?array $fields = null) {
        $this->setFields($fields);
    }

    public function setFields(?array $fields): bool {
        if ($fields && is_array($fields)) {
            $this->fields = $fields;
            return true;
        }
        return false;
    }

	public function validate(array $source, bool $userinput = true): bool {
		$this->errors = [];
		
		// Check the input and make sure the fields are specified.
		if (empty($source)) {
			$this->errors['err'] = 'Invalid input';
			return false;
		} elseif (empty($this->fields)) {
			$this->errors['err'] = 'No fields setup';
			return false;
		}

		$this->input = $source;

		foreach ($this->fields as $k => $field) {
			if (!$field['required'] && !isset($this->input[$k])) {
				continue;
			}

            
            if ($field['required'] && (!isset($this->input[$k]) || ($this->input[$k] === '' && $field['type'] !== 'int'))) {
                $this->errors[$k] = $field['error'];
                continue;
            }

            if (!isset($this->input[$k])) {
                continue;
            }

            switch (strtolower($field['type'])) {
                case 'integer':
                case 'int':
                    if (!is_numeric($this->input[$k])) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'double':
                    if (!is_numeric($this->input[$k])) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'text':
                case 'string':
                    // No specific validation for strings
                    break;
                case 'array':
                    if (!is_array($this->input[$k])) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'radio':
                    // Radio buttons are checked for existence above
                    break;
                case 'date':
                    if (strtotime($this->input[$k]) === false) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'time':
                    // Add specific time validation if needed
                    break;
                case 'phone':
                case 'fax':
                    if (!$this->isPhone($this->input[$k])) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'email':
                    if (!$this->isEmail($this->input[$k])) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'url':
                    if (!$this->isUrl($this->input[$k])) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                case 'password':
                    if (strlen($this->input[$k]) < 5) {
                        $this->errors[$k] = $field['error'] . ' (5 chars min)';
                    }
                    break;
                case 'username':
                    if (strlen($this->input[$k]) < 3) {
                        $this->errors[$k] = $field['error'] . ' (3 chars min)';
                    }
                    break;
                case 'zipcode':
                    if (!is_numeric($this->input[$k]) || (strlen($this->input[$k]) != 5)) {
                        $this->errors[$k] = $field['error'];
                    }
                    break;
                default:
                    $this->errors[$k] = $field['error'] . ' (type not set)';
            }
        }
        return empty($this->errors);
    }
   
    public function hasErrors(): bool {
        return !empty($this->errors);
    }
   
    public function errors(): array {
        return $this->errors;
    }
   
    public static function isEmail(string $email): bool {
        return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
    }

    public static function isPhone(string $phone): bool {
        $stripped = preg_replace("/(\(|\)|\-|\+|[  ]+)/", "", $phone);
        return is_numeric($stripped) && (strlen($stripped) >= 7) && (strlen($stripped) <= 16);
    }
    
    public static function isUrl(string $url): bool {
        return (bool) filter_var($url, FILTER_VALIDATE_URL);
    }

    public static function isIp(string $ip): bool {
        return (bool) filter_var($ip, FILTER_VALIDATE_IP);
    }
}
?>
