geektron has asked for the wisdom of the Perl Monks concerning the following question:

so i'm delving further into my mod_perl experience. i got my baseline handler working, and form reading the docs, if methods are prototyped properly ($$), then they're invoked as object methods. all fine and good. but the question is *when* does the contructor get called?

i have the following code that gives me this error:

[Fri Jul 25 00:58:42 2003] [error] Can't locate object method "server" + via package "My::Greeting" at /webcontent/server_home/test/My/Greeti +ng.pm line 24.
and here's the handler that accompanies it:
package My::Greeting; use strict; use vars qw( $DBH ); use Apache::Constants qw(OK); use Data::Dumper; sub new($$) { my ( $class , %args ) = @_; ## stash a DBH in the object ... $DBH = $args{DBH} || DBI->connect( "dbi:mysql:rentsavers", "root", + "", ) ; my $self = bless { _DBH => $DBH }, $class; return $self; } sub handler($$) { my ( $r, $class ) = @_; my $now = scalar localtime; my $server_name = $r->server->server_hostname; $r->send_http_header('text/plain'); print <<EOT; Thanks for visiting $server_name. The local time is $now. EOT print Dumper( $class ); return OK; }
i understand the error, and know i could fix it by using @ISA ... even if that's not the *right* way to fix it. what's the best way of going about this?

Replies are listed 'Best First'.
Re: mod_perl and objects
by lestrrat (Deacon) on Jul 25, 2003 at 07:04 UTC

    The short answer is that new() doesn't get called.

    The "OO" approach is used solely for inheritance, not for a typical object. A contrived example:

    package MyHandler; sub handler($$) { my($class, $r) = @_; $class->do_this(); $class->do_that(); } sub do_this { # some default behavior } sub do_that { # some default behavior } package MyHandlerSubClass; use base qw(MyHandler); sub do_this { # do something else } sub do_that { ... }

    So in the above example you could extend on MyHandler by subclassing it and changing how do_this() and do_that() behaves. But the handler method will still be called directly without ever "instantiating" anything, you still would be passing the class name.

    In your case, since all you're storing is the DBI object, you could store it in $r->pnotes() earlier in the pipeline, or create a wrapper object/function that does that for you:

    # I use Apache::DBI and also do something that resembles this in my + code, but YMMV package My::DB; sub connect { my $r = Apache->request; my $dbh =$r->pnotes('MyDBIHandle'); if(!$dbh) { $dbh = DBI->connect(...); $r->pnotes('MyDBIHandle' => $dbh); } return $dbh; }
      this is along the lines of what i was looking for, answer-wise.

      in playing around, i figured that new() never gets called, because if i put a $self = $class->new() i get the OO beavior i was expecting.

      i ( wrongly, it seems ) read the docs to mean that new() was auto-magically called ... and couldn't figure out where the magic happened.

Re: mod_perl and objects
by blokhead (Monsignor) on Jul 25, 2003 at 07:02 UTC
    and form reading the docs, if methods are prototyped properly ($$), then they're invoked as object methods.
    Nope! Prototypes and method-calling are orthogonal. The caller can call any sub as a method, and if a sub is called as a method, the prototype is ignored. You should probably get rid of the prototypes unless you have another reason.

    I recommend giving perlobj a good read to understand method calls a bit better. The following two calls are equivalent:

    My::Greeting->handler($r); ## class method My::Greeting::handler('My::Greeting', $r); ## equivalent
    So when your handler sub is called as a method, and puts its first argument into $class, it contains the name of the class (a simple scalar). This makes things a little simpler for doing inheritance and subclassing. And that's why Data::Dumper is printing the name of the class -- perhaps you wanted to Dumper($r) instead? Or call handler as an object method ($obj->handler(...)) instead of a class method?

    blokhead

      In general, you're correct: Perl prototypes have nothing to do with subroutines being called as methods. However the original poster was asking about mod_perl handlers and is quite correct in interpreting the docs to infer a relationship between prototypes and handlers being called as methods.

      When you write a mod_perl handler, you create a module (say YourModule) and in it you create a subroutine called handler. At the appropriate point in the request chain, mod_perl will create a request object $r and call your handler like this:

      YourModule::handler($r)

      However a piece of magic comes into play if you defined your handler sub with a prototype like this:

      sub handler ($$) { my($class, $r) = @_; ...

      In this case, mod_perl will invoke your handler as a class method like this:

      YourModule->handler($r)

      The advantage of this is that someone else can subclass YourModule and override one or two methods, but they can inherit the handler method from your module.

      Actually I'd recommend against using the prototyped handler syntax in new code as it's not compatible with mod_perl 2.0 (which can pass more arguments to a handler). The new way to do it is to use the 'method' attribute on your subroutine definition:

      sub handler : method { my($class, $r) = @_; ...

      This will work in mod_perl 1.0 or 2.0 as long as you're running Perl 5.6 or better.

      The original poster's problem was expecting the handler to be invoked as an object method. If you need an instance of your class then you need to call your constructor to create one yourself in the handler.

        Actually I'd recommend against using the prototyped handler syntax in new code as it's not compatible with mod_perl 2.0 (which can pass more arguments to a handler). The new way to do it is to use the 'method' attribute on your subroutine definition:
        sub handler : method { my($class, $r) = @_; ...
        i'll play around with it. i thought mod_perl 2.0 was still beta... but i see a guide to starting w/ mod_perl 2 on the modperl site
      You should probably get rid of the prototypes unless you have another reason.

      i have no issue getting rid of them. as i said in the other reply, i thought there was some magick with constructors that happened with mod_perl because of the prototypes. i didn't think they really did much ..., and i've never bothered with them before this.

      when i did Dumper( $r ); i got the Request object -- not what i wanted. as previously noted, i thought a My::Greeting object was auto-magickally created.

Re: mod_perl and objects
by ViceRaid (Chaplain) on Jul 25, 2003 at 12:41 UTC

    I think the confusion arises between class method handlers and object method handlers. Both are available, but the 'official' description of mod_perl 1 method handlers doesn't make the distinction very clear. Both are marked by the special prototype.

    A (class) method handler is defined in httpd.conf with something like:

    PerlHandler MyPackage->method

    Here, as in the example you posted, MyPackage::method gets called with "MyPackage" as the first argument, and the request as the second (as if you'd said MyPackage->method($r) in normal perl). As you've seen, there's no magical new called on the way.

    An (object) handler involves the explicit construction of an object at server start-up time, normally in startup.pl. The newly created object is assigned to a package variable, then a method of that variable is specified as a handler

    # in startup.pl $My::handler_obj = $MyPackage->new('foo' => 'bar'); # then, in httpd.conf - NOTE THE '$' SIGIL PerlHandler $My::handler_obj->method

    In this case, what MyPackage->method will receive as arguments is a blessed reference to $My::handler_obj (not the package name, as above), and secondly the request object, as before.

    I don't know whether & how changes to the handler object persist. In particular, if you use object handlers, be careful not to make DBI connections in the new method that gets called in startup.pl, as you'll run into problems. Just use Apache::DBI and connect at handler time; re-use of DBI connections will be dealt with for you.

    HTH
    ViceRaid

      another excellent, on topic response. thanks.

      i didn't think about moving object instantiation out into the startup.pl. that's something else worth fiddling with.

Re: mod_perl and objects
by geektron (Curate) on Jul 25, 2003 at 06:15 UTC
    * update * ( wish i could edit ... )

    if i switch around the handler to:

    my ( $class, $r ) = @_;
    then the handler works, but i only get the package name in the dumper output, rather than the contents of the anonymous hash i'd expect.
    Thanks for visiting devel.comradeburnout.com. The local time is Fri Jul 25 01:12:01 2003. $VAR1 = 'My::Greeting';