CGI::Ex::Validate(3)
okay bestie, welcome to the CGI::Ex::Validate(3) man page from User Contributed Perl Documentation ๐ this is part 1 of 6 so we're not done yet, but you're already in the right place ๐
โจ NAME โจ
CGI::Ex::Validate โ The "Just Right" form validator with javascript in parallel ๐
๐ธ VERSION ๐ธ
version 2.55 โจ
๐ฆ SYNOPSIS ๐ฆ
use CGI::Ex::Validate;
# THE SHORT
my $errobj = CGI::Ex::Validate->new->validate($form, $val_hash);
# THE LONG
my $form = CGI->new;
# OR #
my $form = CGI::Ex->new; # OR CGI::Ex->get_form;
# OR #
my $form = {key1 => 'val1', key2 => 'val2'};
# simplest
my $val_hash = {
'group order' => [qw(username email email2)],
username => {
required => 1,
max_len => 30,
field => 'username',
# field is optional in this case - will use key name
},
email => {
required => 1,
max_len => 100,
type => 'email',
},
email2 => {
equals => 'email',
},
};
# ordered
my $val_hash = {
'group order' => [{
field => 'username', # field is not optional in this case
required => 1,
max_len => 30,
}, {
field => 'email',
required => 1,
max_len => 100,
}, {
field => 'email2',
equals => 'email',
}],
};
my $vob = CGI::Ex::Validate->new;
my $errobj = $vob->validate($form, $val_hash);
if ($errobj) {
# get errors back in any of several useful ways
my $error_heading = $errobj->as_string; # OR "$errobj";
my $error_list = $errobj->as_array; # ordered list of what when wrong
my $error_hash = $errobj->as_hash; # hash of arrayrefs of errors
} else {
# the form passed validation
}
my $js_uri_path = '/js/'; # static or dynamic URI path to find CGI/Ex/validate.js
my $form_name = "the_form"; # name of the form to attach javascript to
# generate javascript to validate an existing form
my $javascript = $vob->generate_js($val_hash, {
form_name => $form_name,
js_uri_path => $js_uri_path,
});
# OR let Validate create the form and javascript for you
my $form = $vob->generate_form($val_hash, {
form_name => $form_name, # will use a random name if not passed
js_uri_path => $js_uri_path,
});
๐ DESCRIPTION ๐
okay so CGI::Ex::Validate is one of many validation modules out there, but it's lowkey different. It's trying to have all of the basic data validation functions, and it's deliberately NOT adding all of the millions of possible types, while still giving you the capability to add your own types for those rare cases where the basic ones don't suffice. Honestly, anything more than basic validation probably needs programmatic or data based validation anyway ๐ซถ
It also has full support for providing the same validation in javascript. It gives you methods for attaching the javascript to existing forms. This ability is tightly integrated into CGI::Ex::App, but it should be easy to add validation just about anywhere using any type of controller โจ
As opposed to other kitchen sync validation modules, CGI::Ex::Validate offers the simple types of validation, and makes it easy to add your own custom types. Aside from custom and custom_js, all validation markup is declarative ๐
๐ METHODS ๐
๐ new
Used to instantiate the object. Arguments are either a hash, or a hashref, or nothing at all. Keys of the hash become the keys of the object ๐ฅ
๐ get_validation
Uses CGI::Ex::Conf::conf_read to read in the hash. conf_read will allow passing a filename or YAML string or a hashref ๐ธ
๐ get_validation_keys
Takes the validation hashref returned from get_validation. Will return all of the possible keys found in the validation hashref. This can be used to check to see if extra items have been passed to validate. If a second argument contains a form hash is passed, get_validation_keys will only return the keys of groups that were validated ๐ฆ
my $key_hashref = $self->get_validation_keys($val_hash);
The keys of the hash are the names of the fields.
๐ validate
Arguments are a form hashref or cgi object, a validation hashref or filename, and an optional what_was_validated arrayref (discussed further later on). If a CGI object is passed, CGI::Ex::get_form will be called on that object to turn it into a hashref. If a filename is given for the validation, get_validation will be called on that filename. If the what_was_validated_arrayref is passed - it will be populated (pushed) with the field hashes that were actually validated (anything that was skipped because of validate_if will not be in the array) ๐
If the form passes validation, validate will return undef. If it fails validation, it will return a CGI::Ex::Validate::Error object. If the 'raise_error' option has been set, validate will die with a CGI::Ex::validate::Error object as the value ๐ญ
my $err_obj = $self->validate($form, $val_hash);
# OR #
$self->{raise_error} = 1; # can also be listed in the val_hash
eval { $self->validate($form, $val_hash) };
if ($@) { my $err_obj = $@; }
๐ generate_form
Takes a validation hash, and additional arguments and generates an HTML form suitable for inclusion in a web based application ๐
my $html = $self->generate_form($val_hash, {
form_name => 'my_form',
js_uri_path => '/cgi-bin/js', # will be used by generate_js
});
๐ generate_js
Works with CGI::Ex::JSONDump.
Takes a validation hash, a form name, and an optional javascript uri path and returns Javascript that can be embedded on a page and will perform identical validations as the server side. The form name must be the name of the form that the validation will act upon - the name is used to register an onsubmit function. The javascript uri path is used to embed the locations of javascript source files included with the CGI::Ex distribution โจ
The javascript uri path is highly dependent upon the server configuration and therefore must be configured manually. It may be passed to generate_js, or it may be specified in $JS_URI_PATH. There is one file included with this module that is needed - CGI/Ex/validate.js. When generating the js code, generate_js will look in $JS_URI_PATH_VALIDATE. If this is not set, generate_js will use "$JS_URI_PATH/CGI/Ex/validate.js" ๐
my $js = $self->generate_js($val_hash, 'my_form', "/cgi-bin/js")
# OR
my $js = $self->generate_js($val_hash, {
form_name => 'my_form',
js_uri_path => '/cgi-bin/js',
});
# would generate something like the following...
<script src="/cgi-bin/js/CGI/Ex/validate.js"></script>
... more js follows ...
$CGI::Ex::Validate::JS_URI_PATH = "/stock/js";
$self->generate_js($val_hash, 'my_form')
# would generate something like the following...
<script src="/stock/js/CGI/Ex/validate.js"></script>
... more js follows ...
Referencing validate.js can be done in any of several ways. It can be copied to or symlinked to a fixed location in the server's html directory. It can also be printed out by a cgi. The method "->print_js" has been provided in CGI::Ex for printing js files found in the perl hierarchy. See CGI::Ex for more details. The $JS_URI_PATH of "/cgi-bin/js" could contain the following ๐
#!/usr/bin/perl -w
use strict;
use CGI::Ex;
# path_info should contain something like /CGI/Ex/validate.js
my $info = $ENV{PATH_INFO} || '';
die "Invalid path" if $info !~ m|^(/\w+)+.js$|;
$info =~ s|^/+||;
CGI::Ex->new->print_js($info);
exit;
The print_js method in CGI::Ex is designed to cache the javascript in the browser ๐
๐ ->cgix
Returns a CGI::Ex object. Used internally if a CGI object is passed to validate rather than a straight form hash ๐ซถ
๐ VALIDATION HASH ๐
Okay so the validation hash โ bestie, listen โ you can hand it over as a hashref, or as a filename, or as a YAML document string. But like, experience has shown it's genuinely better programming to just pass in a hashref. Trust. If your validation "hash" is a filename or a YAML string, it gets translated into a hash using CGI::Ex::Conf. Slay.
Keys that match the regex m/^group \s+ (\w+)$/x โ think "group onevent" โ are reserved and get counted as GROUP OPTIONS. Every other key (if there even are any) should be field names that need validating. Periodt.
If the GROUP OPTION 'group validate_if' is set, then the validation only actually gets validated if the conditions of the validate_if are met ๐ซถ. If 'group validate_if' is not specified, the validation just proceeds like normal. Peep the validate_if VALIDATION type for the full tea.
Every single item listed in the validation gets validated. And the validation order? Determined these ways, bestie:
- Specify
'group order'arrayref with hashrefs.
# order will be (username, password, 'm/\w+_foo/', somethingelse)
{
'group title' => "User Information",
'group order' => [
{field => 'username', required => 1},
{field => 'password', required => 1},
{field => 'm/\w+_foo/', required => 1},
],
somethingelse => {required => 1},
}
- Specify
'group order'arrayref with field key names.
# order will be (username, password, 'm/\w+_foo/', somethingelse)
{
'group title' => "User Information",
'group order' => [qw(username password), 'm/\w+_foo/'],
username => {required => 1},
password => {required => 1},
'm/\w+_foo/' => {required => 1},
somethingelse => {required => 1},
}
- Do nothing โ just use sorted order.
# order will be ('m/\w+_foo/', password, somethingelse, username)
{
'group title' => "User Information",
username => {required => 1},
password => {required => 1},
'm/\w+_foo/' => {required => 1},
somethingelse => {required => 1},
}
Optionally, your 'group order' can contain the word 'OR' as a special keyword ๐. If the item before 'OR' fails validation, then the item after 'OR' gets tested instead. But if the item before 'OR' passes? Then the item after 'OR' does not get tested. Iconic behavior honestly.
'group order' => [qw(zip OR postalcode state OR region)],
Right now, only "group onevent" submit works with this option. And real talk โ using OR isn't even needed if you're testing for one or more values. Instead you should use min_in_set or max_in_set (OR is still useful for other cases though, don't get it twisted).
'zip' => {
max_in_set: '1 of zip, postalcode',
},
'state' => {
max_in_set: '1 of state, region',
},
Each individual field validation hashref operates on the field sitting in the 'field' key. That key can also be a regular expression in the form 'm/somepattern/'. If you use a regex, then every key matching that pattern gets validated ๐. And if the field key isn't specified at all, the key from the top level hash gets used instead.
foobar => { # "foobar" is not used as key because field is specified
field => 'real_key_name',
required => 1,
},
real_key_name2 => {
required => 1,
},
'm/\w+/' => { # this will apply to all fields matching this regex
required => 1,
},
Each of those individual field validation hashrefs should contain the types listed in VALIDATION TYPES. ๐
โจ VALIDATION TYPES โจ
This section lists all the available validation types, bestie. And here's the cute part โ for some validation types you can use multiple instances of the same type by just adding a number to the type (like match, match2, match232 ๐ญ). Multiple instances get validated in sorted order.
The types that allow multiple values are: compare, custom, custom_js, equals, match, required_if, sql, validate_if, and replace (replace is a MODIFICATION TYPE, fyi).
coerceโ Specialty item fortype:array. Turns a single non-array item into an array. Little glow-up for your data ๐ .compareโ Lets you do custom comparisons. The available types are>,<,>=,<=,!=,==,gt,lt,ge,le,ne, andeq. Comparisons also work in the JS ๐ฅ.
{
field => 'my_number',
match => 'm/^\d+$/',
compare1 => '> 100',
compare2 => '< 255',
compare3 => '!= 150',
}
customโ Custom value, and heads up: not available in JS. This lets you add extra programming types. It can be either a boolean value predetermined before calling validate, or a coderef that gets called during validation. If the coderef gets called, it's passed the field name, the form value for that name, and a reference to the field validation hash. If the custom type returns false, the element fails validation and an error gets added.
{
field => 'username',
custom => sub {
my ($key, $val, $field_val_hash, $checktype, $form) = @_;
# do something here
return 0;
},
custom_error => '$name was not valid',
}
Often you'll want a different message depending on the code passed to custom. To use a custom error message, just die with the error message. But note โ you'll wanna add a newline, or else perl adds the line number and file for you, and CGI::Ex::Validate will remove the trailing newline. Slightly feral but okay ๐ซถ.
{
field => 'username',
custom => sub {
my ($key, $val) = @_;
die "Custom error message 1\n" if $val eq '1';
die "Custom error message 2\n" if $val eq '2';
return 0;
},
custom_error => '$name default custom error message',
}
custom_jsโ Custom value, and this one is only available in JS. Lets you add extra programming types. It can be a javascript function (if fully declared in javascript), a string containing a javascript function (that getseval'ed into a real function), a boolean value pre-determined before calling validate, or a section of javascript that getseval'ed (the last value of the eval'ed javascript determines whether validation passed). A false response means the value did not pass validation. A true response means it did โ . Peep thesamples/validate_js_0_tests.htmlpage for a sample of usages.
{
field => 'date',
required => 1,
match => 'm|^\d\d\d\d/\d\d/\d\d$|',
match_error => 'Please enter date in YYYY/MM/DD format',
custom_js => "function (args) {
var t = new Date();
var y = t.getYear()+1900;
var m = t.getMonth() + 1;
var d = t.getDate();
if (m < 10) m = '0'+m;
if (d < 10) d = '0'+d;
(args.value > ''+y+'/'+m+'/'+d) ? 1 : 0;
}",
custom_js_error => 'The date was not greater than today.',
}
Again, you'll often want a different message depending on the function passed to custom_js. To use a custom error message, just throw the error message โจ.
{
field => 'username',
custom_js => 'function (args) {
if (args.value == 1) throw "Custom error message 1";
if (args.value == 2) throw "Custom error message 2";
return 0;
}',
custom_js_error => '$name default custom error message',
}
enumโ Lets you check whether an item matches a set of options. In perl the value may be passed as an arrayref. In the conf or in perl, the value may be passed with the options joined with||.
{
field => 'password_type',
enum => 'plaintext||crypt||md5', # OR enum => [qw(plaintext crypt md5)],
}
equalsโ Lets you compare two form elements ๐. Can have an optional!.
{
field => 'password',
equals => 'password_verify',
},
{
field => 'domain1',
equals => '!domain2', # make sure the fields are not the same
}
had_errorโ Typically used by avalidate_if. Lets you check if this item has had an error.
{
field => 'alt_password',
validate_if => {field => 'password', had_error => 1},
}
This is basically the opposite of was_valid. Lowkey iconic duo.
matchโ Lets you do regular expression comparison. Multiple matches may be concatenated with||. Available in JS.
{
field => 'my_ip',
match => 'm/^\d{1,3}(\.\d{1,3})3$/',
match_2 => '!m/^0\./ || !m/^192\./',
}
max_in_setandmin_in_setโ Somewhat likemin_valuesandmax_values, except that you specify the fields that participate in the count. Also โ entries that aren't defined or don't have length are not counted. An optional"of"can be placed after the number for human readability ๐ซถ.
min_in_set => "2 of foo bar baz",
# two of the fields foo, bar or baz must be set
# same as
min_in_set => "2 foo bar baz",
# same as
min_in_set => "2 OF foo bar baz",
validate_if => {field => 'whatever', max_in_set => '0 of whatever'},
# only run validation if there were zero occurrences of whatever
max_lenandmin_lenโ Lets you check on the length of fields ๐.
{
field => 'site',
min_len => 4,
max_len => 100,
}
max_valuesandmin_valuesโ Lets you specify the maximum number of form elements passed.max_valuesdefaults to1(and yes, you must explicitly set it higher to allow more than one item by any given name โ no shortcuts bestie).requiredโ Requires the form field to have some value. If the field isn't present, no other checks will be run and an error will be given.
It has been common for code to try "required => 0" which essentially has no effect โ instead use "validate_if => 'fieldname', required => 1". This results in the fieldname only being required if the fieldname is present. Chef's kiss ๐ธ.
reqโ Alias ofrequired. Will userequired_error."required_if"โ makes the form field required if the condition is satisfied. The conditions you can use are literally the same ones as forvalidate_if. So it's kinda the same as writing:
validate_if => 'some_condition',
required => 1
which equals
required_if => 'some_condition',
BUT โจ the difference is that with required_if, other checks still run โ whereas validate_if straight up skips all validation if the condition isn't met. Not the same vibe, bestie.
If a regex is used for the field name, the required_if field will have any match patterns swapped in.
{
field => 'm/^(\w+)_pass/',
required_if => '$1_user',
}
So in this example, the foobar_pass field would be required to be set if the foobar_user field was passed. ๐
"sql"โ SQL query based โ not available in JS. The database handle gets looked for in the value$self->{dbhs}->{foo}ifsql_db_typeis set to'foo', otherwise it defaults to$self->{dbh}. And if$self->{dbhs}->{foo}or$self->{dbh}is a coderef โ they'll be called, and they should return a dbh.
{
field => 'username',
sql => 'SELECT COUNT(*) FROM users WHERE username = ?',
sql_error_if => 1, # default is 1 - set to 0 to negate result
# sql_db_type => 'foo', # will look for a dbh under $self->{dbhs}->{foo}
}
"type"โ lets you do way stricter type checking. Currently supported types areCC(credit card),EMAIL,DOMAIN,IP,URL,INT,UINT,NUM,UNUM,STR,CODE,ARRAY, andHASH. Also,[]or{}can be used to indicateARRAYandHASH. If a type check fails โ the other compare, custom, or length checks will not be ran. And the"type_ne_required"parameter can be used to indicate not to run checks if the value is not defined (it separatesrequiredfromtype).
{
field => 'credit_card',
type => 'CC',
}
In Validate.pm (not supported in validate.js) it's possible to have a nested type definition. The value of "type" can be a validation hash specification. In that case, the type of the sub element must be a hash and must satisfy the type requirements. Default thrown errors contain the nested key name.
{
field => 'credit_card',
type => {
num => {required => 1, type => 'CC'},
exp => {required => 1, match => 'm|^\d\d\d\d/\d\d$|'},
},
}
The nested validation also allows for an array of subtypes:
{
field => 'foo',
type => [{
bar => {required => 1}, # needs foo[0].bar
baz => {required => 1, type => {bingo => {required => 1}}}, # wants foo[0].baz.bingo
}],
}
Okay so there's a little bit of ambiguity between what required stands for in the case of an array parameter. Right now, the required applies to each sub element of the array. For arrays it's often appropriate to say the following:
{
field => 'foo',
type => [], # or array
required => 1, # each element of foo must be defined
min_values => 1, # make sure the element contains at least one value
coerce => 1, # translate an item passed as a single item to always be an array
}
"CC" ๐ณ
Simple Luhn-10 check. Note that spaces and dashes are left in place.
"EMAIL" ๐ง
A very basic check to see if the value looks like an address. The local part must only contain [\w.~!\#\$%\^&*\-=+?] and the domain must be a domain or ip. If you want full fledged RFC compliant checking, consider something like:
{
field => 'email',
custom => sub {
my ($key, $val, $fv, $type, $form) = @_;
require Mail::Address;
my @a = Mail::Address->parse($val);
die "Invalid address\n" if @a != 1;
return $form->{$key} = $a[0]->address;
},
}
"DOMAIN" ๐
Checks for a valid domain name โ but it does no lookup of the domain. For that, use a custom sub.
"IP" ๐
Basic IPv4 check.
"URL" ๐
Basic check that the string matches something resembling an http or https url.
"INT" ๐ข
Checks for an integer between -2147483648 and 2147483648.
"UINT" ๐
Checks for an unsigned integer between 0 and 4294967295.
"NUM" ๐งฎ
Checks for something that looks like a number. Scientific notation is not allowed. No range enforced.
"validate_if"โ ifvalidate_ifis specified, the field will only be validated if the conditions are met. Works in JS. โจ
validate_if => {field => 'name', required => 1, max_len => 30}
# Will only validate if the field "name" is present and is less than 30 chars.
validate_if => 'name',
# SAME as
validate_if => {field => 'name', required => 1},
validate_if => '! name',
# SAME as
validate_if => {field => 'name', max_in_set => '0 of name'},
validate_if => 'name was_valid',
# SAME as
validate_if => {field => 'name', was_valid => 1},
validate_if => {field => 'country', compare => "eq US"},
# only if country's value is equal to US
validate_if => {field => 'country', compare => "ne US"},
# if country doesn't equal US
validate_if => {field => 'password', match => 'm/^md5\([a-z0-9]{20}\)$/'},
# if password looks like md5(12345678901234567890)
{
field => 'm/^(\w+)_pass/',
validate_if => '$1_user',
required => 1,
},
{
field => 'm/^(\w+)_pass2/',
validate_if => '$1_pass',
equals => '$1_pass',
required => 1,
}
# will validate foo_pass only if foo_user was present.
The validate_if may also contain an arrayref of validation items, so that multiple checks can be run. They'll be run in order. validate_if will return true only if all options returned true.
validate_if => ['email', 'phone', 'fax']
Optionally, if validate_if is an arrayref, it may contain the word 'OR' as a special keyword. If the item preceding 'OR' fails validation, the item after 'OR' will be tested instead. If the item preceding 'OR' passes validation, the item after 'OR' will not be tested.
validate_if => [qw(zip OR postalcode)],
"was_valid"โ typically used by avalidate_if. Lets you check if this item has successfully been validated. ๐
{
field => 'password2',
validate_if => {field => 'password', was_valid => 1},
}
This is basically the opposite of had_error.
๐ SPECIAL VALIDATION TYPES ๐
"field"โ specify which field to work on. The key may be a regex in the form'm/\w+_user/'. This key is required in a hashref passed to'group order'. It can optionally be used with other types to specify a different form element to operate on. On errors, if a non-default error is found,$fieldwill be swapped with the value found infield.
The field name may also be a regular expression in the form of 'm/somepattern/'. If a regular expression is used, all keys matching that pattern will be validated.
"name"โ the name to use for errors. If a name is not specified, default errors will use"The field $field"as the name. If a non-default error is found,$namewill be swapped with this name."delegate_error"โ this option allows for any errors generated on a field to delegate to a different field. If the field name was a regex, any patterns will be swapped into thedelegate_errorvalue. This option is generally only useful with theas_hashmethod of the error object (for inline errors). ๐
{
field => 'zip',
match => 'm/^\d{5}/',
},
{
field => 'zip_plus4',
match => 'm/^\d{4}/',
delegate_error => 'zip',
},
{
field => 'm/^(id_[\d+])_user$/',
delegate_error => '$1',
},
"exclude_js"โ this lets the cgi do the checking while keeping those checks from being run in JavaScript.
{
field => 'cgi_var',
required => 1,
exclude_js => 1,
}
"exclude_cgi"โ this lets the js do the checking while keeping those checks from being run in the cgi.
{
field => 'js_var',
required => 1,
exclude_cgi => 1,
}
"vif_disable"โ only functions in javascript. Will mark set the form element to disabled ifvalidate_iffails. It will mark it as enabled ifvalidate_ifis successful. This item should normally only be used whenoneventincludes"change"or"blur". ๐
๐ MODIFYING VALIDATION TYPES ๐
okay so these next ones are a whole different vibe โ they're not just checking your value, they're changing it before it even gets processed. and bestie, they work in both perl AND javascript. in JS they literally go in and change the actual value in the form for the form types where that makes sense. main character behaviour honestly ๐
do_not_trim
so by default, validate trims the leading and trailing whitespace off of submitted values. set do_not_trim to 1 if you want it to not do that. leave my whitespace alone challenge, level: impossible โจ
{field => 'foo', do_not_trim => 1}
trim_control_chars
off by default. if you set it to true, it removes characters in the \x00 to \x31 range. and heads up โ tabs get translated to a single space ๐ซถ
{field => 'foo', trim_control_chars => 1}
replace
pass a swap pattern and it'll change the actual value of the form. any perl regex is allowed, but ngl it's suggested you use javascript compatible regexes so that generate_js is actually possible ๐
{field => 'foo', replace => 's/(\d{3})(\d{3})(\d{3})/($1) $2-$3/'}
default
sets the item to the default value if there's no existing value (meaning undefined or a zero length string). the glow-up for empty fields ๐ฆ
{field => 'country', default => 'EN'}
to_upper_case and to_lower_case
they do exactly what they say they do. iconic, no notes ๐
untaint
this one's a little diva. it requires that the validated field has also been checked with an enum, equals, match, compare, custom, or type check. if the field has been checked and there are no errors โ congrats, the field is "untainted." โจ
this is for use in conjunction with perl's -T switch.
clear_on_error
clears the form field if a validation error happens. โ ๏ธ but listen โ this is only supported on the Javascript side (no affect on the server side, fr fr).
๐ ERROR OBJECT ๐
when validation fails, you get an error object, created via the new_error method. the default error class is CGI::Ex::Validate::Error.
the error object has several methods for figuring out what the errors actually were. let's go ๐
as_array
returns an array or an arrayref (depending on scalar context) of the errors that occurred, in the order that they occurred. individual groups may have a heading, and the entire validation will have a heading (the default heading can be changed via the 'as_array_title' group option). each error that occurred is a separate item and they're pre-pended with 'as_array_prefix' (which is a group option โ default is ' '). the as_array_ options may also be set via a hashref passed to as_array. as_array_title defaults to 'Please correct the following items:'. ๐ญ so organised of it
# if this returns the following
my $array = $err_obj->as_array;
# $array looks like
# ['Please correct the following items:', ' error1', ' error2']
# then this would return the following
my $array = $err_obj->as_array({
as_array_prefix => ' - ',
as_array_title => 'Something went wrong:',
});
# $array looks like
# ['Something went wrong:', ' - error1', ' - error2']
as_string
returns the values of as_array joined with a newline. this method is used as the stringification for the error object. the values of as_array are joined with 'as_string_join', which defaults to "\n". if 'as_string_header' is set, it gets pre-pended onto the error string. if 'as_string_footer' is set, it gets appended onto the error string. ๐ธ
# if this returns the following
my $string = $err_obj->as_string;
# $string looks like
# "Please correct the following items:\n error1\n error2"
# then this would return the following
my $string = $err_obj->as_string({
as_array_prefix => ' - ',
as_array_title => 'Something went wrong:',
as_string_join => '<br />',
as_string_header => '<span class="error">',
as_string_footer => '</span>',
});
# $string looks like
# '<span class="error">Something went wrong:<br /> - error1<br /> - error2</span>'
as_hash
returns a hash or a hashref (depending on scalar context) of the errors that occurred. each key is the field name of the form that failed validation, with 'as_hash_suffix' tacked on as a suffix. as_hash_suffix is available as a group option and may also be passed in via a hashref as the only argument to as_hash. the default value is '_error'. the values of the hash are arrayrefs of the errors that occurred to that form element. ๐
by default, as_hash returns the values of the hash as arrayrefs (so, a list of the errors that happened to that key). it's also possible to return the values as strings. three options are available for formatting: 'as_hash_header' which gets pre-pended onto the error string, 'as_hash_footer' which gets appended, and 'as_hash_join' which is used to join the arrayref. the only argument required to force the stringification is 'as_hash_join'. iykyk ๐
# if this returns the following
my $hash = $err_obj->as_hash;
# $hash looks like
# {key1_error => ['error1', 'error2']}
# then this would return the following
my $hash = $err_obj->as_hash({
as_hash_suffix => '_foo',
as_hash_join => '<br />',
as_hash_header => '<span class="error">'
as_hash_footer => '</span>'
});
# $hash looks like
# {key1_foo => '<span class="error">error1<br />error2</span>'}
and that's the whole error-object situation bestie, but don't go anywhere โ there's still more page to go ๐
GROUP OPTIONS ๐
okay bestie so here's the tea: any key in a validation hash that matches the pattern m/^group \s+ (\w+)$/x is what we call a group option.
Now, why can you use either group or general? ๐ it's a whole lore thing โ CGI::Ex::Validate used to have this concept of validation groups, but literally nobody was using them so support got yeeted as of the 2.10 release. the old name general vs group is still hanging around, still supported, but bestie it's deprecated so likeโฆ don't. ๐
"title"
this is the group section heading, used when as_array or as_string gets called by the error object. it's giving header energy โจ
'group title' => 'Title of errors',
"order"
the order in which to validate key/value pairs of the group. because sometimes order matters, periodt. ๐
'group order' => [qw(user pass email OR phone)],
# OR
'group order' => [{
field => 'field1',
required => 1,
}, {
field => 'field2',
required => 1,
}],
"fields"
just an alias for 'group order'. same vibe, different name. ๐
"validate_if"
if you set this, the entire hashref only gets validated when the "if" conditions are met. gatekeeping the validation, iconic.
'group validate_if => {field => 'email', required => 1},
so this group would only validate all its fields if the email field was actually present. no email, no validation, no problem. ๐ฆ
"type_ne_required"
okay so back in early 200x (ancient times fr fr), the type and other checks started imposing requirement if the field wasn't defined. which is so annoying when you're doing API specification โ like you want to define the type, enum, match, length, but only if the field is present.
there's already the required parameter for enforcing required-ness. so adding 'group type_ne_required' lets required mean required, and lets type specification just define the type. when this is set, an error will not be thrown if the input data is undefined. slay, boundaries. ๐ซถ
"raise_error"
if raise_error is true, any call to validate that fails validation will die with an error object as the value. dramatic exit, honestly iconic. ๐
"no_extra_fields"
if no_extra_fields is true, validate will add errors for any field found in form that does not have a field_val hashref in the validation hash. default is false.
if no_extra_fields is set to 'used', it will check for any keys that were not in a group that was validated.
โ ๏ธ important exception bestie:field_valhashrefs or field names listed in avalidate_iforrequired_ifstatement will not be included. you must have an explicit entry for each key. no sneaky shortcuts. ๐
"\w+_error"
these items let you override the default errors. customize your messages, main character energy only. โจ
'group required_error' => '$name is really required',
'group max_len_error' => '$name must be shorter than $value characters',
# OR #
my $self = CGI::Ex::Validate->new({
max_len_error => '$name must be shorter than $value characters',
});
"as_array_title"
used as the section title for all errors that occur, when as_array or as_string is called by the error object. a lil header for your error collection. ๐
"as_array_prefix"
used as a prefix to individual errors that occur, when as_array or as_string is called by the error object. each individual error gets prefixed with this string. headings will not be prefixed. default is ' '. (that's two spaces, bestie, don't miss it.)
"as_string_join"
when as_string is called, the values from as_array get joined with as_string_join. default value is "\n". ๐ธ
"as_string_header"
if set, gets pre-pended onto the string when as_string is called.
"as_string_footer"
if set, gets pre-pended onto the string when as_string is called. (yes, it literally says pre-pended in the original โ that's the spec, I'm not touching it, not my job to fix the docs okay ๐ญ)
"as_hash_suffix"
added on to key names during the call to as_hash. default is '_error'. ๐
"as_hash_join"
by default, as_hash will return hashref values that are errors joined with the default as_hash_join value of <br />. it can also return values that are arrayrefs of the errors. this can be done by setting as_hash_join to a non-true value (for example ''). your choice, your vibe. ๐ฆ
"as_hash_header"
if as_hash_join has been set to a true value, as_hash_header may be set to a string that will be pre-pended on to the error string.
"as_hash_footer"
if as_hash_join has been set to a true value, as_hash_footer may be set to a string that will be postpended on to the error string.
"onevent"
defaults to {submit => 1}. this controls when the javascript validation will take place. you may pass any or all of load, submit, change, or blur. multiple events may be passed in the hash. โก
'group onevent' => {submit => 1, change => 1}',
a comma separated string of types may also be passed:
'group onevent' => 'submit,change,blur,load',
โ ๏ธ currently,changeandblurwill not work for dynamically matched field names such as'm/\w+/'. support will be added. (manifesting it for u bestie ๐)
"set_hook"
defaults to document.validate_set_hook which defaults to nothing. if "group set_hook" or document.validate_set_hook are set to a function, they will be passed the key name of a form element that had a validation error and the error that will be set.
if a true value is returned, then validate will not also the inline error. if no value or false is returned (default), the validate will continue setting the inline error. this gives full control over setting inline errors. samples/validate_js_2_onchange.html has a good example of using these hooks. ๐
'group set_hook' => "function (args) {
alert(\"Setting error to field \"+args.key);
}",
the args parameter includes key, value, val_hash, and form.
the document.validate_set_hook option is probably the better option to use, as it helps to separate display functionality out into your html templates rather than storing too much html logic in your CGI. good architecture is hot, periodt. ๐
"clear_hook"
similar to set_hook, but called when inline error is cleared. its corresponding default is document.validate_clear_hook. the clear hook is also sampled in samples/validate_js_2_onchange.html. ๐ง
'group clear_hook' => "function (args) {
alert(\"Clear error on field \"+args.key);
}",
the args parameter includes key, val_hash, form, and was_valid.
"no_inline"
if set to true, the javascript validation will not attempt to generate inline errors when the only "group onevent" type is "submit". default is true. inline errors are independent of confirm and alert errors.
'group no_inline' => 1,
"no_confirm"
if set to true, the javascript validation will try to use an alert instead of a confirm to inform the user of errors when one of the "group onevent" types is "submit". alert and confirm are independent or inline errors. default is false.
'group no_confirm' => 1,
"no_alert"
if set to true, the javascript validation will not show an alert box when errors occur. default is false. this option only comes into play if no_confirm is also set. this option is only in effect if "group onevent" includes "submit". this option is independent of inline errors.
๐ although it is possible to turn off all errors by settingno_inline,no_confirm, andno_alertall to1, it is suggested that at least one of the error reporting facilities is left on. bestie pls leave at least one on, your users deserve to know what went wrong. ๐
'group no_alert' => 1,
๐ JAVASCRIPT ๐
Okay bestie, here's the tea: CGI::Ex::Validate lets you have duplicate validation on the client side AND the server side. Like, both. At the same time. Iconic behavior honestly. โจ
Your errors can show up in literally any combo you want:
- inline + confirm
- inline + alert
- inline only
- confirm only
- alert only
- none at all
These combinations are controlled by the group options no_inline, no_confirm, and no_alert. And you generate all that JS for a page using the ->generate_js method of CGI::Ex::Validate. ๐
๐ Also possible: storing the validation inline with the html as YAML and reading it in using the HTML conf handler โ BUT that feature has been deprecated. See the included html samples for how to do it anyway. (iykyk, we don't do that anymore.)
generate_js will create something similar to the following (based on your validation):
<script src="/cgi-bin/js/CGI/Ex/validate.js"></script>
<script>
document.validation = {
'group no_confirm': 1,
'group no_alert': 1,
'group onevent': 'change,blur,submit',
'group order': ['username', 'password'],
username: {
required: 1,
max_len: 20
},
password: {
required: 1,
max_len: 30
}
};
if (document.check_form) document.check_form('my_form_name');
</script>
If inline errors are enabled (which is the default, bestie ๐), each error that occurs will attempt to find an html element with its name as the id. So like โ if the field "username" failed validation and created a "username_error", the javascript would set the html of <span id="username_error"></span> to the error message. Cute, right? ๐ฆ
And it is suggested to use something like the following so that you can have inline javascript validation as well as report validation errors from the server side too:
<span class=error id=password_error>[% password_error %]</span><br>
If the javascript fails for some reason, the form should still be able to submit as normal (fail gracefully). ๐
โจ the hooks, babes โจ
Additionally, there are two hooks that are called whenever an inline error is set or cleared. The following hooks are used in samples/validate_js_2_onchange.html to highlight the row and set an icon:
document.validate_set_hook = function (args) {
document.getElementById(args.key+'_img').innerHTML
= '<span style="font-weight:bold;color:red">!</span>';
document.getElementById(args.key+'_row').style.background
= '#ffdddd';
};
document.validate_clear_hook = function (args) {
if (args.was_valid) {
document.getElementById(args.key+'_img').innerHTML
= '<span style="font-weight:bold;color:green">+</span>';
document.getElementById(args.key+'_row').style.background
= '#ddffdd';
} else {
document.getElementById(args.key+'_img').innerHTML = '';
document.getElementById(args.key+'_row').style.background = '#fff';
}
};
And if you have jquery that looks like:
document.validate_set_hook = function (args) {
$('#'+args.key+'_img').html('<span style="font-weight:bold;color:red">!</span>');
$('#'+args.key+'_row').css('backgroundColor', '#ffdddd');
};
document.validate_clear_hook = function (args) {
if (args.was_valid) {
$('#'+args.key+'_img').html('<span style="font-weight:bold;color:green">+</span>');
$('#'+args.key+'_row').css('backgroundColor', '#ddffdd');
} else {
$('#'+args.key+'_img').html('');
$('#'+args.key+'_row').css('backgroundColor', '#fff');
}
};
These hooks can also be set as "group clear_hook" and "group set_hook" which are defined further above. ๐
If the confirm option is used ("group onevent" includes submit and "group no_confirm" is false), the errors will be displayed to the user. If they choose OK they will be able to try and fix the errors. If they choose cancel, the form will submit anyway and will rely on the server to do the validation. This is for fail safety โ to make sure that if the javascript didn't validate correctly, the user can still submit the data. ๐ซถ
๐ LICENSE ๐
This module may be distributed under the same terms as Perl itself. Slay, open source. ๐
๐ AUTHOR ๐
Paul Seamons <perl at seamons dot com> ๐
And that's a wrap on the whole page, bestie!! ๐โจ This one came from the perl v5.42.0 docs, dated 2025-07-25, and it's the CGI::Ex::Validate(3) page. Go forth and validate everything. No cap. ๐
๐ฆ๐
๐ฆ where this page comes from
- fedora 43: perl-CGI-Ex 2.55-6.fc43 ยท GPL-1.0-or-later OR Artistic-1.0-Perl
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms