home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / ValidatePP.pm < prev    next >
Encoding:
Perl POD Document  |  2003-12-03  |  16.9 KB  |  683 lines

  1. # Copyright (c) 2000-2003 Dave Rolsky
  2. # All rights reserved.
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the same terms as Perl itself.  See the LICENSE
  5. # file that comes with this distribution for more details.
  6.  
  7. package Params::Validate;
  8.  
  9. use strict;
  10.  
  11. BEGIN
  12. {
  13.     sub SCALAR    () { 1 }
  14.     sub ARRAYREF  () { 2 }
  15.     sub HASHREF   () { 4 }
  16.     sub CODEREF   () { 8 }
  17.     sub GLOB      () { 16 }
  18.     sub GLOBREF   () { 32 }
  19.     sub SCALARREF () { 64 }
  20.     sub UNKNOWN   () { 128 }
  21.     sub UNDEF     () { 256 }
  22.     sub OBJECT    () { 512 }
  23.  
  24.     sub HANDLE    () { 16 | 32 }
  25.     sub BOOLEAN   () { 1 | 256 }
  26. }
  27.  
  28. # Various internals notes (for me and any future readers of this
  29. # monstrosity):
  30. #
  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.
  35. #
  36. # -- We only calculate $called as needed for this reason, even though it
  37. #    means copying code all over.
  38. #
  39. # - All the validation routines need to be careful never to alter the
  40. #   references that are passed.
  41. #
  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.
  47.  
  48.  
  49. # Matt Sergeant came up with this prototype, which slickly takes the
  50. # first array (which should be the caller's @_), and makes it a
  51. # reference.  Everything after is the parameters for validation.
  52. sub validate_pos (\@@)
  53. {
  54.     return if $NO_VALIDATION && ! defined wantarray;
  55.  
  56.     my $p = shift;
  57.  
  58.     my @specs = @_;
  59.  
  60.     my @p = @$p;
  61.     if ( $NO_VALIDATION )
  62.     {
  63.         # if the spec is bigger that's where we can start adding
  64.         # defaults
  65.         for ( my $x = $#p + 1; $x <= $#specs; $x++ )
  66.     {
  67.             $p[$x] =
  68.                 $specs[$x]->{default}
  69.                     if ref $specs[$x] && exists $specs[$x]->{default};
  70.     }
  71.  
  72.     return wantarray ? @p : \@p;
  73.     }
  74.  
  75.     # I'm too lazy to pass these around all over the place.
  76.     local $options ||= _get_options( (caller(0))[0] )
  77.         unless defined $options;
  78.  
  79.     my $min = 0;
  80.  
  81.     while (1)
  82.     {
  83.         last unless ( ref $specs[$min] ?
  84.                       ! ( exists $specs[$min]->{default} || $specs[$min]->{optional} ) :
  85.                       $specs[$min] );
  86.  
  87.     $min++;
  88.     }
  89.  
  90.     my $max = scalar @specs;
  91.  
  92.     my $actual = scalar @p;
  93.     unless ($actual >= $min && ( $options->{allow_extra} || $actual <= $max ) )
  94.     {
  95.     my $minmax =
  96.             ( $options->{allow_extra} ?
  97.               "at least $min" :
  98.               ( $min != $max ? "$min - $max" : $max ) );
  99.  
  100.     my $val = $options->{allow_extra} ? $min : $max;
  101.     $minmax .= $val != 1 ? ' were' : ' was';
  102.  
  103.         my $called = _get_called();
  104.  
  105.     $options->{on_fail}->
  106.             ( "$actual parameter" .
  107.               ($actual != 1 ? 's' : '') .
  108.               " " .
  109.               ($actual != 1 ? 'were' : 'was' ) .
  110.               " passed to $called but $minmax expected\n" );
  111.     }
  112.  
  113.     my $bigger = $#p > $#specs ? $#p : $#specs;
  114.     foreach ( 0..$bigger )
  115.     {
  116.     my $spec = $specs[$_];
  117.  
  118.     next unless ref $spec;
  119.  
  120.     if ( $_ <= $#p )
  121.     {
  122.         my $value = defined $p[$_] ? qq|"$p[$_]"| : 'undef';
  123.         _validate_one_param( $p[$_], \@p, $spec, "Parameter #" . ($_ + 1) . " ($value)");
  124.     }
  125.  
  126.     $p[$_] = $spec->{default} if $_ > $#p && exists $spec->{default};
  127.     }
  128.  
  129.     _validate_pos_depends(\@p, \@specs);
  130.  
  131.     return wantarray ? @p : \@p;
  132. }
  133.  
  134. sub _validate_pos_depends
  135. {
  136.     my ( $p, $specs ) = @_;
  137.  
  138.     for my $p_idx ( 0..$#$p )
  139.     {
  140.         my $spec = $specs->[$p_idx];
  141.  
  142.         next unless $spec && UNIVERSAL::isa( $spec, 'HASH' ) && exists $spec->{depends};
  143.  
  144.         my $depends = $spec->{depends};
  145.  
  146.         if ( ref $depends )
  147.         {
  148.             require Carp;
  149.             local $Carp::CarpLevel = 2;
  150.             Carp::croak( "Arguments to 'depends' for validate_pos() must be a scalar" )
  151.         }
  152.  
  153.         my $p_size = scalar @$p;
  154.         if ( $p_size < $depends - 1 )
  155.         {
  156.             my $error = ( "Parameter #" . ($p_idx + 1) . " depends on parameter #" .
  157.                           $depends . ", which was not given" );
  158.  
  159.             $options->{on_fail}->($error);
  160.         }
  161.     }
  162.     return 1;
  163. }
  164.  
  165. sub _validate_named_depends
  166. {
  167.     my ( $p, $specs ) = @_;
  168.  
  169.     foreach my $pname ( keys %$p )
  170.     {
  171.         my $spec = $specs->{$pname};
  172.  
  173.         next unless $spec && UNIVERSAL::isa( $spec, 'HASH' ) && $spec->{depends};
  174.  
  175.         unless ( UNIVERSAL::isa( $spec->{depends}, 'ARRAY' ) || ! ref $spec->{depends} )
  176.         {
  177.             require Carp;
  178.             local $Carp::CarpLevel = 2;
  179.             Carp::croak( "Arguments to 'depends' must be a scalar or arrayref" );
  180.         }
  181.  
  182.         foreach my $depends_name ( ref $spec->{depends}
  183.                                    ? @{ $spec->{depends} }
  184.                                    : $spec->{depends} )
  185.         {
  186.             unless ( exists $p->{$depends_name} )
  187.             {
  188.                 my $error = ( "Parameter '$pname' depends on parameter '" .
  189.                               $depends_name . "', which was not given" );
  190.  
  191.                 $options->{on_fail}->($error);
  192.             }
  193.         }
  194.     }
  195. }
  196.  
  197. sub validate (\@$)
  198. {
  199.     return if $NO_VALIDATION && ! defined wantarray;
  200.  
  201.     my $p = $_[0];
  202.  
  203.     my $specs = $_[1];
  204.     local $options = _get_options( (caller(0))[0] ) unless defined $options;
  205.  
  206.     unless ( $NO_VALIDATION )
  207.     {
  208.         if ( ref $p eq 'ARRAY' )
  209.         {
  210.             # we were called as validate( @_, ... ) where @_ has a
  211.             # single element, a hash reference
  212.             if ( ref $p->[0] )
  213.             {
  214.                 $p = $p->[0];
  215.             }
  216.             elsif ( @$p % 2 )
  217.             {
  218.                 my $called = _get_called();
  219.  
  220.                 $options->{on_fail}->
  221.                     ( "Odd number of parameters in call to $called " .
  222.                       "when named parameters were expected\n" );
  223.             }
  224.             else
  225.             {
  226.                 $p = {@$p};
  227.             }
  228.         }
  229.     }
  230.  
  231.     if ( $options->{normalize_keys} )
  232.     {
  233.         $specs = _normalize_callback( $specs, $options->{normalize_keys} );
  234.         $p = _normalize_callback( $p, $options->{normalize_keys} );
  235.     }
  236.     elsif ( $options->{ignore_case} || $options->{strip_leading} )
  237.     {
  238.     $specs = _normalize_named($specs);
  239.     $p = _normalize_named($p);
  240.     }
  241.  
  242.     if ($NO_VALIDATION)
  243.     {
  244.         return
  245.             ( wantarray ?
  246.               (
  247.                # this is a hash containing just the defaults
  248.                ( map { $_ => $specs->{$_}->{default} }
  249.                  grep { ref $specs->{$_} && exists $specs->{$_}->{default} }
  250.                  keys %$specs
  251.                ),
  252.                ( ref $p eq 'ARRAY' ?
  253.                  ( ref $p->[0] ?
  254.                    %{ $p->[0] } :
  255.                    @$p ) :
  256.                  %$p
  257.                )
  258.               ) :
  259.               do
  260.               {
  261.                   my $ref =
  262.                       ( ref $p eq 'ARRAY' ?
  263.                         ( ref $p->[0] ?
  264.                           $p->[0] :
  265.                           {@$p} ) :
  266.                         $p
  267.                       );
  268.  
  269.                   foreach ( grep { ref $specs->{$_} && exists $specs->{$_}->{default} }
  270.                             keys %$specs )
  271.                   {
  272.                       $ref->{$_} = $specs->{$_}->{default}
  273.                           unless exists $ref->{$_};
  274.                   }
  275.  
  276.                   return $ref;
  277.               }
  278.             );
  279.     }
  280.  
  281.     _validate_named_depends($p, $specs);
  282.  
  283.     unless ( $options->{allow_extra} )
  284.     {
  285.         my $called = _get_called();
  286.  
  287.     if ( my @unmentioned = grep { ! exists $specs->{$_} } keys %$p )
  288.     {
  289.         $options->{on_fail}->
  290.                 ( "The following parameter" . (@unmentioned > 1 ? 's were' : ' was') .
  291.                   " passed in the call to $called but " .
  292.                   (@unmentioned > 1 ? 'were' : 'was') .
  293.                   " not listed in the validation options: @unmentioned\n" );
  294.     }
  295.     }
  296.  
  297.     my @missing;
  298.  
  299.     # the iterator needs to be reset in case the same hashref is being
  300.     # passed to validate() on successive calls, because we may not go
  301.     # through all the hash's elements
  302.     keys %$specs;
  303.  OUTER:
  304.     while ( my ($key, $spec) = each %$specs )
  305.     {
  306.     if ( ! exists $p->{$key} &&
  307.              ( ref $spec
  308.                ? ! (
  309.                     do
  310.                     {
  311.                         # we want to short circuit the loop here if we
  312.                         # can assign a default, because there's no need
  313.                         # check anything else at all.
  314.                         if ( exists $spec->{default} )
  315.                         {
  316.                             $p->{$key} = $spec->{default};
  317.                             next OUTER;
  318.                         }
  319.                     }
  320.                     ||
  321.                     do
  322.                     {
  323.                         # Similarly, an optional parameter that is
  324.                         # missing needs no additional processing.
  325.                         next OUTER if $spec->{optional};
  326.                     }
  327.                    )
  328.                : $spec
  329.              )
  330.            )
  331.         {
  332.             push @missing, $key;
  333.     }
  334.         # Can't validate a non hashref spec beyond the presence or
  335.         # absence of the parameter.
  336.         elsif (ref $spec)
  337.         {
  338.         my $value = defined $p->{$key} ? qq|"$p->{$key}"| : 'undef';
  339.         _validate_one_param( $p->{$key}, $p, $spec, "The '$key' parameter ($value)" );
  340.     }
  341.     }
  342.  
  343.     if (@missing)
  344.     {
  345.         my $called = _get_called();
  346.  
  347.     my $missing = join ', ', map {"'$_'"} @missing;
  348.     $options->{on_fail}->
  349.             ( "Mandatory parameter" .
  350.               (@missing > 1 ? 's': '') .
  351.               " $missing missing in call to $called\n" );
  352.     }
  353.  
  354.     return wantarray ? %$p : $p;
  355. }
  356.  
  357. sub validate_with
  358. {
  359.     return if $NO_VALIDATION && ! defined wantarray;
  360.  
  361.     my %p = @_;
  362.  
  363.     local $options = _get_options( (caller(0))[0], %p );
  364.  
  365.     unless ( $NO_VALIDATION )
  366.     {
  367.         unless ( exists $options->{called} )
  368.         {
  369.             $options->{called} = (caller( $options->{stack_skip} ))[3];
  370.         }
  371.  
  372.     }
  373.  
  374.     if ( UNIVERSAL::isa( $p{spec}, 'ARRAY' ) )
  375.     {
  376.     return validate_pos( @{ $p{params} }, @{ $p{spec} } );
  377.     }
  378.     else
  379.     {
  380.         # intentionally ignore the prototype because this contains
  381.         # either an array or hash reference, and validate() will
  382.         # handle either one properly
  383.     return &validate( $p{params}, $p{spec} );
  384.     }
  385. }
  386.  
  387. sub _normalize_callback
  388. {
  389.     my ( $p, $func ) = @_;
  390.  
  391.     my %new;
  392.  
  393.     foreach my $key ( keys %$p )
  394.     {
  395.         my $new_key = $func->( $key );
  396.  
  397.         unless ( defined $new_key )
  398.         {
  399.             die "The normalize_keys callback did not return a defined value when normalizing the key '$key'";
  400.         }
  401.  
  402.         if ( exists $new{$new_key} )
  403.         {
  404.             die "The normalize_keys callback returned a key that already exists, '$new_key', when normalizing the key '$key'";
  405.         }
  406.  
  407.         $new{$new_key} = $p->{ $key };
  408.     }
  409.  
  410.     return \%new;
  411. }
  412.  
  413. sub _normalize_named
  414. {
  415.     # intentional copy so we don't destroy original
  416.     my %h = %{ $_[0] };
  417.  
  418.     if ( $options->{ignore_case} )
  419.     {
  420.     foreach (keys %h)
  421.     {
  422.         $h{ lc $_ } = delete $h{$_};
  423.     }
  424.     }
  425.  
  426.     if ( $options->{strip_leading} )
  427.     {
  428.     foreach my $key (keys %h)
  429.     {
  430.         my $new;
  431.         ($new = $key) =~ s/^\Q$options->{strip_leading}\E//;
  432.         $h{$new} = delete $h{$key};
  433.     }
  434.     }
  435.  
  436.     return \%h;
  437. }
  438.  
  439. sub _validate_one_param
  440. {
  441.     my ($value, $params, $spec, $id) = @_;
  442.  
  443.     if ( exists $spec->{type} )
  444.     {
  445.     unless ( _get_type($value) & $spec->{type} )
  446.     {
  447.             my $type = _get_type($value);
  448.  
  449.         my @is = _typemask_to_strings($type);
  450.         my @allowed = _typemask_to_strings($spec->{type});
  451.         my $article = $is[0] =~ /^[aeiou]/i ? 'an' : 'a';
  452.  
  453.             my $called = _get_called(1);
  454.  
  455.         $options->{on_fail}->
  456.                 ( "$id to $called was $article '@is', which " .
  457.                   "is not one of the allowed types: @allowed\n" );
  458.     }
  459.     }
  460.  
  461.     # short-circuit for common case
  462.     return unless ( $spec->{isa} || $spec->{can} ||
  463.                     $spec->{callbacks} || $spec->{regex} );
  464.  
  465.     if ( exists $spec->{isa} )
  466.     {
  467.     foreach ( ref $spec->{isa} ? @{ $spec->{isa} } : $spec->{isa} )
  468.     {
  469.         unless ( UNIVERSAL::isa( $value, $_ ) )
  470.         {
  471.         my $is = ref $value ? ref $value : 'plain scalar';
  472.         my $article1 = $_ =~ /^[aeiou]/i ? 'an' : 'a';
  473.         my $article2 = $is =~ /^[aeiou]/i ? 'an' : 'a';
  474.  
  475.                 my $called = _get_called(1);
  476.  
  477.         $options->{on_fail}->
  478.                     ( "$id to $called was not $article1 '$_' " .
  479.                       "(it is $article2 $is)\n" );
  480.         }
  481.     }
  482.     }
  483.  
  484.     if ( exists $spec->{can} )
  485.     {
  486.     foreach ( ref $spec->{can} ? @{ $spec->{can} } : $spec->{can} )
  487.     {
  488.             unless ( UNIVERSAL::can( $value, $_ ) )
  489.             {
  490.                 my $called = _get_called(1);
  491.  
  492.                 $options->{on_fail}->( "$id to $called does not have the method: '$_'\n" );
  493.             }
  494.     }
  495.     }
  496.  
  497.     if ( $spec->{callbacks} )
  498.     {
  499.         unless ( UNIVERSAL::isa( $spec->{callbacks}, 'HASH' ) )
  500.         {
  501.             my $called = _get_called(1);
  502.  
  503.             $options->{on_fail}->
  504.                 ( "'callbacks' validation parameter for $called must be a hash reference\n" );
  505.         }
  506.  
  507.  
  508.     foreach ( keys %{ $spec->{callbacks} } )
  509.     {
  510.             unless ( UNIVERSAL::isa( $spec->{callbacks}{$_}, 'CODE' ) )
  511.             {
  512.                 my $called = _get_called(1);
  513.  
  514.                 $options->{on_fail}->( "callback '$_' for $called is not a subroutine reference\n" );
  515.             }
  516.  
  517.             unless ( $spec->{callbacks}{$_}->($value, $params) )
  518.             {
  519.                 my $called = _get_called(1);
  520.  
  521.                 $options->{on_fail}->( "$id to $called did not pass the '$_' callback\n" );
  522.             }
  523.     }
  524.     }
  525.  
  526.     if ( exists $spec->{regex} )
  527.     {
  528.         unless ( $value =~ /$spec->{regex}/ )
  529.         {
  530.             my $called = _get_called(1);
  531.  
  532.             $options->{on_fail}->( "$id to $called did not pass regex check\n" );
  533.         }
  534.     }
  535. }
  536.  
  537. {
  538.     # if it UNIVERSAL::isa the string on the left then its the type on
  539.     # the right
  540.     my %isas = ( 'ARRAY'  => ARRAYREF,
  541.          'HASH'   => HASHREF,
  542.          'CODE'   => CODEREF,
  543.          'GLOB'   => GLOBREF,
  544.          'SCALAR' => SCALARREF,
  545.            );
  546.     my %simple_refs = map { $_ => 1 } keys %isas;
  547.  
  548.     sub _get_type
  549.     {
  550.     return UNDEF unless defined $_[0];
  551.  
  552.     my $ref = ref $_[0];
  553.     unless ($ref)
  554.     {
  555.         # catches things like:  my $fh = do { local *FH; };
  556.         return GLOB if UNIVERSAL::isa( \$_[0], 'GLOB' );
  557.         return SCALAR;
  558.     }
  559.  
  560.     return $isas{$ref} if $simple_refs{$ref};
  561.  
  562.     foreach ( keys %isas )
  563.     {
  564.         return $isas{$_} | OBJECT if UNIVERSAL::isa( $_[0], $_ );
  565.     }
  566.  
  567.     # I really hope this never happens.
  568.     return UNKNOWN;
  569.     }
  570. }
  571.  
  572. {
  573.     my %type_to_string = ( SCALAR()    => 'scalar',
  574.                ARRAYREF()  => 'arrayref',
  575.                HASHREF()   => 'hashref',
  576.                CODEREF()   => 'coderef',
  577.                GLOB()      => 'glob',
  578.                GLOBREF()   => 'globref',
  579.                SCALARREF() => 'scalarref',
  580.                UNDEF()     => 'undef',
  581.                OBJECT()    => 'object',
  582.                UNKNOWN()   => 'unknown',
  583.              );
  584.  
  585.     sub _typemask_to_strings
  586.     {
  587.     my $mask = shift;
  588.  
  589.     my @types;
  590.     foreach ( SCALAR, ARRAYREF, HASHREF, CODEREF, GLOB, GLOBREF,
  591.                   SCALARREF, UNDEF, OBJECT, UNKNOWN )
  592.     {
  593.         push @types, $type_to_string{$_} if $mask & $_;
  594.     }
  595.     return @types ? @types : ('unknown');
  596.     }
  597. }
  598.  
  599. {
  600.     my %defaults = ( ignore_case   => 0,
  601.              strip_leading => 0,
  602.              allow_extra   => 0,
  603.              on_fail       => sub { require Carp;
  604.                                             Carp::confess($_[0]) },
  605.              stack_skip    => 1,
  606.                      normalize_keys => undef,
  607.            );
  608.  
  609.     *set_options = \&validation_options;
  610.     sub validation_options
  611.     {
  612.     my %opts = @_;
  613.  
  614.     my $caller = caller;
  615.  
  616.     foreach ( keys %defaults )
  617.     {
  618.         $opts{$_} = $defaults{$_} unless exists $opts{$_};
  619.     }
  620.  
  621.     $OPTIONS{$caller} = \%opts;
  622.     }
  623.  
  624.     sub _get_options
  625.     {
  626.     my ( $caller, %override ) = @_;
  627.  
  628.         if ( %override )
  629.         {
  630.             return
  631.                 ( $OPTIONS{$caller} ?
  632.                   { %{ $OPTIONS{$caller} },
  633.                     %override } :
  634.                   { %defaults, %override }
  635.                 );
  636.         }
  637.         else
  638.         {
  639.             return
  640.                 ( exists $OPTIONS{$caller} ?
  641.                   $OPTIONS{$caller} :
  642.                   \%defaults );
  643.         }
  644.     }
  645. }
  646.  
  647. sub _get_called
  648. {
  649.     my $extra_skip = $_[0] || 0;
  650.  
  651.     # always add one more for this sub
  652.     $extra_skip++;
  653.  
  654.     my $called =
  655.         ( exists $options->{called} ?
  656.           $options->{called} :
  657.           ( caller( $options->{stack_skip} + $extra_skip ) )[3]
  658.         );
  659.  
  660.     $called = 'N/A' unless defined $called;
  661.  
  662.     return $called;
  663. }
  664.  
  665. 1;
  666.  
  667. __END__
  668.  
  669. =head1 NAME
  670.  
  671. Params::ValidatePP - pure Perl implementation of Params::Validate
  672.  
  673. =head1 SYNOPSIS
  674.  
  675.   See Params::Validate
  676.  
  677. =head1 DESCRIPTION
  678.  
  679. This is a pure Perl implementation of Params::Validate.  See the
  680. Params::Validate documentation for details.
  681.  
  682. =cut
  683.