NAME

CGI::HashOfArrays - Perl module that implements a hash whose keys can have either single or multiple values, and which can process url-encoded data.

DEPENDENCIES

Perl Version

5.004

Standard Modules

I<none>

Nonstandard Modules

I<none>

SYNOPSIS

use CGI::HashOfArrays 1.02;

my $case_insensitive = 1;
my $complementry_set = 1;

my $params = CGI::HashOfArrays->new( $case_insensitive, 
	$ENV{'HTTP_COOKIE'} || $ENV{'COOKIE'}, '; ', '&' );

my $query_string = '';
if( $ENV{'REQUEST_METHOD'} =~ /^(GET|HEAD)$/ ) {
	$query_string = $ENV{'QUERY_STRING'};
} else {
	read( STDIN, $query_string, $ENV{'CONTENT_LENGTH'} );
}
$params->from_url_encoded_string( $query_string );
$params->trim_bounding_whitespace();  # clean up user input

foreach my $key ($params->keys()) {
	my @values = $params->fetch( $key );
	print "Field '$key' contains: '".join( "','", @values )."'\n";
}

my @record_list = ();

open( KEVOEL, "+<guestbook.txt" ) or die "can't open file: $!\n";
flock( KEVOEL, 2 );
seek( KEVOEL, 0, 2 );
$params->to_file( \*KEVOEL );
seek( KEVOEL, 0, 0 );
until( eof( KEVOEL ) ) {
	push( @record_list, CGI::HashOfArrays->new( 
		$case_insensitive, \*KEVOEL ) );
}
flock( KEVOEL, 8 );
close( KEVOEL );
	
foreach my $record (@record_list) {
	print "\nSubmitted by:".$record->fetch_value( 'name' )."\n";
	print "\nTracking cookie:".$record->fetch_value( 'track' )."\n";
	my %Qs_and_As = $record->fetch_all( ['name', 'track'], $complementary_set );
	foreach my $key (keys %Qs_and_As) {
		my @values = @{$Qs_and_As{$key}};
		print "Question: '$key'\n";
		print "Answers: '".join( "','", @values )."'\n";
	}
}

DESCRIPTION

This Perl 5 object class implements a simple data structure that is similar to a hash except that each key can have several values instead of just one. There are many places that such a structure is useful, such as database records whose fields may be multi-valued, or when parsing results of an html form that contains several fields with the same name. This class can export a wide variety of key/value subsets of its data when only some keys are needed. In addition, this class can parse and create url-encoded strings, such as with http query or cookie strings, or for encoding binary information in a text file.

While you could do tasks similar to this class by making your own hash with array refs for values, you will need to repeat some messy-looking code everywhere you need to use that data, creating a lot of redundant access or parsing code and increasing the risk of introducing errors.

SYNTAX

This class does not export any functions or methods, so you need to call them using indirect notation. This means using Class->function() for functions and $object->method() for methods.

All method parameters and results are passed by value (where appropriate) such that subsequent editing of them will not change values internal to the HoA object; this is the generally accepted behaviour.

Most methods take either KEY or VALUES parameters. KEYs are always treated as scalars and VALUES are taken as a list. Value lists can be passed either as an ARRAY ref, whereupon they are internally flattened, or as an ordinary LIST. If the first VALUES parameter is an ARRAY ref, it is interpreted as being the entire list and subsequent parameters are ignored. If you want to store an actual ARRAY ref as a value, make sure to put it inside another ARRAY ref first, or it will be flattened.

Any method which returns a list will check if it is being called in scalar or list context. If the context wants a scalar then the method returns its list in an ARRAY ref; otherwise, the list is returned as a list. This behaviour is the same whether the returned list is an associative list (hash) or an ordinary list (array). Failures are returned as "undef" in scalar context and "()" in list context. Scalar results are returned as themselves, of course.

FUNCTIONS AND METHODS

new([ CASE[, SOURCE[, *]] ])

This function creates a new CGI::HashOfArrays object and returns it.

The first optional parameter CASE (scalar) specifies whether or not the new object uses case-insensitive keys or not; the default value is false. This attribute can not be changed later, except by calling the initialize() method. Case-insensitivity simplifies matching form field names whose case may have been changed by the web browser while in transit (I have seen it happen).

The second optional parameter, SOURCE is used as initial keys and values for this object. If it is a Hash Ref (normal or of arrays), then the store_all( SOURCE ) method is called to handle it. If the same parameter is an HoA object, then its keys and values are similarly given to store_all( SOURCE ). If SOURCE is a valid file handle then from_file( SOURCE, * ) is used. Otherwise, the method from_url_encoded_string( SOURCE, * ) is used.

initialize([ CASE[, SOURCE[, *]] ])

This method is used by new() to set the initial properties of objects that it creates. Calling it yourself will empty the internal hash. If you provide arguments to this method then the first one, CASE, will initialize the case-insensitivity attribute, and any subsequent parameters will provide initial keys and values for the internal hash. Nothing is returned.

clone([ CLONE[, KEYS[, COMPLEMENT]] ])

This method initializes a new object to have all of the same properties of the current object and returns it. This new object can be provided in the optional argument CLONE (if CLONE is an object of the same class as the current object); otherwise, a brand new object of the current class is used. Only object properties recognized by CGI::HashOfArrays are set in the clone; other properties are not changed. If the optional arguments KEYS and COMPLEMENT are used, then the clone may not have all the keys that the parent does. KEYS is an array ref that specifies a subset of all this object's keys that we want returned. If the boolean COMPLEMENT is true, then the complement of the keys listed in KEYS is returned instead.

ignores_case()

This method returns true if this object uses case-insensitive keys.

keys()

This method returns a list of all this object's keys.

keys_count()

This method returns a count of this object's keys.

values()

This method returns a flattened list of all this object's values.

values_count()

This method returns a count of all this object's values.

exists( KEY )

This method returns true if KEY is in the hash, although it may not have any values.

count( KEY )

This method returns a count of the values that KEY has. It returns failure if KEY doesn't exist.

fetch( KEY )

This method returns a list of all values that KEY has. It returns failure if KEY doesn't exist.

fetch_value( KEY[, INDEX] )

This method returns a single value of KEY, which is at INDEX position in the internal array of values; the default INDEX is 0. It returns failure if KEY doesn't exist.

fetch_first([ KEYS[, COMPLEMENT] ])

This method returns a hash with all this object's keys, but only the first value for each key. The first optional argument, KEYS, is an array ref that specifies a subset of all this object's keys that we want returned. If the second optional boolean argument, COMPLEMENT, is true, then the complement of the keys listed in KEYS is returned instead.

fetch_last([ KEYS[, COMPLEMENT] ])

This method returns a hash with all this object's keys, but only the last value for each key. The first optional argument, KEYS, is an array ref that specifies a subset of all this object's keys that we want returned. If the second optional boolean argument, COMPLEMENT, is true, then the complement of the keys listed in KEYS is returned instead.

fetch_all([ KEYS[, COMPLEMENT] ])

This method returns a hash with all this object's keys and values. The values for each key are contained in an ARRAY ref. The first optional argument, KEYS, is an array ref that specifies a subset of all this object's keys that we want returned. If the second optional boolean argument, COMPLEMENT, is true, then the complement of the keys listed in KEYS is returned instead.

store( KEY, VALUES )

This method adds a new KEY to this object, if it doesn't already exist. The VALUES replace any that may have existed before. This method returns the new count of values that KEY has. The best way to get a key which has no values is to pass an empty ARRAY ref as the VALUES.

store_all( SOURCE )

This method takes one argument, SOURCE, which is an associative list or hash ref or HoA object containing new keys and values to store in this object. The value associated with each key can be either scalar or an array. Symantics are the same as for calling store() multiple times, once for each KEY. Existing keys and values with the same names are replaced.

push( KEY, VALUES )

This method adds a new KEY to this object, if it doesn't already exist. The VALUES are appended to the list of any that existed before. This method returns the new count of values that KEY has.

unshift( KEY, VALUES )

This method adds a new KEY to this object, if it doesn't already exist. The VALUES are prepended to the list of any that existed before. This method returns the new count of values that KEY has.

pop( KEY )

This method removes the last value associated with KEY and returns it. It returns failure if KEY doesn't exist.

shift( KEY )

This method removes the last value associated with KEY and returns it. It returns failure if KEY doesn't exist.

delete( KEY )

This method removes KEY and returns its values. It returns failure if KEY doesn't previously exist.

delete_all()

This method deletes all this object's keys and values and returns them in a hash. The values for each key are contained in an ARRAY ref.

trim_bounding_whitespace()

This method cleans up all of this object's values by trimming any leading or trailing whitespace. The keys are left alone. This would normally be done when the object is representing user input from a form, including when they entered nothing but whitespace, and the program should act like they left the field empty.

to_url_encoded_string([ DELIM[, VALSEP] ])

This method returns a scalar containing all of this object's keys and values encoded in an url-escaped "query string" format. The escaping format specifies that any characters which aren't in [a-zA-Z0-9_ .-] are replaced with a triplet containing a "%" followed by the two-hex-digit representation of the ascii value for the character. Also, any " " (space) is replaced with a "+". Each key and value pair is delimited by a "=". If a key has multiple values, then there are that many "key=value" pairs. The optional argument, DELIM, is a scalar specifying what to use as a delimiter between pairs. This is "&" by default. If a "\n" is given for DELIM, the resulting string would be suitable for writing to a file where each key=value pair is on a separate line. The second optional argument, VALSEP, is a scalar that specifies the delimiter between multiple consecutive values which share a common key, and that key only appears once. For example, SOURCE could be "key1=val1&val2; key2=val3&val4", as is the case with "cookie" strings (DELIM is "; " and VALSEP is "&") or "isindex" queries.

from_url_encoded_string( SOURCE[, DELIM[, VALSEP]] )

This method takes a scalar, SOURCE, containing a set of keys and values encoded in an url-escaped "query string" format, and adds them to this object. The escaping format specifies that any characters which aren't in [a-zA-Z0-9_ .-] are replaced with a triplet containing a "%" followed by the two-hex-digit representation of the ascii value for the character. Also, any " " (space) is replaced with a "+". Each key and value pair is delimited by a "=". If a key has multiple values, then there are that many "key=value" pairs. The first optional argument, DELIM, is a scalar specifying what to use as a delimiter between pairs. This is "&" by default. If a "\n" is given for DELIM, the source string was likely read from a file where each key=value pair is on a separate line. The second optional argument, VALSEP, is a scalar that specifies the delimiter between multiple consecutive values which share a common key, and that key only appears once. For example, SOURCE could be "key1=val1&val2; key2=val3&val4", as is the case with "cookie" strings (DELIM is "; " and VALSEP is "&") or "isindex" queries.

to_file( FH[, DELIM[, VALSEP[, REC_DELIM[, EMPTY]]]]] )

This method encodes all of this object's keys and values using the to_url_encoded_string( DELIM, VALSEP ) method and writes it to the filehandle provided in FH. The optional argument REC_DELIM is a scalar value that will be written to FH before this encoded object, and serves to delimit multiple encoded objects of this class. The default values for [DELIM, VALSEP, REC_DELIM] are ["\n", undef, "\n=\n"]. If the boolean argument EMPTY is true then this object will be written to FH even if it is empty (has no keys), resulting in only a REC_DELIM actually being written. The default behaviour of false prevents this from happening, so only objects containing data are output. This method returns 1 on a successful write, 0 for an empty record that was skipped, and it returns undef on a file-system error.

from_file( FH[, DELIM[, VALSEP[, REC_DELIM[, EMPTY]]]]] )

This method adds keys and values to this object from an encoded record read from the filehandle provided in FH and parsed with from_url_encoded_string( ., DELIM, VALSEP ). The optional argument REC_DELIM is a scalar value that delimits encoded records in the file stream. The default values for [DELIM, VALSEP, REC_DELIM] are ["\n", undef, "\n=\n"]. If the boolean argument EMPTY is true then this object will be initialized to empty (has no keys) if the record delimiter is encountered in the file stream before any valid encoded record. The default behaviour of false prevents this from happening, so the file stream continues to be read until a valid record is found. This method returns 1 on a successful read, 0 for an empty record that was kept (may be end-of-file), and it returns undef on a file-system error.

to_html_encoded_hidden_fields()

This method returns a scalar containing html text which defines a list of hidden form fields whose names and values are all of this object's keys and values. Each list element looks like '<INPUT TYPE="hidden" NAME="key" VALUE="value">'. Where a key has multiple values, a hidden field is made for each value. All keys and values are html-escaped such that any occurances of [&,",<,>] are substitued with [&amp;,&quot;,&gt;,&lt;]. In cases where this object was storing user input that was submitted using 'post', this method can generate the content of a self-referencing form, should the main program need to call itself. It would handle persistant data which is too big to put in a self-referencing query string.

AUTHOR

Copyright (c) 1999-2000, Darren R. Duncan. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. However, I do request that this copyright information remain attached to the file. If you modify this module and redistribute a changed version then please attach a note listing the modifications.

I am always interested in knowing how my work helps others, so if you put this module to use in any of your own code then please send me the URL. Also, if you make modifications to the module because it doesn't work the way you need, please send me a copy so that I can roll desirable changes into the main release.

Address comments, suggestions, and bug reports to perl@DarrenDuncan.net.

SEE ALSO

perl(1).