I know there's already a CPAN module that does this, and I know there's plently of people out there who have reinvented this wheel many times, but while hacking a quick user registration thing for a site, I wrote this bit of code, I thought other might find it useful...
Takes two arguments, a ref to a CGI object (or anything that implements param with the same semantics as CGI) and a ref to a hash of (key, valitation) pairs.
sub validate_data($$) { my $q = shift; my $tmpl = shift; my %data = (); my %invalid = (); foreach my $key (keys %{$tmpl}) { my $value = $q->param($key); if (&{$tmpl->{$key}}($value)) { $data{$key} = $value if defined $value; } else { $invalid{$key} = 1; } } return (\%data, \%invalid); }
And the validation routines are:
sub is_string { return sub { return $_[0] ne ""; }; } sub is_number { return sub { return $_[0] =~ m{^\d+$}; }; } sub is_range($$) { (my $l, my $u) = @_; return sub { return $_[0] =~ m{^\d+$} && $_[0] >= $l && $_[0] <= $u; }; } sub is_in_list($) { my $l = shift; return sub { return (grep { $_[0] eq $_ } @{$l}) != 0; }; } sub is_email { return sub { return $_[0] =~ m{^[-.+a-z0-9]+\@[-.+a-z0-9]+$}i; }; } sub is_required { my $t = shift; return sub { defined $_[0] and &{$t}($_[0]); }; } sub is_optional { my $t = shift; return sub { return defined $_[0] ? &{$t}($_[0]) : 1; }; } sub is_equal { my $v = shift; return sub { $_[0] eq $v ? 1 : 0; } }
The "is_equal" routine probably needs to be generalized to be able to compare numbers and other things... You can use it like this:
my %fields = ( name => is_required(is_string()), email => is_required(is_email()), phone => is_optional(is_phone()), );

Replies are listed 'Best First'.
Re: Validate CGI data
by merlyn (Sage) on Jun 26, 2005 at 19:55 UTC
    sub is_email { return sub { return $_[0] =~ m{^[-.+a-z0-9]+\@[-.+a-z0-9]+$}i; }; }
    One reason not to reinvent the wheel is to not make the same common mistakes. You're making the common mistake here. Please understand RFC 822 before writing a routine like this. Or just use something like Email::Valid to get it right.

    Your code downvoted. Ugh.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Fair point.

      I want a local part plus domain, not a full blown email address. Yes, I know I've got even that wrong in the presented code. And, although valid, I really don't want to deal with "My Name"@example.org nor I want to deal with nulls embedded in the local part.

      Is there room for improvement? Yes, sure. But that really wasn't the point of the original post.

      Thanks!
        Is there room for improvement? Yes, sure. But that really wasn't the point of the original post.
        OK, then I'm confused. If you post code here, the implication is that you're posting your code for peer review and comment, in the intent to improve it or find the holes. So, I make a comment, and then you say "that's not the point of the original post". You are clear on what postings here are about, right?

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.