setData($data); $this->addRules($rules); } /** * Magic invoke method to make Rule instance * @param mixed $rule * @param mixed $params * @return Rule * @throws ValidationException */ public function __invoke($rule, $params = []) { $object = Rule::factory($rule, $params, $this); if ($object !== false) { return $object; } throw new ValidationException(_t('system', "Validation rule '[rule]' is not registered", ['rule' => $rule])); } /** * Set data to validate * @param string|array $data */ public function setData($data) { $this->data = []; if (is_string($data)) { if ($data === 'get') { $this->data = bff::input()->get(); } elseif ($data === 'post') { $this->data = bff::input()->post(); } } elseif (is_array($data)) { $this->data = $data; } $this->dataValid = []; } /** * Make validation rule * @param int|string|Rule|Closure $rule * @param array $params * @return Rule|false */ public function rule($rule, $params = []) { return $this->__invoke($rule, $params); } /** * Add rules * @param array $rules * @param bool $reset */ public function addRules($rules = [], $reset = false) { if ($reset) { $this->rules = []; $this->ruleLast = null; } foreach ($rules as $attribute => $list) { if (is_array($list)) { foreach ($list as $rule => $params) { if (is_int($rule)) { if (is_string($params) || $params instanceof Rule) { $this->addRule($attribute, $params); } elseif ($params instanceof Closure) { $this->addRule($attribute, 'callback', $params); } elseif (is_array($params)) { $this->addFilter($attribute, $rule, $params); } elseif (is_int($params)) { $this->addFilter($attribute, $params); } } elseif (is_string($rule)) { $this->addRule($attribute, $rule, $params); } } } elseif (is_string($list) || $list instanceof Rule) { $this->addRule($attribute, $list); } elseif ($list instanceof Closure) { $this->addRule($attribute, 'callback', $list); } elseif (is_int($list)) { $this->addFilter($attribute, $list); } } } /** * Add attribute rule * @param string $attribute * @param string|Rule $rule * @param array|mixed $params */ public function addRule($attribute, $rule, $params = []) { if ($this->addDirective($attribute, $rule, $params)) { return; } $rule = Rule::factory($rule, $params, $this); if ($rule !== false) { if (! array_key_exists($attribute, $this->rules)) { $this->rules[$attribute] = []; } $this->rules[$attribute][] = ($this->ruleLast = $rule); # Not Required => Optional if ($rule instanceof Required && ! $this->isOptional($attribute)) { $this->setOptional($attribute, $rule); # deferred } } } /** * Check if any rules added * @param bool $attribute * @return bool */ public function hasRules($attribute = false) { if ($attribute !== false) { return array_key_exists($attribute, $this->rules); } return sizeof($this->rules) > 0; } /** * Get attribute rule instance * @param string $attribute attribute * @param string $rule rule name * @return bool|Rule */ protected function getRule($attribute, $rule) { if (array_key_exists($attribute, $this->rules) && is_string($rule)) { $ruleSearch = mb_strtolower($rule); /** @var Rule $rule */ foreach ($this->rules[$attribute] as $rule) { if ($rule->getName() === $ruleSearch) { return $rule; } } } return false; } /** * Get attribute value * @param string $attribute attribute name * @param mixed $default default value * @return mixed|null */ public function getValue($attribute, $default = null) { return Arr::get($this->data, $attribute, $default); } /** * Add validator directive * @param string $attribute * @param string $directive * @param array $params * @return bool */ public function addDirective($attribute, $directive, $params = []) { if (is_string($directive) && in_array($directive, $this->directivesList, true)) { if ($directive === 'optional') { if (is_bool($params) || $params instanceof Closure) { $this->setOptional($attribute, $params); } else { $this->setOptional($attribute, true); } return true; } elseif ($directive === 'stop') { if ($this->ruleLast instanceof Rule) { $this->ruleLast->stop(); } return true; } elseif ($directive === 'title' && is_string($params)) { $this->setTitle($attribute, $params); return true; } elseif ($directive === 'default') { $this->setDefault($attribute, $params); return true; } } return false; } /** * Add filter * @param string $attribute * @param int|Closure $filter * @param array $params filter params * @return void */ public function addFilter($attribute, $filter, array $params = []) { if (is_int($filter) && $filter > 0) { $this->filters[$attribute][$filter] = $params; } elseif ($filter instanceof Closure) { $this->filters[$attribute][] = $filter; } $this->ruleLast = null; } /** * Set attribute title * @param string $attribute * @param string $title */ public function setTitle($attribute, $title) { $this->titles[$attribute] = $title; } /** * Is optional attribute * @param string $attribute * @return bool */ protected function isOptional($attribute) { if (array_key_exists($attribute, $this->optional)) { $optional = $this->optional[$attribute]; if (is_bool($optional)) { return $optional; } elseif ($optional instanceof Required) { $optional = $optional->isOptional($this->data(false, true)); $this->setOptional($attribute, $optional); return $optional; } elseif ($optional instanceof Closure) { $optional = ($optional($this->data(false, true), $this) === true); $this->setOptional($attribute, $optional); return $optional; } } return false; } /** * Mark attribute as optional * @param string $attribute * @param bool|Required|Closure $optional */ public function setOptional($attribute, $optional = true) { $this->optional[$attribute] = $optional; } /** * Set attribute default value * @param string $attribute * @param mixed $value */ public function setDefault($attribute, $value) { if (! is_null($value)) { $this->defaults[$attribute] = $value; } } /** * Validate data * @param bool $errors set errors * @return bool */ public function validate($errors = false) { $this->failed = 0; $this->messages = []; $this->dataValid = []; # Defaults foreach ($this->defaults as $attribute => $default) { $value = $this->getValue($attribute); if (is_null($value) && ! $this->isOptional($attribute)) { Arr::set($this->data, $attribute, $default); } } # Filter foreach ($this->filters as $attribute => $filters) { $value = $this->getValue($attribute); if (is_null($value) && $this->isOptional($attribute)) { continue; } foreach ($filters as $filter => $params) { if (is_array($params)) { $value = bff::input()->clean($value, $filter, !is_null($value), $params); } elseif ($params instanceof Closure) { $value = $params($value); } } Arr::set($this->data, $attribute, $value); Arr::set($this->dataValid, $attribute, $value); } # Validate /** @var $rule Rule */ foreach ($this->rules as $attribute => $rules) { $value = $this->getValue($attribute); if (is_null($value) && $this->isOptional($attribute)) { continue; } foreach ($rules as $rule) { if (! $rule->check($value)) { $this->failed++; $rule->replace('value', $value, false); $message = $this->getRuleMessage($attribute, $rule); if ($errors) { Errors::set($message, false, ($attribute !== '' ? $attribute : null)); } $this->messages[] = [ 'attribute' => $attribute, 'message' => $message, 'rule' => $rule, ]; Arr::forget($this->dataValid, $attribute); if ($rule->hasStop()) { break; } } else { if ($value != $this->getValue($attribute)) { Arr::set($this->data, $attribute, $value); } Arr::set($this->dataValid, $attribute, $value); } } } return !$this->failed; } /** * Get failed * @param bool $extended extended version * @return int|array */ public function failed($extended = false) { if ($extended) { return $this->messages; } return $this->failed; } /** * Prepare rule message * @param string $attribute * @param Rule $rule * @return string */ protected function getRuleMessage($attribute, Rule $rule) { return $rule->getMessage(array_key_exists($attribute, $this->titles) ? $this->titles[$attribute] : $attribute); } /** * Set attribute rule message * @param string $attribute * @param string|array $rule * @param string $message */ public function setMessage($attribute, $rule, $message = '') { if (is_string($attribute)) { if (is_string($rule) && is_string($message)) { if ($message === '' && mb_stripos($attribute, '.') !== false) { $message = $rule; list($attribute, $rule) = explode('.', $attribute, 2); $this->setMessage($attribute, $rule, $message); } else { $rule = $this->getRule($attribute, $rule); if ($rule !== false) { $rule->setMessage($message); } } } elseif (is_array($rule)) { foreach ($rule as $k => $v) { if (is_string($k) && is_string($v)) { $this->setMessage($attribute, $k, $v); } } } } } /** * Set attributes messages * @param array $messages */ public function setMessages(array $messages) { foreach ($messages as $attribute => $message) { if (is_string($attribute) && is_string($message) && mb_stripos($attribute, '.') !== false) { list($attribute, $rule) = explode('.', $attribute, 2); $this->setMessage($attribute, $rule, $message); } } } /** * Get messages * @param bool $group group messages - one per attribute * @return array */ public function messages($group = true) { $messages = []; foreach ($this->messages as $v) { if ($group) { if (! array_key_exists($v['attribute'], $messages)) { $messages[$v['attribute']] = $v['message']; } } else { $messages[$v['attribute']][] = $v['message']; } } return $messages; } /** * Get data * @param bool $valid valid only * @param bool $object as fluent object * @return array|Fluent */ public function data($valid = true, $object = false) { if ($object) { return new Fluent(($valid ? $this->dataValid : $this->data)); } return ($valid ? $this->dataValid : $this->data); } }