'asko@bien.ee']; * $rules = ['email' => 'required|email']; * $hird = new Hird($fields, $rules); * * if ($hird->fails()) { * return $hird->errors(); * } * ``` * * @author Asko Nomm */ class Hird { private array $errors = []; private array $validators = []; private array $fieldNames = []; public function __construct( private array $fields, private array $rules, ) { $this->setFieldNames([]); $this->registerDefaultValidators(); } /** * Registers the default, built-in validators. * * @return void */ private function registerDefaultValidators(): void { $this->registerValidator('len', LenValidator::class); $this->registerValidator('email', EmailValidator::class); $this->registerValidator('required', RequiredValidator::class); $this->registerValidator('date-format', DateFormatValidator::class); } /** * Composes the field names array. * * @param array $fieldNames * @return void */ public function setFieldNames(array $fieldNames): void { foreach ($this->fields as $field => $value) { $this->fieldNames[$field] = $fieldNames[$field] ?? $field; } // Re-register the default validators to pass the field names. $this->registerDefaultValidators(); } /** * Registers a validator to a `$ruleName`. * * @param string $ruleName * @param Validator $validator * @return void */ public function registerValidator(string $ruleName, string $validator): void { $class = new \ReflectionClass($validator); $instance = null; if ($class->getConstructor() !== null) { $instance = $class->newInstanceArgs([$this->fields, $this->fieldNames]); } else { $instance = $class->newInstance(); } $this->validators[$ruleName] = $instance; } /** * Removes a validator assigned to the `$ruleName`. * * @param string $ruleName * @return void */ public function removeValidator(string $ruleName): void { unset($this->validators[$ruleName]); } /** * Runs `$this->rules` over `$this->fields` to construct * potential errors that will be stored as an array of strings * in `$this->errors`. * * @return void */ public function validate(): void { foreach ($this->rules as $field => $rule) { $value = isset($this->fields[$field]) ? $this->fields[$field] : ''; foreach (explode('|', $rule) as $item) { if (str_contains($item, ':')) { $itemParts = explode(':', $item); $name = $itemParts[0]; $modifier = implode(':', array_slice($itemParts, 1, count($itemParts) - 1, true)); if (!$this->validators[$name]->validate($field, $value, $modifier)) { $this->errors[] = $this->validators[$name]->composeError($field, $modifier); } } else { if (!$this->validators[$item]->validate($field, $value)) { $this->errors[] = $this->validators[$item]->composeError($field); } } } } } /** * Returns a boolean `true` if there have been any errors. * Returns `false` otherwise. * * @return boolean */ public function fails(): bool { $this->validate(); return count($this->errors) !== 0; } /** * Returns an array of strings where each string * is a single error that happened during validation. * * @return array */ public function errors(): array { return $this->errors; } /** * If errors are present, returns the first one. * Otherwise returns an empty string. * * @return string */ public function firstError(): string { if (count($this->errors) > 0) { return $this->errors[0]; } return ''; } }