Search code examples
functionperlvalidationfunction-parameter

How can I validate function parameters in Perl?


Could you please tell me what in Perl is the smartest way to validate function parameter?

Code-Snippet:

sub testfunction {
    my ($args) = @_;
    my $value = $args->{value} || die "no value set";
    # process value ...
}


testfunction({value => 'Hallo'});

testfunction({novalue => 'Hallo'});

Is the above way an good alternative to validate function parameter in perl or are there smarter ways? Many Thanks to everybody.


Solution

  • It depends a lot on what you're trying to accomplish, and whether it's user input, local functions or remote functions.

    I mean, some parameters - it's far easier to 'validate' by trying to use it. A file name for example - rather than trying to trap all the edge cases by hand, just pass it to open .... or die $!

    And sometimes - it's more sensible to 'default-and-override'. E.g.:

    my ( $first_thing, $second_thing ) = @_; 
    $first_thing //= "default value";
    $second_thing //= 0; 
    

    Note - use of // rather than || - it's similar in usage, but it tests for just being undefined - rather than being defined-but-false such as '' or 0.

    And other times you want better still validation, by applying a set of validation regexes. Just be careful, some things - like email addresses or IP addresses - can be a lot more complicated to exhaustively validate than you think - so a simple edge case of 'was there a parameter at all' can work.

    Of course, at that point you can also start looking at function prototypes. Which aren't anything like the prototyping you may think of from other languages:

    #!/usr/bin/env perl
    
    #says - must have a single scalar arg. 
    sub testfunction($) {
       my ( $required_arg ) = @_; 
       print $required_arg;
    }
    
    testfunction(1);
    testfunction;
    

    The latter will fail with:

    Not enough arguments for main::testfunction
    

    Note - perl prototypes check type and number of arguments (e.g. is it scalar, is it array). But don't check values, and can have some slightly unexpected effects when it comes to passing in arrays.

    I'd suggest that passing in hashes is quite well suited to a defaulting mechanism, because you can:

    #!/usr/bin/env perl
    use strict;
    use warnings;
    use Data::Dumper;
    
    my %defaults = (
      "test" => 1,
    );
    
    sub with_default {
        my %args = (%defaults, @_); 
        print Dumper \%args; 
    }
    
    with_default;
    with_default(test => 4 );
    

    But to go back to your original case - if your function simply won't work without 'value' being set, then it's good to spell it out in code:

    if ( not defined $args -> {value} 
      or not $args -> {value} =~ m/^\w+$/ ) {
       die 'value parameter must be supplied and match m/^\w+$/';
    }
    

    It's probably worth mentioning 'taint' mode at this point. It's a feature of perl that you don't see in many other languages.

    It specifically tells the interpreter that 'user supplied' is 'tainted' and thus cannot be used without validation. And that includes insecure paths, environment vars etc.

    See perlsec

    But:

    #!/usr/bin/env perl -T
    use strict;
    use warnings;
    
    system "echo $ENV{'USERNAME'}";
    

    Will fail - because system will not allow 'tainted' vars. (But print will). You need to pass it through a validation step (e.g. regex typically) before the 'taint' is removed.