1 package Params::Validate;
8 # suppress subroutine redefined warnings if we tried to load the XS
10 no warnings 'redefine';
24 sub HANDLE () { 16 | 32 }
25 sub BOOLEAN () { 1 | 256 }
28 # Various internals notes (for me and any future readers of this
31 # - A lot of the weirdness is _intentional_, because it optimizes for
32 # the _success_ case. It does not really matter how slow the code is
33 # after it enters a path that leads to reporting failure. But the
34 # "success" path should be as fast as possible.
36 # -- We only calculate $called as needed for this reason, even though it
37 # means copying code all over.
39 # - All the validation routines need to be careful never to alter the
40 # references that are passed.
42 # -- The code assumes that _most_ callers will not be using the
43 # skip_leading or ignore_case features. In order to not alter the
44 # references passed in, we copy them wholesale when normalizing them
45 # to make these features work. This is slower but lets us be faster
46 # when not using them.
48 # Matt Sergeant came up with this prototype, which slickly takes the
49 # first array (which should be the caller's @_), and makes it a
50 # reference. Everything after is the parameters for validation.
51 sub validate_pos (\@@) {
52 return if $NO_VALIDATION && !defined wantarray;
61 # if the spec is bigger that's where we can start adding
63 for ( my $x = $#p + 1; $x <= $#specs; $x++ ) {
64 $p[$x] = $specs[$x]->{default}
65 if ref $specs[$x] && exists $specs[$x]->{default};
68 return wantarray ? @p : \@p;
71 # I'm too lazy to pass these around all over the place.
72 local $options ||= _get_options( ( caller(0) )[0] )
73 unless defined $options;
81 ? !( exists $specs[$min]->{default} || $specs[$min]->{optional} )
88 my $max = scalar @specs;
90 my $actual = scalar @p;
91 unless ( $actual >= $min
92 && ( $options->{allow_extra} || $actual <= $max ) ) {
94 $options->{allow_extra}
96 : ( $min != $max ? "$min - $max" : $max )
99 my $val = $options->{allow_extra} ? $min : $max;
100 $minmax .= $val != 1 ? ' were' : ' was';
102 my $called = _get_called();
104 $options->{on_fail}->( "$actual parameter"
105 . ( $actual != 1 ? 's' : '' ) . " "
106 . ( $actual != 1 ? 'were' : 'was' )
107 . " passed to $called but $minmax expected\n" );
110 my $bigger = $#p > $#specs ? $#p : $#specs;
111 foreach ( 0 .. $bigger ) {
112 my $spec = $specs[$_];
114 next unless ref $spec;
117 my $value = defined $p[$_] ? qq|"$p[$_]"| : 'undef';
118 _validate_one_param( $p[$_], \@p, $spec,
119 "Parameter #" . ( $_ + 1 ) . " ($value)" );
122 $p[$_] = $spec->{default} if $_ > $#p && exists $spec->{default};
125 _validate_pos_depends( \@p, \@specs );
132 && $specs[$_]{untaint}
135 ( $p[$_] ) = $p[$_] =~ /(.+)/;
138 return wantarray ? @p : \@p;
141 sub _validate_pos_depends {
142 my ( $p, $specs ) = @_;
144 for my $p_idx ( 0 .. $#$p ) {
145 my $spec = $specs->[$p_idx];
149 && UNIVERSAL::isa( $spec, 'HASH' )
150 && exists $spec->{depends};
152 my $depends = $spec->{depends};
154 if ( ref $depends ) {
156 local $Carp::CarpLevel = 2;
158 "Arguments to 'depends' for validate_pos() must be a scalar");
161 my $p_size = scalar @$p;
162 if ( $p_size < $depends - 1 ) {
166 . " depends on parameter #"
168 . ", which was not given" );
170 $options->{on_fail}->($error);
176 sub _validate_named_depends {
177 my ( $p, $specs ) = @_;
179 foreach my $pname ( keys %$p ) {
180 my $spec = $specs->{$pname};
184 && UNIVERSAL::isa( $spec, 'HASH' )
187 unless ( UNIVERSAL::isa( $spec->{depends}, 'ARRAY' )
188 || !ref $spec->{depends} ) {
190 local $Carp::CarpLevel = 2;
192 "Arguments to 'depends' must be a scalar or arrayref");
195 foreach my $depends_name (
197 ? @{ $spec->{depends} }
200 unless ( exists $p->{$depends_name} ) {
202 = ( "Parameter '$pname' depends on parameter '"
204 . "', which was not given" );
206 $options->{on_fail}->($error);
213 return if $NO_VALIDATION && !defined wantarray;
218 local $options = _get_options( ( caller(0) )[0] ) unless defined $options;
220 if ( ref $p eq 'ARRAY' ) {
222 # we were called as validate( @_, ... ) where @_ has a
223 # single element, a hash reference
225 $p = { %{ $p->[0] } };
228 my $called = _get_called();
231 ->( "Odd number of parameters in call to $called "
232 . "when named parameters were expected\n" );
239 if ( $options->{normalize_keys} ) {
240 $specs = _normalize_callback( $specs, $options->{normalize_keys} );
241 $p = _normalize_callback( $p, $options->{normalize_keys} );
243 elsif ( $options->{ignore_case} || $options->{strip_leading} ) {
244 $specs = _normalize_named($specs);
245 $p = _normalize_named($p);
248 if ($NO_VALIDATION) {
253 # this is a hash containing just the defaults
255 map { $_ => $specs->{$_}->{default} }
257 ref $specs->{$_} && exists $specs->{$_}->{default}
285 && exists $specs->{$_}->{default}
289 $ref->{$_} = $specs->{$_}->{default}
290 unless exists $ref->{$_};
298 _validate_named_depends( $p, $specs );
300 unless ( $options->{allow_extra} ) {
301 if ( my @unmentioned = grep { !exists $specs->{$_} } keys %$p ) {
302 my $called = _get_called();
304 $options->{on_fail}->( "The following parameter"
305 . ( @unmentioned > 1 ? 's were' : ' was' )
306 . " passed in the call to $called but "
307 . ( @unmentioned > 1 ? 'were' : 'was' )
308 . " not listed in the validation options: @unmentioned\n"
315 # the iterator needs to be reset in case the same hashref is being
316 # passed to validate() on successive calls, because we may not go
317 # through all the hash's elements
320 while ( my ( $key, $spec ) = each %$specs ) {
328 # we want to short circuit the loop here if we
329 # can assign a default, because there's no need
330 # check anything else at all.
331 if ( exists $spec->{default} ) {
332 $p->{$key} = $spec->{default};
338 # Similarly, an optional parameter that is
339 # missing needs no additional processing.
340 next OUTER if $spec->{optional};
349 # Can't validate a non hashref spec beyond the presence or
350 # absence of the parameter.
351 elsif ( ref $spec ) {
352 my $value = defined $p->{$key} ? qq|"$p->{$key}"| : 'undef';
353 _validate_one_param( $p->{$key}, $p, $spec,
354 "The '$key' parameter ($value)" );
359 my $called = _get_called();
361 my $missing = join ', ', map {"'$_'"} @missing;
362 $options->{on_fail}->( "Mandatory parameter"
363 . ( @missing > 1 ? 's' : '' )
364 . " $missing missing in call to $called\n" );
367 # do untainting after we know everything passed
373 && $specs->{$_}{untaint}
377 ( $p->{$key} ) = $p->{$key} =~ /(.+)/;
380 return wantarray ? %$p : $p;
384 return if $NO_VALIDATION && !defined wantarray;
388 local $options = _get_options( ( caller(0) )[0], %p );
390 unless ($NO_VALIDATION) {
391 unless ( exists $options->{called} ) {
392 $options->{called} = ( caller( $options->{stack_skip} ) )[3];
397 if ( UNIVERSAL::isa( $p{spec}, 'ARRAY' ) ) {
398 return validate_pos( @{ $p{params} }, @{ $p{spec} } );
402 # intentionally ignore the prototype because this contains
403 # either an array or hash reference, and validate() will
404 # handle either one properly
405 return &validate( $p{params}, $p{spec} );
409 sub _normalize_callback {
410 my ( $p, $func ) = @_;
414 foreach my $key ( keys %$p ) {
415 my $new_key = $func->($key);
417 unless ( defined $new_key ) {
419 "The normalize_keys callback did not return a defined value when normalizing the key '$key'";
422 if ( exists $new{$new_key} ) {
424 "The normalize_keys callback returned a key that already exists, '$new_key', when normalizing the key '$key'";
427 $new{$new_key} = $p->{$key};
433 sub _normalize_named {
435 # intentional copy so we don't destroy original
436 my %h = ( ref $_[0] ) =~ /ARRAY/ ? @{ $_[0] } : %{ $_[0] };
438 if ( $options->{ignore_case} ) {
439 $h{ lc $_ } = delete $h{$_} for keys %h;
442 if ( $options->{strip_leading} ) {
443 foreach my $key ( keys %h ) {
445 ( $new = $key ) =~ s/^\Q$options->{strip_leading}\E//;
446 $h{$new} = delete $h{$key};
453 sub _validate_one_param {
454 my ( $value, $params, $spec, $id ) = @_;
456 if ( exists $spec->{type} ) {
457 unless ( defined $spec->{type}
458 && Scalar::Util::looks_like_number( $spec->{type} )
459 && $spec->{type} > 0 ) {
461 = "$id has a type specification which is not a number. It is ";
462 if ( defined $spec->{type} ) {
463 $msg .= "a string - $spec->{type}";
470 .= ".\n Use the constants exported by Params::Validate to declare types.";
472 $options->{on_fail}->($msg);
475 unless ( _get_type($value) & $spec->{type} ) {
476 my $type = _get_type($value);
478 my @is = _typemask_to_strings($type);
479 my @allowed = _typemask_to_strings( $spec->{type} );
480 my $article = $is[0] =~ /^[aeiou]/i ? 'an' : 'a';
482 my $called = _get_called(1);
484 $options->{on_fail}->( "$id to $called was $article '@is', which "
485 . "is not one of the allowed types: @allowed\n" );
489 # short-circuit for common case
491 unless ( $spec->{isa}
493 || $spec->{callbacks}
496 if ( exists $spec->{isa} ) {
497 foreach ( ref $spec->{isa} ? @{ $spec->{isa} } : $spec->{isa} ) {
498 unless ( eval { $value->isa($_) } ) {
499 my $is = ref $value ? ref $value : 'plain scalar';
500 my $article1 = $_ =~ /^[aeiou]/i ? 'an' : 'a';
501 my $article2 = $is =~ /^[aeiou]/i ? 'an' : 'a';
503 my $called = _get_called(1);
506 ->( "$id to $called was not $article1 '$_' "
507 . "(it is $article2 $is)\n" );
512 if ( exists $spec->{can} ) {
513 foreach ( ref $spec->{can} ? @{ $spec->{can} } : $spec->{can} ) {
514 unless ( eval { $value->can($_) } ) {
515 my $called = _get_called(1);
518 ->("$id to $called does not have the method: '$_'\n");
523 if ( $spec->{callbacks} ) {
524 unless ( UNIVERSAL::isa( $spec->{callbacks}, 'HASH' ) ) {
525 my $called = _get_called(1);
527 $options->{on_fail}->(
528 "'callbacks' validation parameter for $called must be a hash reference\n"
532 foreach ( keys %{ $spec->{callbacks} } ) {
533 unless ( UNIVERSAL::isa( $spec->{callbacks}{$_}, 'CODE' ) ) {
534 my $called = _get_called(1);
536 $options->{on_fail}->(
537 "callback '$_' for $called is not a subroutine reference\n"
541 unless ( $spec->{callbacks}{$_}->( $value, $params ) ) {
542 my $called = _get_called(1);
545 ->("$id to $called did not pass the '$_' callback\n");
550 if ( exists $spec->{regex} ) {
551 unless ( ( defined $value ? $value : '' ) =~ /$spec->{regex}/ ) {
552 my $called = _get_called(1);
555 ->("$id to $called did not pass regex check\n");
562 # if it UNIVERSAL::isa the string on the left then its the type on
569 'SCALAR' => SCALARREF,
571 my %simple_refs = map { $_ => 1 } keys %isas;
574 return UNDEF unless defined $_[0];
579 # catches things like: my $fh = do { local *FH; };
580 return GLOB if UNIVERSAL::isa( \$_[0], 'GLOB' );
584 return $isas{$ref} if $simple_refs{$ref};
586 foreach ( keys %isas ) {
587 return $isas{$_} | OBJECT if UNIVERSAL::isa( $_[0], $_ );
590 # I really hope this never happens.
596 my %type_to_string = (
597 SCALAR() => 'scalar',
598 ARRAYREF() => 'arrayref',
599 HASHREF() => 'hashref',
600 CODEREF() => 'coderef',
602 GLOBREF() => 'globref',
603 SCALARREF() => 'scalarref',
605 OBJECT() => 'object',
606 UNKNOWN() => 'unknown',
609 sub _typemask_to_strings {
614 SCALAR, ARRAYREF, HASHREF, CODEREF, GLOB, GLOBREF,
615 SCALARREF, UNDEF, OBJECT, UNKNOWN
617 push @types, $type_to_string{$_} if $mask & $_;
619 return @types ? @types : ('unknown');
630 Carp::confess( $_[0] );
633 normalize_keys => undef,
636 *set_options = \&validation_options;
638 sub validation_options {
643 foreach ( keys %defaults ) {
644 $opts{$_} = $defaults{$_} unless exists $opts{$_};
647 $OPTIONS{$caller} = \%opts;
658 %{ $OPTIONS{$caller} },
666 exists $OPTIONS{$caller}
675 my $extra_skip = $_[0] || 0;
677 # always add one more for this sub
681 exists $options->{called}
683 : ( caller( $options->{stack_skip} + $extra_skip ) )[3]
686 $called = 'N/A' unless defined $called;
697 Params::ValidatePP - pure Perl implementation of Params::Validate
705 This is a pure Perl implementation of Params::Validate. See the
706 Params::Validate documentation for details.
710 Copyright (c) 2004-2007 David Rolsky. All rights reserved. This
711 program is free software; you can redistribute it and/or modify it
712 under the same terms as Perl itself.