A standalone, expandable and a single-class validator for your user inputs. Good for those small projects where using a framework is too much.
Adding additional rules is easy. You add the error message to the protected $messages
array defined at the top of the class, and if there is a strict mode to the rule you add a strict variant of your message into the protected $strictMessages
defined above the protected $messages
array. And then you set $this->messages['ruleName'] = $this->strictMessages['ruleName']
. Set the key to the lowercase version of your rule you will be using in the validator rule list.
Let's say you want to add a rule named camelCase
. You would have a message in $this->messages
array with the key set to camelCase
and than you would add a protected function named validateCamelCase($field, $arg, $arg2, $arg3)
and then use that in your rules list like camelCase:arg,arg2,arg3
. That's all it takes to add your own validation rule to this class.
<?php
class Validate
{
protected array $input;
protected array $rules;
protected array $errors;
public array $validated;
protected $strictMessages = [
'numeric' => ':field must consist of only numbers.',
'lowercase' => ':field must have all lowercase letters.',
'uppercase' => ':field must have all uppercase letters.',
'alpha' => ':field must only consist of alphabetical characters.',
];
protected $messages = [
'required' => ':field is required.',
'email' => 'The :field must be a valid email address.',
'phone' => ':field is not a valid phone number.',
'min' => ':field must be at least :min characters long.',
'max' => ':field must not exceed :max characters in length.',
'unique' => ':field already exists in our records.',
'confirm' => ':field does not match with the :confirm field.',
'numeric' => ':field must have numbers.',
'lowercase' => ':field must have a lowercase letter',
'uppercase' => ':field must have an uppercase letter.',
'alpha' => ':field must have a letter.',
'mixed' => ':field must contain a mix of numbers and letters.',
'mixedCase' => ':field must consist of uppercase and lowercase letters.',
'range' => ':field must be between :min and :max characters in length.'
];
// :field is replaced seperately.
protected $placeholders = [':min', ':max', ':confirm'];
protected $pattern = [
'phone' => '/^\(\d{3}\) \d{3}-\d{4}$/',
'uppercase' => '/[A-Z]/',
'lowercase' => '/[a-z]/',
'alpha' => '/[a-zA-Z]/',
'numeric' => '/[0-9]/',
];
public function __construct(array $input, array $rules, array $customMessages = null, array $customStrictMessages = null)
{
$this->input = $input;
$this->rules = $rules;
$this->messages = $customMessages ?? $this->messages;
$this->strictMessages = $customStrictMessages ?? $this->strictMessages;
}
public function validate() :bool
{
foreach ($this->rules as $field => $fieldRules) {
$rules = explode('|', $fieldRules);
$rules = array_filter($rules, 'strlen');
foreach ($rules as $rule) {
$this->applyRule($field, $rule);
}
}
return empty($this->errors);
}
protected function applyRule(string $field, string $rule) :void
{
$params = [];
if (strpos($rule, ':') !== false) {
list($rule, $params) = explode(':', $rule, 2);
$params = explode(',', $params);
}
$method = "validate" . ucfirst($rule);
if (method_exists($this, $method)) {
$value = $this->input[$field] ?? null;
if (!$this->$method($field, ...$params) && (!in_array('nullable', explode('|', $this->rules[$field])) || !empty($value))) {
$errorMessage = $this->getErrorMessage($field, $rule, $params);
$this->errors[$field][] = $errorMessage;
} else {
if ($rule != 'confirm') {
$this->validated[$field] = $value;
} else {
if (isset($this->validated[$field])) {
unset($this->validated[$field]);
}
}
}
} else {
throw new \InvalidArgumentException("Validation rule '{$rule}' not supported.");
}
}
protected function validateNullable(string $field) :bool
{
return true;
}
protected function validateRequired(string $field) :bool
{
return isset($this->input[$field]) && trim($this->input[$field]) !== '';
}
protected function validateMin(string $field, int $minLength) :bool
{
return isset($this->input[$field]) && strlen($this->input[$field]) >= $minLength;
}
protected function validateMax(string $field, int $maxLength) :bool
{
return isset($this->input[$field]) && strlen($this->input[$field]) <= $maxLength;
}
protected function validateEmail(string $field) :bool
{
return isset($this->input[$field]) && filter_var($this->input[$field], FILTER_VALIDATE_EMAIL) !== false;
}
protected function validateConfirm(string $field, string $confirmationField) :bool
{
return isset($this->input[$field]) && isset($this->input[$confirmationField]) &&
$this->input[$field] === $this->input[$confirmationField];
}
protected function validateUnique(string $field, string $modelName) :bool
{
$model = "App\\Models\\" . ucfirst($modelName);
return (new $model)->unique($field, $this->input[$field]);
}
protected function validatePhone(string $field) :bool
{
// Return the result of the pattern match
return preg_match($this->pattern['phone'], $this->input[$field]);
}
protected function validateNumeric(string $field, bool $strict = false) :bool
{
if ($strict) {
$this->messages['numeric'] = $this->strictMessages['numeric'];
return ctype_digit($this->input[$field]);
}
return preg_match($this->pattern['numeric'], $this->input[$field]) > 0;
}
protected function validateAlpha(string $field, bool $strict = false) :bool
{
if ($strict) {
$this->messages['alpha'] = $this->strictMessages['alpha'];
return ctype_alpha($this->input[$field]);
}
return preg_match($this->pattern['alpha'], $this->input[$field]) > 0;
}
protected function validateMixed(string $field) :bool
{
return ($this->validateNumeric($field) && $this->validateAlpha($field));
}
protected function validateUppercase(string $field, bool $strict = false) :bool
{
if ($strict) {
$this->messages['uppercase'] = $this->strictMessages['uppercase'];
return ctype_upper(str_replace(' ', '', $this->input[$field]));
}
return preg_match($this->pattern['uppercase'], $this->input[$field]) > 0;
}
protected function validateLowercase(string $field, bool $strict = false) :bool
{
if ($strict) {
$this->messages['lowercase'] = $this->strictMessages['lowercase'];
return ctype_lower(str_replace(' ', '', $this->input[$field]));
}
return preg_match($this->pattern['lowercase'], $this->input[$field]) > 0;
}
protected function validateMixedCase(string $field) :bool
{
return ($this->validateUppercase($field) && $this->validateLowercase($field));
}
protected function validateRange(string $field, int $min, int $max) :bool
{
$length = strlen($this->input[$field]);
return ($length >= $min && $length <= $max);
}
protected function getErrorMessage(string $field, string $rule, array $params) :string
{
$message = $this->messages[$rule] ?? "Validation failed for rule '{$rule}'";
foreach ($params as $param) {
foreach ($this->placeholders as $placeholder) {
if (strpos($message, $placeholder)) {
$message = str_replace($placeholder, $param, $message);
break; // Break out of the inner loop once a placeholder is replaced
}
}
}
return str_replace(':field', ucwords(str_replace('_', ' ', $field)), $message);
}
public function getErrors() :array
{
return $this->errors;
}
public function validated() :array
{
return $this->validated;
}
}
This code snippet was published on It was last edited on