'\bff\validation\rules\Accepted', 'between' => '\bff\validation\rules\Between', 'callback' => '\bff\validation\rules\Callback', 'date' => '\bff\validation\rules\Date', 'email' => '\bff\validation\rules\Email', 'in' => '\bff\validation\rules\In', 'ip' => '\bff\validation\rules\Ip', 'array' => '\bff\validation\rules\IsArray', 'integer' => '\bff\validation\rules\IsInteger', 'numeric' => '\bff\validation\rules\IsNumeric', 'string' => '\bff\validation\rules\IsString', 'json' => '\bff\validation\rules\Json', 'max' => '\bff\validation\rules\Max', 'min' => '\bff\validation\rules\Min', 'password' => '\bff\validation\rules\Password', 'phone' => '\bff\validation\rules\Phone', 'regexp' => '\bff\validation\rules\Regexp', 'required' => '\bff\validation\rules\Required', 'uuid' => '\bff\validation\rules\Uuid', 'version' => '\bff\validation\rules\Version', ]; /** * Construct * @param array|mixed $params * @param Validator|null $validator */ public function __construct($params = [], $validator = null) { $this->setValidator($validator); $this->setParams($params); $props = get_object_vars($this); foreach ($props as $key => $value) { if (is_null($value)) { throw new ValidationException(_t('system', 'Missing required parameter "[param]" in validation rule "[rule]"', [ 'param' => $key, 'rule' => static::class, ])); } } } /** * Set rule params * @param array|mixed $params * @throws ValidationException */ public function setParams($params) { $params = $this->defaults($params); $params = (array)$params; foreach ($params as $key => $value) { if (is_string($key)) { if (method_exists($this, $method = "set{$key}")) { if ($this->{$method}($value) === false) { throw new ValidationException(_t('system', 'Wrong parameter "[param]" in validation rule "[rule]"', [ 'param' => $key, 'rule' => static::class, ])); } } } } } /** * Set validator * @param Validator $validator * @return bool */ public function setValidator($validator) { if ($validator instanceof Validator) { $this->validator = $validator; return true; } return false; } /** * Get validator * @return Validator|null */ public function getValidator() { return $this->validator; } /** * Prepare default params * @param mixed $params * @return mixed */ protected function defaults($params) { return $params; } /** * Get rule name as string * @return string */ public function getName() { return mb_strtolower(basename(str_replace('\\', '/', static::class))); } /** * Rule factory * @param string|self $rule * @param array|mixed $params rule params * @param Validator|null $validator * @return bool|static */ public static function factory($rule, $params = [], $validator = null) { if (is_string($rule)) { $rule = mb_strtolower($rule); if (mb_strpos($rule, 'not') === 0) { # in, regexp, between $rule = mb_substr($rule, 3); $not = true; } $ruleClass = array_key_exists($rule, static::$registered) ? static::$registered[$rule] : false; if ($ruleClass !== false && class_exists($ruleClass)) { /** @var static $object */ $object = new $ruleClass($params, $validator); if (isset($not)) { $object->setNot($not); } return $object; } } elseif ($rule instanceof self) { return $rule; } elseif ($rule instanceof Closure) { $params['callback'] = $rule; return new static::$registered['callback']($params, $validator); } return false; } /** * Register new rule * @param string $name rule name * @param string $class rule class * @return bool */ public static function register($name, $class) { if (is_string($name)) { static::$registered[$name] = $class; return true; } return false; } /** * Check method * @param mixed $value * @return bool */ abstract public function check(&$value); /** * Set rule message * @param string $message * @param bool $force * @return bool */ public function setMessage($message, $force = true) { if ($force || empty($this->message)) { $this->message = $message; } return true; } /** * Get rule message * @param string $attribute attribute title * @return string */ public function getMessage($attribute = '') { if (! empty($this->message)) { $message = $this->message; } else { $message = $this->defaultMessage(); } $replace = ['[attribute]' => $attribute]; foreach ($this->replace as $k => $v) { $replace['[' . $k . ']'] = $v; } return strtr($message, $replace); } /** * Get rule message * @param string $message * @return static */ public function message($message) { $this->setMessage($message, true); return $this; } /** * Get rule message * @return string */ public function defaultMessage(): string { return _t('error', 'The [attribute] is invalid'); } /** * Add message replacer * @param string $key * @param mixed $value * @param bool $force * @return static */ public function replace($key, $value, $force = true) { $value = $this->fromClosure($value); if ( is_string($key) && ! empty($key) && is_scalar($value) && ($force || ! array_key_exists($key, $this->replace)) ) { $this->replace[$key] = $this->toString($value); } return $this; } /** * Set inverted * @param bool $not * @return bool */ protected function setNot($not) { $this->not = !empty($not); return true; } /** * Set inverted * @param bool $not * @return static */ public function not($not = true) { $this->setNot($not); return $this; } /** * Is inverted * @return bool */ public function isNot() { return ! empty($this->not); } /** * Stop validation in case the rule failed * @param bool $stop * @return bool */ protected function setStop($stop) { $this->stop = !empty($stop); return true; } /** * Do stop in case of failure * @return bool */ public function hasStop() { return $this->stop; } /** * Stop in case of failure * @param bool $stop * @return static */ public function stop($stop = true) { $this->setStop($stop); return $this; } /** * Callable to value converter * @param mixed $value * @return mixed */ protected function fromClosure($value) { return $value instanceof Closure ? $value() : $value; } /** * Convert value to number * @param mixed $value * @return int|bool */ protected function toNumber($value) { if (is_numeric($value)) { $value = intval($value); } elseif (is_string($value)) { $value = mb_strlen($value); } elseif (is_array($value)) { $value = count($value); } else { return false; } return $value; } /** * Safely string convertion * @param mixed $data * @param bool $trim trim string * @return string */ protected function toString($data, $trim = false) { # Safely convert arrays and object (without __toString) to empty strings if (is_null($data)) { $data = ''; } elseif (! is_scalar($data) && ! method_exists($data, '__toString')) { $data = ''; } else { $data = ($trim ? trim(strval($data)) : strval($data)); } return $data; } /** * Empty closure * @param Closure $callable * @return bool */ public function setIsEmpty($callable) { if ($callable instanceof Closure) { $this->isEmpty = $callable; return true; } return false; } /** * Checks if the given value is empty. * @param mixed $value * @return bool whether the value is empty */ public function isEmpty($value) { if ($this->isEmpty !== false && $this->isEmpty instanceof Closure) { return call_user_func($this->isEmpty, $value); } if (is_null($value)) { return true; } elseif (is_string($value) && trim($value) === '') { return true; } elseif ((is_array($value) || $value instanceof Countable) && ! count($value)) { return true; } return false; } }