OOP validators – класс для проверки параметров

OOP validators – класс для проверки параметров

Хотелось бы в отдельном топике обсудить данную тему. За основу для обсуждения взято сообщение Макса в Live Journal (http://www.livejournal.com/users/max_m/15107.html).

Итак, некоторый механизм для проверки параметров однозначно нужен. Когда появляется необходимость в проверке большого количества данных, стандартный подход становится неудобным и непрактичным.

Для начала нужно определиться с задачами, которые должен решать данный класс. Мне кажется, основной задачей является проверка данных, полученных от пользователя, посредством get / post /cookies.

При проверке данных чаще всего приходится проверять следующее:
1. Заполнено ли поле.
2. Не превышает ли длина поля максимальную длину.
3. Превышает ли длина поля минимальную длину.
4. Не содержит ли поле недопустимые символы.
5. Соответствует ли поле необходимому формату (рег. выр.).

Эти 5 проверок являются базовыми, и зачастую их хватает для решения практически любых задач. Если при добавлении правила для поля указывать его тайтл, можно автоматизировать составление сообщений, которые должны отображаться в случае непрохождения проверки. Например, общие для всего проекта сообщение для третей проверки может выглядеть так:

Поле %field_name заполнено неверно. Минимальная длина поля - %min_length, текущая длина поля - %field_lengh.

В классе Validator, я считаю, нет необходимости. Базовые проверки можно реализовать в самом классе ValidatorsChain, а для расширения функциональности использовать колбэк функции / методы, набор которых можно хранить в одном файле. Я бы не изобретал велосипед и обратил внимание на HTML_QuickForm.

В метод add передавать следующие параметры:
add(string $element, string $element_title, string $message, mixed $filter, mixed $param, integer $mask);

$element - имя параметра. Для одного и того же параметра можно добавлять несколько правил, они будут дополнятся, а не перезаписываться.

$element_title – тайтл параметра. Например, "E-mail", "Логин".

$message – сообщение. В тексте сообщения можно использовать оговоренные замены. Для базовых проверок можно не указывать.

$filter – имя одного из базовых фильтров. Если необходимы вызвать функцию или метод для проверки, указывается ссылка на массив, содержащий ссылку на объект или имя класса (для функции – пустое значение), а так же имя функции / метода.

$param – значение, которые передается в качестве параметра в фильтр. Для простых фильтров (мин / макс длина, соответствие регулярному выражению) может быть скаляром, для дополнительных – ссылкой на хеш или на массив.

-~{}~ 21.11.04 12:14:

Забыл еще один момент. При регистрации параметров значение нигде указывать не нужно. Ссылку на хеш, содержащий, параметры и соответствующие им значения можно передавать непосредственно в $chain->validate(). Это удобно, когда отдельно существует метод для получения такого хеша, или уже существует готовый хеш ($_REQUEST, например).
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Re: OOP validators – класс для проверки параметров

Автор оригинала: 2NetFly
В классе Validator, я считаю, нет необходимости. Базовые проверки можно реализовать в самом классе ValidatorsChain, а для расширения функциональности использовать колбэк функции / методы, набор которых можно хранить в одном файле. Я бы не изобретал велосипед и обратил внимание на HTML_QuickForm.
Я бы не обращал внимания на нынешний QuickForm, потому что щас его разработчики чешут репы на предмет того, что бы надо в нём поправить в плане проверки для версии 4.0

В частности потому, что запомнить сигнатуру подобного метода
В метод add передавать следующие параметры:
add(string $element, string $element_title, string $message, mixed $filter, mixed $param, integer $mask);
практически невозможно.
 
Re: Re: OOP validators – класс для проверки параметров

Можно создать набор методов, выполняющих те же задачи, а для любящих задавать все одной структурой, т.е. меня, написать метод обертку. Хотелось бы обсудить не формат вызова метода, а общую концепцию решения проблемы.

Я раньше использовал такой метод:
add(element, min_length, max_length, regexp, def_val, callback_func);
Но его уже не хватает.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Автор оригинала: 2NetFly
Хотелось бы обсудить не формат вызова метода, а общую концепцию решения проблемы.
Общую концепцию в применении к чему? В QuickForm v4 я пока вижу следующую структуру:
1) иерархия классов Rule, каждый из к-рых умеет проверять выполнение некоего правила и генерировать Javascript для проверки оного правила на клиенте.
2) экземпляры Rule привязываются к конкретным элементам формы:
PHP:
$field->addRule(new H_Q4_Rule_Required('Заполни поле, урод!'));
при этом здесь будет более сложная структура, чем простая последовательность правил, описанная у max_m. например, двоичное дерево, ветви к-рого будут связаны через AND и OR.
3) Есть класс Validator, реализующий паттерн Посетитель (т.е. работающий так же, как щас Renderer'ы в QuickForm)

Но это, ясный хобот, относится к QuickForm, где уже есть объекты элементов, "знающие" о себе много интересного: своё значение, как создать javascript для получения значения на стороне клиента и т.п.

теперь к твоему выступлению:
При проверке данных чаще всего приходится проверять следующее:
1. Заполнено ли поле.
2. Не превышает ли длина поля максимальную длину.
3. Превышает ли длина поля минимальную длину.
4. Не содержит ли поле недопустимые символы.
5. Соответствует ли поле необходимому формату (рег. выр.).
6. совпадает ли пароль в поле passwd со своим подтверждением в поле passwd_confirm
7. выбрано ли не более m (не менее m) чекбоксов

это весьма частые проверки и если твоё решение не поддерживает их автоматизацию --- с решением что-то не то.

В классе Validator, я считаю, нет необходимости. Базовые проверки можно реализовать в самом классе ValidatorsChain, а для расширения функциональности использовать колбэк функции / методы, набор которых можно хранить в одном файле.
Да, только такой подход не имеет ничего общего с "хорошим" ООП. Надо объяснять почему?

Вообще вот ещё интересный тред для изучения: http://www.sitepoint.com/forums/showthread.php?t=204993

и перекину-ка я это в форум для продвинутых.
 
QuickForm это не совсем то, что нужно. Меня, да и думаю Макса, интересует класс, который сможет проверять параметры и "сообщать" о найденных ошибках, если таковые будут. Конструировать формы и, как следствие, автоматизировать проверку данных на стороне клиента он не должен.

>> двоичное дерево, ветви к-рого будут связаны через AND и OR.
Согласен, это решение, которое позволит производить сложные проверки. Однако, класс должен позволять легко описывать правила для тривиальных проверок.

>> теперь к твоему выступлению ... с решением что-то не то.
Мое решение сейчас вообще ничего не поддерживает %) Как я уже писал выше, я использую add(element, min_length, max_length, regexp, def_val, callback_func), что меня уже не устраивает. Замечания касательно пунктов 6 и 7 верные, но, как и первые 5 пунктов можно реализовать соответствующие проверки в самом ValidatorsChain.

>> Да, только такой подход не имеет ничего общего с "хорошим" ООП.
Согласен. Но нужно решить, что мы хотим сделать: ООП правильное решение, или механизм, который облегчит нам жизнь. Это я к тому, что если для описания правил для одного параметра нужно будет вызвать пять методов AddRule, каждому из которого передавать экземпляр объекта Validator, то для 10 параметров правильность такого решения будет вызывать сомнения. Имхо, конечный механизм должен предоставлять возможность простые задачи решать просто, но и уметь решать сложные задачи (где-то я это уже слышал %).

В принципе, мне тоже импонирует решение с использованием класса Validator (используя его можно закодировать сообщение по умолчанию и сделать все то, что я описывал в перовом сообщении). Но оно должно быть более приспособлено для проверки полученных от пользователя параметров.

Например, как я уже говорил выше, чаще всего перед выполнением проверки уже существует хеш, содержащий параметры и их значения, посему вот такая вот запись мне кажется несколько неправильной:
$chain->add('id', new Validator_Smth($param["id"], '... ');
$chain->add('sid', new Validator_Smth($param["pid"], '... ');
$chain->add('pid', new Validator_Smth($param["sid"], '... ');
Но это уже мелочи %)
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Автор оригинала: 2NetFly
>> Да, только такой подход не имеет ничего общего с "хорошим" ООП.
Согласен. Но нужно решить, что мы хотим сделать: ООП правильное решение, или механизм, который облегчит нам жизнь. Это я к тому, что если для описания правил для одного параметра нужно будет вызвать пять методов AddRule, каждому из которого передавать экземпляр объекта Validator, то для 10 параметров правильность такого решения будет вызывать сомнения.
у меня никаких сомнений в правильности почему-то не возникает. что именно тебя напрягает --- необходимость написать 5 addRule для 5 никак между собой не связанных проверок? а искуственно связать 5 этих проверок в одну --- нормально?

Имхо, конечный механизм должен предоставлять возможность простые задачи решать просто, но и уметь решать сложные задачи (где-то я это уже слышал %).
Сделать простую оболочку вокруг гибкого и сложного решения --- как два пальца. А вот "простое" и "оптимизированное" решение можно только выкинуть нах. и переписать заново.

Например, как я уже говорил выше, чаще всего перед выполнением проверки уже существует хеш, содержащий параметры и их значения, посему вот такая вот запись мне кажется несколько неправильной:
$chain->add('id', new Validator_Smth($param["id"], '... ');
$chain->add('sid', new Validator_Smth($param["pid"], '... ');
$chain->add('pid', new Validator_Smth($param["sid"], '... ');
Но это уже мелочи %)
ну это действительно просто мелкий недочёт, легко лечащийся
PHP:
$chain->add('id1', new Validator_Smth(...));
$chain->add('id2', new Validator_Smth(...));
$chain->add('id1', new Validator_Smth(...));
$chain->validate($hash);
 
>> а искуственно связать 5 этих проверок в одну --- нормально?
Не особо =) С другой стороны, никто не мешает унаследовать Validator и создать какой-нибудь Validator_Common, выполняющий 2-3 проверки, которые часто описываются для каждого параметра.

>> Сделать простую оболочку вокруг гибкого и сложного решения --- как два пальца.
Возразить нечего.

>> ну это действительно просто мелкий недочёт, легко лечащийся
Выше писал: "Ссылку на хеш, содержащий, параметры и соответствующие им значения можно передавать непосредственно в $chain->validate(). Это удобно, когда отдельно существует метод для получения такого хеша, или уже существует готовый хеш ($_REQUEST, например)". С другой стороны, хотелось бы иметь возможность использовать классы Validator_* без ValidatorsChain т.е. для проверки единичных значений.
PHP:
$v = new Validator_Smth($var, ...);|
if (!$v->validate()) { ... }
-~{}~ 21.11.04 18:50:

В голову пришла разве что передача null, когда валидация должна проходить на основе параметров, содержащихся в хеше.

-~{}~ 21.11.04 21:07:

>> Вообще вот ещё интересный тред для изучения ...
Там есть ссылка ни интересную статью http://radio.weblogs.com/0124037/2004/02/03.html .
 
Готовых фреймворков существует не одна сотня и, тем ни менее, каждый пытается изобрести что-то свое. Не вижу в этом ничего плохого %)
 

Crazy

Developer
Одни изобретают свое потому, что их не устраивает готовое. Другие -- от незнания того, что уже сделано другими. Это две большие разницы, как говорят в Одессе.

Хотя если мсье этой разницы замечать не хочет, то не буду настаивать.
 
Переведенная вами ссылка содержит неверный запрос. Дело в том, что мы обсуждаем проверку параметров полученных не только от пользователя и не только посредством форм. Но это все оффтопик %)
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Автор оригинала: Crazy
Одни изобретают свое потому, что их не устраивает готовое. Другие -- от незнания того, что уже сделано другими. Это две большие разницы, как говорят в Одессе.
Лично я в данный момент размышляю над улучшением имеющегося велосипеда --- трещоток там навесить, в кислотный цвет покрасить, седло со спинкой приладить.

Поэтому был бы благодарен за ссылки на конкретные --- удачные на твой взгляд --- модели велосипедов...
 
Кстати, как думаете, насколько рациональным будет совмещение механизма проверки с механизмом изменяющих-значения-параметров-фильтров? Фильтры можно реализовать таким же образом, как и валидаторы (наследовать абстрактный класс Filter), с возможность их использования как для отдельно взятых значений, так и для цепочек. Можно использовать общий класс-цепочку для валидаторов и для фильтров с применением последних только в случае успешной проверки всех параметров.

-~{}~ 27.11.04 23:30:

По поводу валидации параметров вне классов модели. Недавно вдумчиво читал документацию к php.MVC (порт Struts). Так вот там ребята используют механизм предварительной проверки параметров (заполнено ли поле, мин / макс длинна, соответствие выражению и т.д.), затем, в случае успешной валидации, передают управление непосредственно обработчику команд в котором выполняются все специальный проверки (существует ли объект с указанным ID, уникальный ли e-mail и т.д.). Оказывается, распространенная практика.
 

syfisher

TDD infected!!
Неплохой пример валидаторов есть в WACT-е.

1) Клиент добавляет необходимые правила в валидатор.
2) Валидатор получает объекта класса Datasource, который нужно проверить.
3) Далее этот Datasource передается по ссылке каждому правилу.
4) Ошибки складывают в общий объект класса errors_list. Сами ошибки представляют собой набор константа + параметры, для того, чтобы из errors_list позже сделать локализованный список.
5) Если хотябы одно правило не выполнилось, то валидация не прошла.

Реализация очень интересна, есть тесты.

...затем, в случае успешной валидации, передают управление непосредственно обработчику команд в котором выполняются все специальный проверки (существует ли объект с указанным ID, уникальный ли e-mail и т.д.).
Я думаю, что и для данных проверок нужно использовать тот же самый механизм, только он уже будет сильно зависеть от конкретных обстоятельств. На эту тему мне очень понравился один шабон - Specification (DDD by Evans).
 
Недавно возникла необходимость в обсуждаемом классе, пришлось наконец-то реализовать =)

Сделал таким образом:
1. Класс RuleBase. В конструктор передается ссылка на массив параметров, и значение переменной, проверку которой нужно производить. Так же реализован методы setValue и абстрактный метод validate.
2. Класс Validator с методами addRule, validate и т.д.

Все пользовательские правила наследуют RuleBase. Каждый класс может производить одну или более проверок, а метод validate возвращает 0, если все проверки пройдены успешно, или ссылку на массив, содержащий строки (константны), указывающие на непройденные проверки.

Например, RuleLength.
PHP:
$v = new RuleLength(array(5, 10), $val);
$code = $v->validate();
Если $val имеет длину от 5 до 10 символов, метод вернет 0, иначе ссылку на массив строк (например, ('vl_length_min')). Такой подход позволяет возвращать методу проверки более одного значения, а так же не обременять Rule работой с сообщениями об ошибках.

После регистрации правил в Validator необходимо установить тексты сообщений для каждой ошибки. Я просто передаю хеш, ключи которого являются строками (константами) возвращаемыми методом validate классов Rule*, а значения - собственно тексты сообщений.

-~{}~ 13.12.04 23:22:

Привел класс в порядок и написал по этому поводу что-то вроде небольшой статьи: http://feotast.net/validator.html . Примеры, правда, на преле, но пользователи PHP, думаю, без труда их поймут (если что, можно пропустить реализацию классов). Сейчас в оффтопике создам отдельную тему для обсуждения.

-~{}~ 17.12.04 21:34:

Автор оригинала: syfisher
Неплохой пример валидаторов есть в WACT-е.

Реализация очень интересна, есть тесты.
Неплохой пример валидаторов есть в WACT-е.
Детально изучил несколько дней назад механизм валидации. В принципе, я пришел к такому же решению, вот только "знание" правил о Datasource и ErrorsList мне кажется неправильным. Правила должны принимать уже реальные значения для проверки (не имена полей), а возвращать массив кодов (на языке WACT - id). Так же правила должны предоставлять методы getArgs и getValues, который возвращают хеши, который могут использоваться при формировании текста сообщений. Как и в WACT, валидатору передается объект Datasource, однако вместо передачи его по ссылке каждому из правила, он, на основе данных переданных в addRule, получает значения нужных полей и передает правилам непосредственно их. Валидатор получает от каждого правила массив кодов ошибок и добавляет ошибки в общий ErrorsList (для получения параметров и аргументов используются методы getArgs и getValues правил).

Такая реализация позволяет переместить логику работы с правилами из множества классов-правил в один класс Validator, а правила использовать независимо от валидатора для проверки единичных значений. Кроме того, лично меня раздражает необходимость подключений большого количества классов-правил и создание вручную объектов, поэтому запись:
PHP:
$validator->addRule('name', 'LengthRange', [3, 10]);
Мне нравится больше, чем:
PHP:
$validator->addRule('name', WCP::Rule::LengthRange->new(3, 10));
Вообще мне показалось, что механизм валидации в WACT-е написан так, как будто его писали не на PHP, а на языке со строгой типизацией (Java например) и такое решение "притянуто за уши".
 

syfisher

TDD infected!!
Jeff Moore, создатель WACT, в прошлом занимался именно Java программированием, причем в очень крупном проекте. Зато у него огромный опыт проектирования и тестирования.

Такая реализация позволяет переместить логику работы с правилами из множества классов-правил в один класс Validator, а правила использовать независимо от валидатора для проверки единичных значений. Кроме того, лично меня раздражает необходимость подключений большого количества классов-правил и создание вручную объектов, поэтому запись:
Мы передаем не экземпляры классов, а только так называется handlers на них. Например:

PHP:
function _init_validator()
{
  parent :: _init_validator();

  $this->validator->add_rule($v[] = array(LIMB_DIR . 'core/lib/validators/rules/required_rule', 'annotation'));
  $this->validator->add_rule($v[] = array(LIMB_DIR . 'core/lib/validators/rules/required_rule', 'image_id'));
}
То есть правила работает с Datasource (в данном случае, это так называемый TransactionObject в терминологии j2eepatterns) и ErrorList - не так и страшно, даже мне кажется более верным.

Дело в том, что правила могут быть более сложными, чем проверка на длину поля. Это могут быть бизнес правила, а форма ошибки - весьма произвольная и одними кодами здесь не обойтись. Плюс правила, реализованные таким способом, очень легко тестировать, а это ИМХО главный фактор в любом проектировании.

Не хочу вступать в полемику, твой вариант тоже неплохой. У нас был такой же вариант, просто мы решили отказаться от своих валидаторов в пользу тех, что идут с WACT для уменьшения кол-ва поддерживаемого кода.

Плюс, заметь, что ErrorList состоит из констант ошибок и параметров, далее он передается в ErrorDictionary для преобразования в строки и локализации.

-~{}~ 18.12.04 12:47:

Плюс еще можно задавать, откуда брать правила. Это очень удобно, так как LIMB-зависимые правила у нас лежат в одном месте, а механиз валидации плюс базовые правила - в другом.
 
Jeff Moore, создатель WACT, в прошлом занимался именно Java программированием, причем в очень крупном проекте. Зато у него огромный опыт проектирования и тестирования.
Как я написал выше, это сразу же заметно =) Если бы я писал на Java, думаю, тоже пришел бы к такому же решению.

ErrorList - не так и страшно, даже мне кажется более верным. Дело в том, что правила могут быть более сложными, чем проверка на длину поля. Это могут быть бизнес правила, а форма ошибки - весьма произвольная и одними кодами здесь не обойтись. Плюс правила, реализованные таким способом, очень легко тестировать, а это ИМХО главный фактор в любом проектировании.
У меня не одни коды. Метод правил getArgs возвращается хеш аргументов, а метод getValues – хеш значений. Например:
PHP:
sub getArgs
{
  my $self = shift;
  return ( min_length => $self->{min_length}, max_length => $self->{min_length} );
}

sub getValues
{
  my $self = shift;
  return ( value => $self->{value} );
}
В случае, если правило вернуло ложное значение, валидатор просматривает массив кодов, для каждого кода создает объект ErrorCode, заполняет поля code, group, args (на основе getArgs), values (на основе getValues), и fields и добавляет в ErrorList. То же самое, что в WACT, только логика работы с ошибками сокрыта в классе Validator, а не в классах-правилах.

Не хочу вступать в полемику, твой вариант тоже неплохой. У нас был такой же вариант, просто мы решили отказаться от своих валидаторов в пользу тех, что идут с WACT для уменьшения кол-ва поддерживаемого кода.
Наоборот, мне интересны любые недостатки моей реализации и преимущества WACT. Я пока еще не перешел на новый механизм валидации, поэтому не поздно все переписать =)

Плюс, заметь, что ErrorList состоит из констант ошибок и параметров, далее он передается в ErrorDictionary для преобразования в строки и локализации.
У меня такая же фишка, как формируется ErrorList я описал выше. Есть класс-хранилище сообщений, унаследовав которое и переопределив метод получения текстов сообщений, можно привязать его к любому источнику данных, а есть класс, который на основе хранилища и ErrorList создает структуру, которая может непосредственно использоваться в шаблоне.

От FieldNameDictionary я пока отказался (хотя в ErrorList лист имена полей храню). Тайтлы полей я пока не использую, т.к. вывожу сообщение об ошибке под инпутом, которое было неверно заполнено. Т.е. вместо "Поле имя содержит недопустимые символы", я пишу |Поле содержит недопустимые символы" и вывожу его под соответствующим полем. Таким образом, я определяю всего по одному сообщению для каждого кода ошибки.

-~{}~ 18.12.04 19:08:

В общем, сегодня проломал голову над реализацией правил =) Как я уже писал выше, хочу передавать в правила не ссылку на Datasource, а конкретные значения для проверки, чтоб правила можно было использовать независимо от валидатора. В итоге пришел к интересному решению:

PHP:
my $rule = WCP::Validator::Rule::MathRegexp->new('^\d+$');
$rule->validate('1234');
При добавлении правила валидатор создает экземпляр объекта нового класса и передает ему параметры. Значения для проверки на данном этапе недоступны т.к. объект Datasource передается в метод validate, который вызывается после установки всех правил. При вызове метода validate валидатора, он последовательно вызывает метод validate всех правил, передавая значения, полученные из Datasource по имеющимся именам полей.

Таким образом, во-первых происходит логическое разделение на аргументы и значения, которые должны быть проверены, а во-вторых если использовать правила отдельно, то на основе одних и тех же аргументов можно выполнить проверку нескольких значений %)
 

slego

Новичок
Господа! Вы скажите, вот. Сколько уже инфы по валидаторам просмотрел и нигде не видел, чтобы кого-то беспокоила следующая проблема (может я один такой?):
Допустим, нужно проверить параметр на то, что:
1. Это строка.
2. Она не пустая.
3. Соответствует регуляному выражению.
Что проверить - не важно, ключевой момент здесь то, что нужно построить цепочку проверок:

PHP:
$hash['first_name'] = "blah-blah-blah";
$hash['last_name'] = "blah-blah-blah";

$vld = new Validate();
$vld->addRule("first_name", new StringValidator());
$vld->addRule("first_name", new LengthsValidator(array(5,20)));
$vld->addRule("first_name", new RegexpValidator("^[a-zA-Z\d]$")
);
if( !$vld->validate($hash))
{
   $vld->getErrors();
}
Ок. Организовали цепочку. Все красиво, все прекрасно, все работает. Но!
Допустим, у нашего $hash'a имеется еще с десяток ключей, причем 5 из них нужно подвергнуть такой же проверке, как и 'first_name'.
Получается для каждого я опять должен буду дописать 3 правила. Пэчально :(
Согласен, можно какой-то цикл организовать по этим пяти параметрам, но это как-то не кузяво.
Итак, к чему я клоню. Может быть никто и не поднимает этот вопрос, потому что это очевидно? Мне показалось, что хорошы бы создать ГРУППЫ ПРОВЕРОК. Т.е. сделать допустим так как-то (привожу только приблизительную (читай корявую) схему)

PHP:
class SafeStringValidator extends ValidatorRule
{
     public function validate($val)
     {
        ...new StringValidator();
        ...new LengthsValidator(array(5,20); 
        ...new RegexpValidator("^[a-zA-Z\d]$");
     }    
}

$vld->addRule('first_name', new SafeStringValidator() );
$vld->addRule('last_name', new SafeStringValidator() );
$vld->validate($hash);
Что скажете?
 

itprog

Cruftsman
slego

Во-первых, зачем проверять что параметр строка, когда у нас есть для этого регулярка (пункт 3)
Во-вторых, зачем проверять длину отдельно, когда можно проверить ее регуляркой?
Получается что-то типа:
PHP:
$vld = new Validate(); 
$vld->addRule("first_name", new RegexpValidator("^[a-zA-Z\d]{5,20}$")
-~{}~ 16.06.05 14:12:

Можно еще сделать так:
PHP:
$vld->addRule(array("first_name","last_name"), new RegexpValidator("^[a-zA-Z\d]$")
 
itprog, а если на каждую из ошибок (регулярное выражение, длина) нужно выводить свое сообщение?
 
Сверху