c = $container; $this->reset(); } /** * Сбрасывает настройки к начальным состояниям * * @return Validator */ public function reset() { $this->validators = [ 'absent' => [$this, 'vAbsent'], 'array' => [$this, 'vArray'], 'checkbox' => [$this, 'vCheckbox'], 'file' => [$this, 'vFile'], 'image' => [$this, 'vImage'], 'in' => [$this, 'vIn'], 'integer' => [$this, 'vInteger'], 'max' => [$this, 'vMax'], 'min' => [$this, 'vMin'], 'numeric' => [$this, 'vNumeric'], 'not_in' => [$this, 'vNotIn'], 'password' => [$this, 'vPassword'], 'referer' => [$this, 'vReferer'], 'regex' => [$this, 'vRegex'], 'required' => [$this, 'vRequired'], 'required_with' => [$this, 'vRequiredWith'], 'same' => [$this, 'vSame'], 'string' => [$this, 'vString'], 'token' => [$this, 'vToken'], ]; $this->rules = []; $this->result = []; $this->arguments = []; $this->messages = []; $this->aliases = []; $this->errors = []; $this->fields = []; $this->status = []; return $this; } /** * Добавляет валидаторы * * @param array $validators * * @return Validator */ public function addValidators(array $validators) { $this->validators = \array_replace($this->validators, $validators); return $this; } /** * Добавляет правила * * @param array $list * * @throws RuntimeException * * @return Validator */ public function addRules(array $list) { foreach ($list as $field => $raw) { $suffix = null; // правило для элементов массива if (\strpos($field, '.') > 0) { list($field, $suffix) = \explode('.', $field, 2); } $rules = []; // перебор правил для текущего поля foreach (\explode('|', $raw) as $rule) { //???? нужно экранирование для разделителей $vs = \explode(':', $rule, 2); if (empty($this->validators[$vs[0]])) { try { $validator = $this->c->{'VL' . $vs[0]}; } catch (Exception $e) { $validator = null; } if ($validator instanceof Validators) { $this->validators[$vs[0]] = [$validator, $vs[0]]; } else { throw new RuntimeException($vs[0] . ' validator not found'); } } $rules[$vs[0]] = isset($vs[1]) ? $vs[1] : ''; } if (isset($suffix)) { $this->rules[$field]['array'][$suffix] = $rules; } else { $this->rules[$field] = $rules; } $this->fields[$field] = $field; } return $this; } /** * Добавляет дополнительные аргументы для конкретных "имя поля"."имя правила". * * @param array $arguments * * @return Validator */ public function addArguments(array $arguments) { $this->arguments = \array_replace($this->arguments, $arguments); return $this; } /** * Добавляет сообщения для конкретных "имя поля"."имя правила". * * @param array $messages * * @return Validator */ public function addMessages(array $messages) { $this->messages = \array_replace($this->messages, $messages); return $this; } /** * Добавляет псевдонимы имен полей для сообщений об ошибках * * @param array $aliases * * @return Validator */ public function addAliases(array $aliases) { $this->aliases = \array_replace($this->aliases, $aliases); return $this; } /** * Проверяет данные * * @param array $raw * * @throws RuntimeException * * @return bool */ public function validation(array $raw) { if (empty($this->rules)) { throw new RuntimeException('Rules not found'); } $this->errors = []; $this->status = []; $this->curData = []; $this->raw = $raw; foreach ($this->fields as $field) { $this->__get($field); } $this->raw = null; return empty($this->errors); } /** * Проверяет наличие поля * * @param string $field * * @return bool */ public function __isset($field) { return isset($this->result[$field]); } /** * Проверяет поле согласно заданным правилам * Возвращает значение запрашиваемого поля * * @param string * * @throws RuntimeException * * @return mixed */ public function __get($field) { if (isset($this->status[$field])) { return $this->result[$field]; } elseif (empty($this->rules[$field])) { throw new RuntimeException("No rules for '{$field}' field"); } $value = null; if (! isset($this->raw[$field]) && isset($this->rules[$field]['required'])) { $rules = ['required' => '']; } else { $rules = $this->rules[$field]; if (isset($this->raw[$field])) { $value = $this->c->Secury->replInvalidChars($this->raw[$field]); // пустое поле в соответствии с правилом 'required' должно быть равно null if ((\is_string($value) && 0 === \strlen(\preg_replace('%^\s+|\s+$%u', '', $value))) || (\is_array($value) && empty($value)) ) { $value = null; } } } $value = $this->checkValue($value, $rules, $field); $this->status[$field] = true !== $this->error; // в $this->error может быть состояние false $this->result[$field] = $value; return $value; } /** * Проверяет значение списком правил * * @param mixed $value * @param array $rules * @param string $field * * @return mixed */ protected function checkValue($value, array $rules, $field) { foreach ($rules as $validator => $attr) { // данные для обработчика ошибок $this->error = null; $this->curData[] = [ 'field' => $field, 'rule' => $validator, 'attr' => $attr, ]; $value = $this->validators[$validator]($this, $value, $attr, $this->getArguments($field, $validator)); \array_pop($this->curData); if (null !== $this->error) { break; } } return $value; } /** * Добавляет ошибку * * @param mixed $error * @param string $type * * @throws RuntimeException */ public function addError($error, $type = 'v') { if (empty($vars = \end($this->curData))) { throw new RuntimeException('The array of variables is empty'); } // нет ошибки, для выхода из цикла проверки правил if (true === $error) { $this->error = false; return; } \extract($vars); // псевдоним имени поля $alias = isset($this->aliases[$field]) ? $this->aliases[$field] : $field; // текст ошибки if (isset($this->messages[$field . '.' . $rule])) { $error = $this->messages[$field . '.' . $rule]; } elseif (isset($this->messages[$field])) { $error = $this->messages[$field]; } if (\is_array($error)) { $type = $error[1]; $error = $error[0]; } $this->errors[$type][] = \ForkBB\__($error, [':alias' => \ForkBB\__($alias), ':attr' => $attr]); $this->error = true; } /** * Возвращает дополнительные аргументы * * @param string $field * @param string $rule * * @return mixed */ protected function getArguments($field, $rule) { if (isset($this->arguments[$field . '.' . $rule])) { return $this->arguments[$field . '.' . $rule]; } elseif (isset($this->arguments[$field])) { return $this->arguments[$field]; } else { return null; } } /** * Возвращает статус проверки поля * * @param string $field * * @return bool */ public function getStatus($field) { if (! isset($this->status[$field])) { $this->__get($field); } return $this->status[$field]; } /** * Возвращает проверенные данные * Поля с ошибками содержат значения по умолчанию или значения с ошибками * * @param bool $all * * @throws RuntimeException * * @return array */ public function getData($all = false) { if (empty($this->status)) { throw new RuntimeException('Data not found'); } if ($all) { return $this->result; } else { return \array_filter($this->result, function ($value) { return null !== $value; }); } } /** * Возращает массив ошибок * * @return array */ public function getErrors() { return $this->errors; } /** * Выполняет проверку значения по правилу * * @param Validator $first ссылка на валидатор * @param mixed $second проверяемое значение * @param mixed $third атрибут правила * @param mixed $fourth дополнительный аргумент * * @return mixed */ protected function vAbsent(Validator $v, $value, $attr) { if (null !== $value) { $this->addError('The :alias should be absent'); } if (isset($attr{0})) { return $attr; } else { return null; } } protected function vRequired(Validator $v, $value) { if (\is_string($value)) { if (\strlen(\preg_replace('%^\s+|\s+$%u', '', $value)) > 0) { return $value; } } elseif (\is_array($value)) { if (! empty($value)) { return $value; } } elseif (null !== $value) { return $value; } $this->addError('The :alias is required'); return null; } protected function vRequiredWith(Validator $v, $value, $attr) //??????????????????????? { foreach (\explode(',', $attr) as $field) { if (null !== $this->__get($field)) { // если есть хотя бы одно поле, return $this->vRequired($v, $value); // то проверяем данное поле } // на обязательное наличие } if (null === $value) { // если данное поле отсутствует, $this->addError(true); // то прерываем его проверку } return $value; # list(, $error) = $this->vRequired($v, $value); # if (false === $error) { # return [null, 'The :alias is not required']; # } else { # return [$value, true]; # } } protected function vString(Validator $v, $value, $attr) { if (null === $value) { return null; } elseif (\is_string($value)) { foreach(\explode(',', $attr) as $action) { switch ($action) { case 'trim': $value = \preg_replace('%^\s+|\s+$%u', '', $value); break; case 'lower': $value = \mb_strtolower($value, 'UTF-8'); break; case 'spaces': $value = \preg_replace('%\s+%u', ' ', $value); break; } } return $value; } else { $this->addError('The :alias must be string'); return null; } } protected function vNumeric(Validator $v, $value) { if (null === $value) { return null; } elseif (\is_numeric($value)) { return 0 + $value; } else { $this->addError('The :alias must be numeric'); return null; } } protected function vInteger(Validator $v, $value) { if (null === $value) { return null; } elseif (\is_numeric($value) && \is_int(0 + $value)) { return (int) $value; } else { $this->addError('The :alias must be integer'); return null; } } protected function vArray(Validator $v, $value, $attr) { if (null !== $value && ! \is_array($value)) { $this->addError('The :alias must be array'); return null; } elseif (! $attr) { return $value; } if (empty($vars = \end($this->curData))) { throw new RuntimeException('The array of variables is empty'); } $result = []; foreach ($attr as $name => $rules) { $this->recArray($value, $result, $name, $rules, $vars['field'] . '.' . $name); } return $result; } protected function recArray(&$value, &$result, $name, $rules, $field) { $idxs = \explode('.', $name); $key = \array_shift($idxs); $name = \implode('.', $idxs); if ('*' === $key) { if (! \is_array($value)) { return; //???? } foreach ($value as $i => $cur) { if ('' === $name) { $result[$i] = $this->checkValue($cur, $rules, $field); } else { $this->recArray($value[$i], $result[$i], $name, $rules, $field); } } } else { if (! \array_key_exists($key, $value)) { return; //???? } if ('' === $name) { $result[$key] = $this->checkValue($value[$key], $rules, $field); } else { $this->recArray($value[$key], $result[$key], $name, $rules, $field); } } } protected function vMin(Validator $v, $value, $attr) { if (\is_string($value)) { $isBytes = \strpos($attr, 'bytes'); if (($isBytes && \strlen($value) < (int) $attr) || (! $isBytes && \mb_strlen($value, 'UTF-8') < $attr) ) { $this->addError('The :alias minimum is :attr characters'); } } elseif (\is_numeric($value)) { if (0 + $value < $attr) { $this->addError('The :alias minimum is :attr'); } } elseif (\is_array($value)) { if (\count($value) < $attr) { $this->addError('The :alias minimum is :attr elements'); } } elseif (null !== $value) { $this->addError('The :alias minimum is :attr'); return null; } return $value; } protected function vMax(Validator $v, $value, $attr) { if (\is_string($value)) { $isBytes = \strpos($attr, 'bytes'); if (($isBytes && \strlen($value) > (int) $attr) || (! $isBytes && \mb_strlen($value, 'UTF-8') > $attr) ) { $this->addError('The :alias maximum is :attr characters'); } } elseif (\is_numeric($value)) { if (0 + $value > $attr) { $this->addError('The :alias maximum is :attr'); } } elseif (\is_array($value)) { if (\reset($value) instanceof File) { $attr *= 1024; foreach ($value as $file) { if ($file->size() > $attr) { $this->addError('The :alias contains too large a file'); return null; } } } elseif (\count($value) > $attr) { $this->addError('The :alias maximum is :attr elements'); } } elseif ($value instanceof File) { if ($value->size() > $attr * 1024) { $this->addError('The :alias contains too large a file'); return null; } } elseif (null !== $value) { $this->addError('The :alias maximum is :attr'); //???? return null; } return $value; } protected function vToken(Validator $v, $value, $attr, $args) { if (! \is_array($args)) { $args = []; } if (! \is_string($value) || ! $this->c->Csrf->verify($value, $attr, $args)) { $this->addError('Bad token', 'e'); return null; } else { return $value; } } protected function vCheckbox(Validator $v, $value) { return null === $value ? false : (string) $value; } protected function vReferer(Validator $v, $value, $attr, $args) { if (! \is_array($args)) { $args = []; } return $this->c->Router->validate($value, $attr, $args); } protected function vSame(Validator $v, $value, $attr) { if (! $this->getStatus($attr) || $value === $this->__get($attr)) { return $value; } else { $this->addError('The :alias must be same with original'); return null; } } protected function vRegex(Validator $v, $value, $attr) { if (null !== $value && (! \is_string($value) || ! \preg_match($attr, $value)) ) { $this->addError('The :alias is not valid format'); return null; } else { return $value; } } protected function vPassword(Validator $v, $value) { return $this->vRegex($v, $value, '%[^\x20][\x20][^\x20]%'); } protected function vIn(Validator $v, $value, $attr) { if (null !== $value && ! \in_array($value, \explode(',', $attr))) { $this->addError('The :alias contains an invalid value'); } return $value; } protected function vNotIn(Validator $v, $value, $attr) { if (null !== $value && \in_array($value, \explode(',', $attr))) { $this->addError('The :alias contains an invalid value'); } return $value; } protected function vFile(Validator $v, $value, $attr) { if (null === $value) { return null; } if (! \is_array($value)) { $this->addError('The :alias not contains file'); return null; } $value = $this->c->Files->upload($value); if (null === $value) { return null; } elseif (false === $value) { $this->addError($this->c->Files->error()); return null; } elseif ('multiple' === $attr) { if (! \is_array($value)) { $value = [$value]; } } elseif (\is_array($value)) { $this->addError('The :alias contains more than one file'); return null; } return $value; } protected function vImage(Validator $v, $value, $attr) { $value = $this->vFile($v, $value, $attr); if (\is_array($value)) { foreach ($value as $file) { if (false === $this->c->Files->isImage($file)) { $this->addError('The :alias not contains image'); return null; } } } elseif (null !== $value && false === $this->c->Files->isImage($value)) { $this->addError('The :alias not contains image'); return null; } return $value; } }