📄 validatepp.pm
字号:
# Copyright (c) 2000-2004 Dave Rolsky# All rights reserved.# This program is free software; you can redistribute it and/or# modify it under the same terms as Perl itself. See the LICENSE# file that comes with this distribution for more details.package Params::Validate;use strict;BEGIN{ sub SCALAR () { 1 } sub ARRAYREF () { 2 } sub HASHREF () { 4 } sub CODEREF () { 8 } sub GLOB () { 16 } sub GLOBREF () { 32 } sub SCALARREF () { 64 } sub UNKNOWN () { 128 } sub UNDEF () { 256 } sub OBJECT () { 512 } sub HANDLE () { 16 | 32 } sub BOOLEAN () { 1 | 256 }}# Various internals notes (for me and any future readers of this# monstrosity):## - A lot of the weirdness is _intentional_, because it optimizes for# the _success_ case. It does not really matter how slow the code is# after it enters a path that leads to reporting failure. But the# "success" path should be as fast as possible.## -- We only calculate $called as needed for this reason, even though it# means copying code all over.## - All the validation routines need to be careful never to alter the# references that are passed.## -- The code assumes that _most_ callers will not be using the# skip_leading or ignore_case features. In order to not alter the# references passed in, we copy them wholesale when normalizing them# to make these features work. This is slower but lets us be faster# when not using them.# Matt Sergeant came up with this prototype, which slickly takes the# first array (which should be the caller's @_), and makes it a# reference. Everything after is the parameters for validation.sub validate_pos (\@@){ return if $NO_VALIDATION && ! defined wantarray; my $p = shift; my @specs = @_; my @p = @$p; if ( $NO_VALIDATION ) { # if the spec is bigger that's where we can start adding # defaults for ( my $x = $#p + 1; $x <= $#specs; $x++ ) { $p[$x] = $specs[$x]->{default} if ref $specs[$x] && exists $specs[$x]->{default}; } return wantarray ? @p : \@p; } # I'm too lazy to pass these around all over the place. local $options ||= _get_options( (caller(0))[0] ) unless defined $options; my $min = 0; while (1) { last unless ( ref $specs[$min] ? ! ( exists $specs[$min]->{default} || $specs[$min]->{optional} ) : $specs[$min] ); $min++; } my $max = scalar @specs; my $actual = scalar @p; unless ($actual >= $min && ( $options->{allow_extra} || $actual <= $max ) ) { my $minmax = ( $options->{allow_extra} ? "at least $min" : ( $min != $max ? "$min - $max" : $max ) ); my $val = $options->{allow_extra} ? $min : $max; $minmax .= $val != 1 ? ' were' : ' was'; my $called = _get_called(); $options->{on_fail}-> ( "$actual parameter" . ($actual != 1 ? 's' : '') . " " . ($actual != 1 ? 'were' : 'was' ) . " passed to $called but $minmax expected\n" ); } my $bigger = $#p > $#specs ? $#p : $#specs; foreach ( 0..$bigger ) { my $spec = $specs[$_]; next unless ref $spec; if ( $_ <= $#p ) { my $value = defined $p[$_] ? qq|"$p[$_]"| : 'undef'; _validate_one_param( $p[$_], \@p, $spec, "Parameter #" . ($_ + 1) . " ($value)"); } $p[$_] = $spec->{default} if $_ > $#p && exists $spec->{default}; } _validate_pos_depends(\@p, \@specs); foreach ( grep { defined $p[$_] && ! ref $p[$_] && ref $specs[$_] && $specs[$_]{untaint} } 0..$bigger ) { ($p[$_]) = $p[$_] =~ /(.+)/; } return wantarray ? @p : \@p;}sub _validate_pos_depends{ my ( $p, $specs ) = @_; for my $p_idx ( 0..$#$p ) { my $spec = $specs->[$p_idx]; next unless $spec && UNIVERSAL::isa( $spec, 'HASH' ) && exists $spec->{depends}; my $depends = $spec->{depends}; if ( ref $depends ) { require Carp; local $Carp::CarpLevel = 2; Carp::croak( "Arguments to 'depends' for validate_pos() must be a scalar" ) } my $p_size = scalar @$p; if ( $p_size < $depends - 1 ) { my $error = ( "Parameter #" . ($p_idx + 1) . " depends on parameter #" . $depends . ", which was not given" ); $options->{on_fail}->($error); } } return 1;}sub _validate_named_depends{ my ( $p, $specs ) = @_; foreach my $pname ( keys %$p ) { my $spec = $specs->{$pname}; next unless $spec && UNIVERSAL::isa( $spec, 'HASH' ) && $spec->{depends}; unless ( UNIVERSAL::isa( $spec->{depends}, 'ARRAY' ) || ! ref $spec->{depends} ) { require Carp; local $Carp::CarpLevel = 2; Carp::croak( "Arguments to 'depends' must be a scalar or arrayref" ); } foreach my $depends_name ( ref $spec->{depends} ? @{ $spec->{depends} } : $spec->{depends} ) { unless ( exists $p->{$depends_name} ) { my $error = ( "Parameter '$pname' depends on parameter '" . $depends_name . "', which was not given" ); $options->{on_fail}->($error); } } }}sub validate (\@$){ return if $NO_VALIDATION && ! defined wantarray; my $p = $_[0]; my $specs = $_[1]; local $options = _get_options( (caller(0))[0] ) unless defined $options; unless ( $NO_VALIDATION ) { if ( ref $p eq 'ARRAY' ) { # we were called as validate( @_, ... ) where @_ has a # single element, a hash reference if ( ref $p->[0] ) { $p = $p->[0]; } elsif ( @$p % 2 ) { my $called = _get_called(); $options->{on_fail}-> ( "Odd number of parameters in call to $called " . "when named parameters were expected\n" ); } else { $p = {@$p}; } } } if ( $options->{normalize_keys} ) { $specs = _normalize_callback( $specs, $options->{normalize_keys} ); $p = _normalize_callback( $p, $options->{normalize_keys} ); } elsif ( $options->{ignore_case} || $options->{strip_leading} ) { $specs = _normalize_named($specs); $p = _normalize_named($p); } if ($NO_VALIDATION) { return ( wantarray ? ( # this is a hash containing just the defaults ( map { $_ => $specs->{$_}->{default} } grep { ref $specs->{$_} && exists $specs->{$_}->{default} } keys %$specs ), ( ref $p eq 'ARRAY' ? ( ref $p->[0] ? %{ $p->[0] } : @$p ) : %$p ) ) : do { my $ref = ( ref $p eq 'ARRAY' ? ( ref $p->[0] ? $p->[0] : {@$p} ) : $p ); foreach ( grep { ref $specs->{$_} && exists $specs->{$_}->{default} } keys %$specs ) { $ref->{$_} = $specs->{$_}->{default} unless exists $ref->{$_}; } return $ref; } ); } _validate_named_depends($p, $specs); unless ( $options->{allow_extra} ) { my $called = _get_called(); if ( my @unmentioned = grep { ! exists $specs->{$_} } keys %$p ) { $options->{on_fail}-> ( "The following parameter" . (@unmentioned > 1 ? 's were' : ' was') . " passed in the call to $called but " . (@unmentioned > 1 ? 'were' : 'was') . " not listed in the validation options: @unmentioned\n" ); } } my @missing; # the iterator needs to be reset in case the same hashref is being # passed to validate() on successive calls, because we may not go # through all the hash's elements keys %$specs; OUTER: while ( my ($key, $spec) = each %$specs ) { if ( ! exists $p->{$key} && ( ref $spec ? ! ( do { # we want to short circuit the loop here if we # can assign a default, because there's no need # check anything else at all. if ( exists $spec->{default} ) { $p->{$key} = $spec->{default}; next OUTER; } } || do { # Similarly, an optional parameter that is # missing needs no additional processing. next OUTER if $spec->{optional}; } ) : $spec ) ) { push @missing, $key; } # Can't validate a non hashref spec beyond the presence or # absence of the parameter. elsif (ref $spec) { my $value = defined $p->{$key} ? qq|"$p->{$key}"| : 'undef'; _validate_one_param( $p->{$key}, $p, $spec, "The '$key' parameter ($value)" ); } } if (@missing) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -