Zend\Validator предоставляет набор наиболее востребованных валидаторов, но неизбежна ситуация, когда разработчикам понадобиться написать собственные валидаторы для конкретных нужд. Задача создания собственных валидаторов описана в этом разделе.
В Zend\Validator\ValidatorInterface определены два метода, isValid() и getMessages(), которые должны быть реализованы пользовательскими классами для создания собственных объектов валидации. Объект, который реализует интерфейс Zend\Validator\AbstractValidator, может быть добавлен в цепочку валидаторов с помощью Zend\Validator\ValidatorChain::addValidator(). Такие объекты могут быть также использованы с Zend\Filter\Input.
Как вы уже поняли из приведенного выше описания Zend\Validator\ValidatorInterface, классы валидации, которые предоставляет Zend Framework, возвращают логическое значение того, успешна или нет проверка некоторого значения. Они также предоставляют информацию о том, почему значение не прошло проверку. Наличие причин провала валидации может быть полезно в приложении для различных целей, таких, как предоставление статистики для удобства анализа.
Базовая функциональность сообщений об ошибках валидации реализована в Zend\Validator\AbstractValidator. Чтобы включить эту функциональность при создании класса валидации, просто наследуйте Zend\Validator\AbstractValidator. В дочернем классе вы можете реализовать логику метода isValid() и определить переменные сообщений и шаблоны сообщений, соответствующие типам ошибок валидации, которые могут возникнуть. Если значение не проходит проверку, то isValid() должен возвращать FALSE. Если значение проходит проверку, то isValid() должен возвращать TRUE.
В основном, метод isValid() не должен бросать исключений, кроме случаев, когда нельзя определить, является ли входное значение валидным, или нет. Вот несколько примеров, когда бросок исключения может быть обоснован: если файл не может быть открыт, если не удается соединиться с сервером LDAP, если соединение с базой данных недоступно, где такие вещи могут потребоваться для определения успешной или неудачной валидации.
Создание простого класса валидации
Следующий пример демонстрирует создание простейшего валидатора. В этом случае правила валидации просто состоят в том, что входное значение должно быть числом с плавающей запятой.
class MyValid\Float extends Zend\Validator\AbstractValidator { const FLOAT = 'float'; protected $messageTemplates = array( self::FLOAT => "'%value%' is not a floating point value" ); public function isValid($value) { $this->setValue($value); if (!is_float($value)) { $this->error(self::FLOAT); return false; } return true; } }
Класс определяет шаблон для единственного сообщения об ошибке валидации, который включает встроенный магический параметр, %value%. Вызов setValue() автоматически подготавливает объект для вставки проверяемого значения в сообщение об ошибке, если это значение не пройдет проверку. Вызов error() устанавливает причину сбоя проверки. Так как в этом классе определено только одно сообщение об ошибке, то не нужно передавать в error() имя шаблона сообщения об ошибке.
Создание классов валидации, имеющих подчиненные условия
(исполнение которого не может быть потребовано до исполнения другого,
предварительного условия)
Следующий пример демонстрирует более сложный набор правил валидации, в котором требуется, чтобы входное значение было числом, а также находилось в диапазоне минимального и максимального пороговых значений. Входное значение может не пройти валидацию по одной из следующих причин:
• входное значение не является числом.
• входное значение меньше минимально допустимого значения.
• входное значение больше максимально допустимого значения.
Эти причины ошибок валидации преобразованы в константы класса:
class MyValid\NumericBetween extends Zend\Validator\AbstractValidator { const MSG_NUMERIC = 'msgNumeric'; const MSG_MINIMUM = 'msgMinimum'; const MSG_MAXIMUM = 'msgMaximum'; public $minimum = 0; public $maximum = 100; protected $messageVariables = array( 'min' => 'minimum', 'max' => 'maximum' ); protected $messageTemplates = array( self::MSG_NUMERIC => "'%value%' is not numeric", self::MSG_MINIMUM => "'%value%' must be at least '%min%'", self::MSG_MAXIMUM => "'%value%' must be no more than '%max%'" ); public function isValid($value) { $this->setValue($value); if (!is_numeric($value)) { $this->error(self::MSG_NUMERIC); return false; } if ($value < $this->minimum) { $this->error(self::MSG_MINIMUM); return false; } if ($value > $this->maximum) { $this->error(self::MSG_MAXIMUM); return false; } return true; } }
Публичные свойства $minimum и $maximum были созданы, чтобы обеспечить минимальный и максимальный пороги, для которых значение успешно проходит валидацию. В классе также определены две переменные сообщений, которые соответствуют публичным свойствам и позволяют использовать min и max в шаблонах сообщений в качестве магических параметров, как и value.
Заметим, что если любая из проверок в методе isValid() будет провалена, это подготовит соответствующее сообщение об ошибке, и метод немедленно вернет FALSE. Поэтому такие правила валидации являются последовательно-зависимыми. То есть, если одна из проверок будет провалена, нет необходимости производить последующие проверки. Однако, это не обязательно. Следующий пример иллюстрирует, как создать класс с независимыми правилами проверки, чей объект валидации может возвращать несколько причин, по которым конкретная валидация не удалась.
Создание классов валидации с взаимно не связанными условиями
и множеством допустимых причин для сбоя
Рассмотрим написание класса валидации для обеспечения стойкости пароля - когда пользователю необходимо выбрать пароль, который соответствует определенным критериям в целях защиты учетных записей пользователей. Предположим, что критерии безопасности требуют от пароля:
• минимум 8 символов,
• содержать хотя бы одну букву в верхнем регистре,
• содержать хотя бы одну букву в нижнем регистре,
• содержать хотя бы одну цифру.
Следующий класс реализует эти критерии проверки:
class MyValid\PasswordStrength extends Zend\Validator\AbstractValidator { const LENGTH = 'length'; const UPPER = 'upper'; const LOWER = 'lower'; const DIGIT = 'digit'; protected $messageTemplates = array( self::LENGTH => "'%value%' must be at least 8 characters in length", self::UPPER => "'%value%' must contain at least one uppercase letter", self::LOWER => "'%value%' must contain at least one lowercase letter", self::DIGIT => "'%value%' must contain at least one digit character" ); public function isValid($value) { $this->setValue($value); $isValid = true; if (strlen($value) < 8) { $this->error(self::LENGTH); $isValid = false; } if (!preg_match('/[A-Z]/', $value)) { $this->error(self::UPPER); $isValid = false; } if (!preg_match('/[a-z]/', $value)) { $this->error(self::LOWER); $isValid = false; } if (!preg_match('/\d/', $value)) { $this->error(self::DIGIT); $isValid = false; } return $isValid; } }
Заметьте, что эти четыре проверки в методе isValid() не возвращают немедленно FALSE. Это позволяет классу валидации предоставить все причины того, почему введенный пароль не отвечает требованиям валидации. Например, если пользователь введет строку “#$%” в качестве пароля, isValid() будет обуславливать все четыре сообщения об ошибке валидации, чтобы вернуть их последующим вызовом getMessages().