errors = []; foreach ($rules as $field => $fieldRules) { foreach ($fieldRules as $rule) { // Check if the rule has parameters if (strpos($rule, ':') !== false) { list($ruleName, $ruleParam) = explode(':', $rule, 2); } else { $ruleName = $rule; $ruleParam = null; } // Skip validation if the field is not required and empty if ($ruleName !== 'required' && (!isset($data[$field]) || $data[$field] === '')) { continue; } // Validate according to rule $method = 'validate'.ucfirst($ruleName); if (method_exists($this, $method)) { $this->$method($field, $data[$field] ?? null, $ruleParam); } } } return empty($this->errors); } /** * @return mixed */ public function getErrors() { return $this->errors; } /** * @return mixed */ public function getFirstError() { foreach ($this->errors as $field => $errors) { if (!empty($errors)) { return $errors[0]; } } return null; } /** * @param $field * @param $value * @param $param */ private function validateRequired($field, $value, $param = null) { if (!isset($value) || $value === '') { $this->errors[$field][] = "The $field field is required."; } } /** * @param $field * @param $value * @param $param */ private function validateEmail($field, $value, $param = null) { if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { $this->errors[$field][] = "The $field must be a valid email address."; } } /** * @param $field * @param $value * @param $param */ private function validateMin($field, $value, $param) { if (is_string($value) && mb_strlen($value) < $param) { $this->errors[$field][] = "The $field must be at least $param characters."; } elseif (is_numeric($value) && $value < $param) { $this->errors[$field][] = "The $field must be at least $param."; } } /** * @param $field * @param $value * @param $param */ private function validateMax($field, $value, $param) { if (is_string($value) && mb_strlen($value) > $param) { $this->errors[$field][] = "The $field may not be greater than $param characters."; } elseif (is_numeric($value) && $value > $param) { $this->errors[$field][] = "The $field may not be greater than $param."; } } /** * @param $field * @param $value * @param $param */ private function validateNumeric($field, $value, $param = null) { if (!is_numeric($value)) { $this->errors[$field][] = "The $field must be a number."; } } /** * @param $field * @param $value * @param $param */ private function validateInteger($field, $value, $param = null) { if (!filter_var($value, FILTER_VALIDATE_INT)) { $this->errors[$field][] = "The $field must be an integer."; } } /** * @param $field * @param $value * @param $param */ private function validateFloat($field, $value, $param = null) { if (!filter_var($value, FILTER_VALIDATE_FLOAT)) { $this->errors[$field][] = "The $field must be a decimal number."; } } /** * @param $field * @param $value * @param $param */ private function validateAlpha($field, $value, $param = null) { if (!ctype_alpha($value)) { $this->errors[$field][] = "The $field may only contain letters."; } } /** * @param $field * @param $value * @param $param */ private function validateAlphaNum($field, $value, $param = null) { if (!ctype_alnum($value)) { $this->errors[$field][] = "The $field may only contain letters and numbers."; } } /** * @param $field * @param $value * @param $param */ private function validateUrl($field, $value, $param = null) { if (!filter_var($value, FILTER_VALIDATE_URL)) { $this->errors[$field][] = "The $field must be a valid URL."; } } /** * @param $field * @param $value * @param $param */ private function validateDate($field, $value, $param = null) { if (!strtotime($value)) { $this->errors[$field][] = "The $field must be a valid date."; } } /** * @param $field * @param $value * @param $param */ private function validateIn($field, $value, $param) { $allowedValues = explode(',', $param); if (!in_array($value, $allowedValues)) { $this->errors[$field][] = "The selected $field is invalid."; } } /** * @param $field * @param $value * @param $param */ private function validateUnique($field, $value, $param) { list($table, $column, $exceptId) = array_pad(explode(',', $param), 3, null); $db = Database::getInstance(); $query = "SELECT COUNT(*) FROM $table WHERE $column = ?"; $params = [$value]; if ($exceptId) { $query .= " AND id != ?"; $params[] = $exceptId; } $count = $db->fetchColumn($query, $params); if ($count > 0) { $this->errors[$field][] = "The $field has already been taken."; } } /** * @param $field * @param $value * @param $param */ private function validateExists($field, $value, $param) { list($table, $column) = explode(',', $param); $db = Database::getInstance(); $query = "SELECT COUNT(*) FROM $table WHERE $column = ?"; $count = $db->fetchColumn($query, [$value]); if ($count == 0) { $this->errors[$field][] = "The selected $field is invalid."; } } /** * @param $field * @param $value * @param $param */ private function validateConfirmed($field, $value, $param = null) { $confirmation = $field.'_confirmation'; if (!isset($_POST[$confirmation]) || $value !== $_POST[$confirmation]) { $this->errors[$field][] = "The $field confirmation does not match."; } } }