Class::CGI - Fetch objects from your CGI object
Version 0.20
use Class::CGI handlers => { customer_id => 'My::Customer::Handler' }; my $cgi = Class::CGI->new; my $customer = $cgi->param('customer_id'); my $name = $customer->name; my $email = $cgi->param('email'); # behaves like normal if ( my %errors = $cgi->errors ) { # do error handling }
For small CGI scripts, it's common to get a parameter, untaint it, pass it to an object constructor and get the object back. This module would allow one to to build Class::CGI handler classes which take the parameter value, automatically perform those steps and just return the object. Much grunt work goes away and you can get back to merely pretending to work.
Class::CGI
Note that this work is still under development. It is not yet suitable for production work as the interface may change. Join the mailing list in the SUPPORT section if you would like to influence the future direction of this project.
None.
The simplest method of using Class::CGI is to simply specify each form parameter's handler class in the import list:
use Class::CGI handlers => { customer => 'My::Customer::Handler', sales => 'Sales::Loader' }; my $cgi = Class::CGI->new; my $customer = $cgi->param('customer'); my $email = $cgi->param('email'); # validate email $customer->email($email); $customer->save;
Note that there is no naming requirement for the handler classes and any form parameter which does not have a handler class behaves just like a normal form parameter. Each handler class is expected to have a constructor named new which takes the raw form value and returns an object corresponding to that value. All untainting and validation is expected to be dealt with by the handler. See "WRITING HANDLERS".
new
If you need different handlers for the same form parameter names (this is common in persistent environments) you may omit the import list and use the handlers method.
handlers
When the handlers are specified, either via the import list or the handlers() method, we verify that the handler exists and croak() if it is not. However, we do not load the handler until the parameter for that handler is fetched. This allows us to not load unused handlers but still have a semblance of safety that the handlers actually exist.
handlers()
croak()
my $cgi = Class::CGI->new(@args);
This method takes the same arguments (if any) as CGI::Simple's constructor.
use Class::CGI; my $cust_cgi = Class::CGI->new; $cust_cgi->handlers( customer => 'My::Customer::Handler', ); my $order_cgi = Class::CGI->new($other_params); $order_cgi->handlers( order => 'My::Order::Handler', ); my $customer = $cust_cgi->param('customer'); my $order = $order_cgi->param('order'); $order->customer($customer); my $handlers = $cgi->handlers; # returns hashref of current handlers
Sometimes we get our CGI parameters from different sources. This commonly happens in a persistent environment where the class handlers for one form may not be appropriate for another form. When this occurs, you may set the handler classes on an instance of the Class::CGI object. This overrides global class handlers set in the import list:
use Class::CGI handlers => { customer => "Some::Customer::Handler", order => "My::Order::Handler" }; my $cgi = Class::CGI->new; $cgi->handlers( customer => "Some::Other::Customer::Handler" );
In the above example, the $cgi object will not use the Some::Customer::Handler class. Further, the "order" handler will not be available. Setting hanlders on an makes the global handlers unavailable. If you also needed the "order" handler, you need to specify that in the &handlers method.
$cgi
Some::Customer::Handler
&handlers
If called without arguments, returns a hashref of the current handlers in effect.
$cgi->profiles($profile_file, @use);
If you prefer, you can specify a config file listing the available Class::CGI profile handlers and an optional list stating which of the profiles to use. If the @use list is not specified, all profiles will be used. Otherwise, only those profiles listed in @use will be used. These profiles are used on a per instance basis, similar to &handlers.
@use
See "DEFINING PROFILES" for more information about the profile configuration file.
use Class::CGI handlers => { customer => 'My::Customer::Handler' }; my $cgi = Class::CGI->new; my $customer = $cgi->param('customer'); # returns an object, if found my $email = $cgi->param('email'); # returns the raw value my @sports = $cgi->param('sports'); # behaves like you would expect
If a handler is defined for a particular parameter, the param() calls the new() method for that handler, passing the Class::CGI object and the parameter's name. Returns the value returned by new(). In the example above, for "customer", the return value is essentially:
param()
new()
return My::Customer::Handler->new( $self, 'customer' );
my $id = $cgi->raw_param('customer');
This method returns the actual value of a parameter, ignoring any handlers defined for it.
$cgi->args('customer', \@whatever_you_want); my $args = $cgi->args($param);
This method allows you to pass extra arguments to a handler. Specify the name of the parameter for which you wish to provide the arguments and then provide a single argument (it may be a reference). In your handler, you can access it like this:
package Some::Handler; sub new { my ( $class, $cgi, $param ) = @_; my $args = $cgi->args($param); ... }
if ( my %errors = $cgi->errors ) { ... }
Returns exceptions thrown by handlers, if any. In scalar context, returns a hash reference. Note that these exceptions are generated via the overloaded ¶m method. For example, let's consider the following:
¶m
use Class::CGI handlers => { customer => 'My::Customer::Handler', date => 'My::Date::Handler', order => 'My::Order::Handler', }; my $cgi = Class::CGI->new; my $customer = $cgi->param('customer'); my $date = $cgi->param('date'); my $order = $cgi->param('order'); if ( my %errors = $cgi->errors ) { # do error handling }
If errors are generated by the param statements, returns a hash of the errors. The keys are the param names and the values are whatever exception the handler throws. Returns a hashref in scalar context.
If no errors were generated, this method simply returns. This allows you to do this:
if ( $cgi->errors ) { ... }
If any of the $cgi->param calls generates an error, it will not throw an exception. Instead, control will pass to the next statement. After all $cgi->param calls are made, you can check the &errors method to see if any errors were generated and, if so, handle them appropriately.
$cgi->param
&errors
This allows the programmer to validate the entire set of form data and report all errors at once. Otherwise, you wind up with the problem often seen on Web forms where a customer will incorrectly fill out multiple fields and have the Web page returned for the first error, which gets corrected, and then the page returns the next error, and so on. This is very frustrating for a customer and should be avoided at all costs.
$cgi->clear_errors;
Deletes all errors returned by the &errors method.
$cgi->add_error( $param, $error );
This method add an error for the given parameter.
$cgi->add_missing( $param, $optional_error_message );
Helper function used in handlers to note that a parameter is "missing". This should only be used for "required" parameters. Calling this method with a non-required parameter is a no-op. See the required and is_required methods.
is_required
Missing parameters will be reported via the errors and is_missing_required methods.
if ( $cgi->is_missing_required( $param ) ) { ... }
Returns a boolean value indicating whether or not a required parameter is missing. Always return false for parameters which are not required.
Note that this value is set via the add_missing method.
$cgi->error_encoding( $unsafe_characters );
Error messages must be properly escaped for display in HTML. We use HTML::Entities to handle the encoding. By default, this encodes control characters, high bit characters, and the "<", "&", ">", "'" and """ characters. This should suffice for most uses.
HTML::Entities
If you need to specify a different set of characters to encode, you may set them with this method. See the encode_entities documentation in HTML::Entities for details on the $unsafe_characters.
encode_entities
$unsafe_characters
$cgi->required(@required_parameters);
Allows you to set which parameters are required for this Class::CGI object. Any previous "required" parameters will be cleared.
if ( $cgi->is_required($param) ) { ... }
Generally used in handlers, this method returns a boolean value indicating whether or not a given parameter is required.
Handlers are usually pretty easy to write. There are a few simple rules to remember.
Inherit from Class::CGI::Handler.
Provide a method named handle which takes $self as an argument.
handle
$self
Return whatever value you want.
For virtual parameters, override the has_param method.
has_param
And that's pretty much it. See the Class::CGI::Handler documentation for what methods are available to call on $self. The ones which will probably always be used are the cgi and param methods.
cgi
param
Writing a handler is a fairly straightforward affair. Let's assume that our form has a parameter named "customer" and this parameter should point to a customer ID. The ID is assumed to be a positive integer value. For this example, we assume that our customer class is named My::Customer and we load a customer object with the load_from_id() method. The handler might look like this:
My::Customer
load_from_id()
package My::Customer::Handler; use base 'Class::CGI::Handler'; use My::Customer; sub handle { my $self = shift; my $cgi = $self->cgi; my $param = $self->param; my $id = $cgi->raw_param($param); unless ( $id && $id =~ /^\d+$/ ) { die "Invalid id ($id) for $class"; } return My::Customer->load_from_id($id) || die "Could not find customer for ($id)"; } 1;
Pretty simple, eh?
Using this in your code is as simple as:
use Class::CGI handlers => { customer => 'My::Customer::Handler', };
If Class::CGI is being used in a persistent environment and other forms might have a param named customer but this param should not become a My::Customer object, then set the handler on the instance instead:
customer
use Class::CGI; my $cgi = Class::CGI->new; $cgi->handlers( customer => 'My::Customer::Handler' );
Important: Note that earlier versions of Class::CGI listed handlers with names like Class::CGI::Order. It is recommended that you not use the Class::CGI:: namespace to avoid possibly conflicts with handlers which may be released to the CPAN in this namespace unless you also intend to release your module to the CPAN in this namespace.
Class::CGI::Order
Class::CGI::
As a more common example, let's say you have the following data in a form:
<select name="month"> <option value="01">January</option> ... <option value="12">December</option> </select> <select name="day"> <option value="01">1</option> ... <option value="31">31</option> </select> <select name="year"> <option value="2006">2006</option> ... <option value="1900">1900</option> </select>
Ordinarily, pulling all of that out, untainting it is a pain. Here's a hypothetical handler for it:
package My::Date::Handler; use base 'Class::CGI::Handler'; use My::Date; sub handle { my $self = shift; my $cgi = $self->cgi; my $month = $cgi->raw_param('month'); my $day = $cgi->raw_param('day'); my $year = $cgi->raw_param('year'); return My::Date->new( month => $month, day => $day, year => $year, ); } # because this is a virtual parameter, we must override the has_param() # method. sub has_param { my $self = shift; return $self->has_virtual_param( date => qw/day month year/ ); } 1;
And in the user's code:
use Class::CGI handlers => { date => 'My::Date::Handler', }; my $cgi = Class::CGI->new; my $date = $cgi->param('date'); my $day = $date->day;
Note that this does not even require an actual param named "date" in the form. The handler encapsulates all of that and the end user does not need to know the difference.
Note that the parameter a user fetches might not exist on the form. In the $cgi->param('date') example above, there is no "date" parameter. Instead, it's a composite formed of other fields. It's strongly recommended that if you have a handler which uses virtual parameters that you do not use a parameter with the same name. If you must, you can still access the value of the real parameter with $cgi->raw_param('date');.
$cgi->param('date')
$cgi->raw_param('date');
Sometimes you might want to use a handler more than once for the same set of data. For example, you might want to have more than one date on a page. To handle issues like this, we pass in the parameter name to the constructor so you can know which date you're trying to fetch.
So for example, let's say their are three dates in a form. One is the customer birth date, one is an order date and one is just a plain date. Maybe our code will look like this:
$cgi->handlers( birth_date => 'My::Date::Handler', order_date => 'My::Date::Handler', date => 'My::Date::Handler', );
One way of handling that would be the following:
package My::Date::Handler; use base 'Class::CGI::Handler'; use strict; use warnings; use My::Date; sub handle { my $self = shift; my $cgi = $self->cgi; my $param = $self->param; my $prefix; if ( 'date' eq $param ) { $prefix = ''; } else { ($prefix = $param) =~ s/date$//; } my ( $day, $month, $year ) = grep {defined} map { $cgi->raw_param($_) } $self->components; return My::Date->new( day => $day, month => $month, year => $year, ); } sub components { my $self = shift; my $cgi = $self->cgi; my $param = $self->param; my $prefix; if ( 'date' eq $param ) { $prefix = ''; } else { ($prefix = $param) =~ s/date$//; } return map { "$prefix$_" } qw/day month year/; } sub has_param { my $self = shift; return $self->has_virtual_param( $self->param, $self->components ); } 1;
For that, the birthdate will be built from params named birth_day, birth_month and birth_year. The order date would be order_day and so on. The "plain" date would be built from params named day, month, and year. Thus, all three could be accessed as follows:
birth_day
birth_month
birth_year
order_day
day
month
year
my $birthdate = $cgi->param('birth_date'); my $order_date = $cgi->param('order_date'); my $date = $cgi->param('date');
Handlers for parameters may be defined in an import list:
use Class::CGI handlers => { customer => 'My::Customer::Handler', order_date => 'My::Date::Handler', order => 'My::Order::Handler', };
For larger sites, it's not very practical to replicate this in all code which needs it. Instead, Class::CGI allows you to define a "profiles" file. This is a configuration file which should match the Config::Std format. At the present time, only one section, "profiles", is supported. This should be followed by a set of colon-delimited key/value pairs specifying the CGI parameter name and the handler class for the parameter. The above import list could be listed like this in the file:
Config::Std
[profiles] customer: My::Customer::Handler order_date: My::Date::Handler order: My::Order::Handler
You may then use the profiles in your code as follows:
use Class::CGI profiles => $location_of_profile_file;
It may be the case that you don't want all of the profiles. In that case, you can list a "use" section for that:
use Class::CGI profiles => $location_of_profile_file, use => [qw/ order_date order /];
As with &handlers, you may find that you don't want the profiles globally applied. In that case, use the &profiles method described above:
&profiles
$cgi->profiles( $profile_file, @optional_list_of_profiles_to_use );
Because this module is a subclass of CGI::Simple, all of CGI::Simple's methods and behaviors should be available. We do not subclass off of CGI because CGI::Simple is faster and it's assumed that if we're going the full OO route that we are already using templates. Thus, the CGI HTML generation methods are not available and should not be needed. This decision may be revisited in the future.
CGI::Simple
CGI
More to the point, CGI.pm, while being faster and more lightweight than most people give it credit for, is a pain to subclass. Further, it would need to be subclassed without exposing the functional interface due to the need to maintain state in Class::CGI.
When handlers are specified, either at compile time or setting them on an instance, the existence of the handlers is verified. However, the handlers are not loaded until used, thus reducing memory usage if they are not needed.
In a similar vein, if you choose to use a profile file (see "Creating a profile file"), Config::Std is used. However, that module is also not loaded unless needed.
The biggest complaint about CGI::Simple seems to be that it's "reinventing the wheel". Before you agree with that complaint, see http://www.perlmonks.org/?node_id=543742. Pointy-haired boss summary of that link: you had better reinvent the wheel if you're creating a motorcycle instead of a car.
There's nothing wrong with Data::FormValidator. It's fast, powerful, and well-proven in its approach. Class::CGI, in fact, can easily benefit from Data::FormValidator inside of handler classes. However, the approach we take is fundamentally different. First, instead of learning a list of required hash keys and trying to remember what optional_regexp, filters, field_filter_regexp_map, dependency_groups and so on do, you just need to know that a handler constructor takes a Class::CGI instance and the parameter name. Everything else is just normal Perl code, no memorization required.
Data::FormValidator
optional_regexp
filters
field_filter_regexp_map
dependency_groups
With Class::CGI, you can pick and choose what handlers you wish to support for a given piece of code. You can have a global set of handlers to enforce consistency in your Web site or you can have "per page" handlers set up as needed.
This module should be considered alpha code. It probably has bugs. Comments and suggestions welcome.
Curtis "Ovid" Poe, <ovid@cpan.org>
<ovid@cpan.org>
There is a mailing list at http://groups.yahoo.com/group/class_cgi/. Currently it is low volume. That might change in the future.
Please report any bugs or feature requests to bug-class-cgi@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Class-CGI. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
bug-class-cgi@rt.cpan.org
If you are unsure if a particular behavior is a bug, feel free to send mail to the mailing list.
This module is based on the philosophy of building super-simple code which solves common problems with a minimum of memorization. That being said, it may not be the best fit for your code. Here are a few other options to consider.
Data::FormValidator - Validates user input based on input profile
HTML::Widget - HTML Widget And Validation Framework
Rose::HTML::Objects - Object-oriented interfaces for HTML
Thanks to Aristotle for pointing out how useful passing the parameter name to the handler would be.
Copyright 2006 Curtis "Ovid" Poe, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Class::CGI, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Class::CGI
CPAN shell
perl -MCPAN -e shell install Class::CGI
For more information on module installation, please visit the detailed CPAN module installation guide.