Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Overloading subs = weirdness in mod_perl

by cfreak (Chaplain)
on Nov 15, 2004 at 17:23 UTC ( [id://407882]=perlquestion: print w/replies, xml ) Need Help??

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

Let me start off by saying what I'm doing probably isn't entirely sane, but hey, Perl is supposed to be for the insane right? :)

I'm building a large shopping cart type app and I have a base set of modules. This app is going to be used by several customers and a few of them need some customizations. Since everything happens in modules, every site calls the same startup.pl and then loads a configuration based on PerlSetVar directives in their virtual host setup. The config can load custom modules which in some cases I've overloaded some subs that are located in my core modules. (I do this per request so as not to leave these overloads in memory thus breaking other sites).

In most cases this has worked really well, however I've come to a function where one of the Apache::Request parameters needs to be changed. I reference the original sub to a scalar, then overload and then I call original sub through the reference after I've made my modifications. However when I modify the parameter it appears my scalar reference suddenly maps back to overloaded sub instead of the original, thus creating an infinate self referencing call (oops). Here's the offending code in question: (The Apache::Request object is stored as $self->{apr}

$SWMetal::Buffs::wsc_list_images = \&WSC::Image::list_images; *WSC::Image::list_images = sub { my($self,$msg) = @_; my $dbh = $self->{dbh}; my $itemID = $self->{apr}->param('itemID'); my $query = "SELECT size,cloth,ply,stitch FROM buffs WHERE itemID= +?"; my $sth = $dbh->prepare($query); $sth->execute($itemID) or $self->error($sth->errstr()); my @qparams = $sth->fetchrow(); $sth->finish(); if(@qparams) { my $query = "SELECT itemID FROM buffs WHERE size=? AND cloth=? + AND ply=? AND stitch=?"; $sth = $dbh->prepare($query); $sth->execute(@qparams) or $self->error($sth->errstr()); my @IDs = (); while (my $itemID = $sth->fetchrow()) { push(@IDs,$itemID); } $sth->finish(); # commenting out this line stops the infinate call $self->{apr}->param(itemID=>\@IDs); } $wsc_list_images->(@_); };

The only thing I can figure is that modifying the $self variable changes its type? It seems very odd to me. Has anyone else seen this behavior and if so how did you work around it? I know I could get around it by simply passing the IDs, but I'm trying to avoid modifying the core function since other site rely on its current behavior.

Update Messing with the symbol table in a mod_perl environment has becoming an increasingly bad idea (tm). I fixed this problem using diotalevi's and bart's suggestions and learned something about OO as well :). If anyone's interested ...

Basically my program was backwards. I had a core module that I'm was trying to include and then let it get objects for the needed underlying modules. After doing that I was trying to bolt my customizations on top (thus mucking with the symbol table). Well first of all with this approach, calling methods between modules was a real pain because I had to pass each new call a hash reference that contained the database handle, the Apache::Request handle, a configuration handle and some other stuff. Passing the object itself worked fine, however if I needed to return from one of those calls the object would be re-blessed into the new object and all my previous methods would go out of scope.

The second problem was of course my original question, changing the symbols. This worked fine with a few requests but it appears I must have been getting answers from the same child of Apache. I believe upon getting a new child, creating a reference to a particular function would reference the newly created function by that name in memory and not the original one. This led to the infinate recursive call I talked about earlier. (hey crashing apache was fun!)

Well what I learned is that when you subclass and call a method, Perl looks for that method in your subclass first and then goes looking for it in the base classes until it finds it. By turing everything around and calling my customized module from the handler and overriding the base classes in it, I can create my customizations. Calls between modules are fixed now too because instead of calling new each time and reblessing my object I just subclass those modules as well.

Anyway I hope reading that helps someone else if they attempt a large OO project like mine :)

Replies are listed 'Best First'.
Re: Overloading subs = weirdness in mod_perl
by bart (Canon) on Nov 15, 2004 at 18:40 UTC
    As you seem to be using OO (judging by the looks of the first parameter, with the name $self), why don't you just subclass SWMetal::Buffs? That's more sane than globally override a method, so that the original method no longer exists.

      That would be much better, true. But what I want is all calls from anywhere in the program to WSC::Image::list_images to be mapped to my custom subroutine (for this site). With my somewhat limited knowledge, I didn't think that subclassing would do that. Is there a way to make it work?

      Update: clarified a bit

Re: Overloading subs = weirdness in mod_perl
by diotalevi (Canon) on Nov 15, 2004 at 23:08 UTC

    Just a note, you want to avoid writing functions to your symbol table at runtime. Perl's cache over @ISA is thrown away whenever you do this. This is so that any method calls take into account whatever method it was that you altered.

    Good runtime code won't throw away this cache and especially not on a per-request basis.

      This is a good point, and after a lot of thought I could probably do what I want without messing with the symbol table by modifying a dispatch table. I do have dispatch tables in my handler but they are currently private. I'd have to make them global and change all the internal calls in my modules to reference them.

      Unfortunately that's not something I have time to do at the moment as I'm supposed to be done with the project this week (and even that's not looking good at the moment :) ). I'm hoping there's an easier way.

Re: Overloading subs = weirdness in mod_perl
by revdiablo (Prior) on Nov 15, 2004 at 22:24 UTC
    when I modify the parameter it appears my scalar reference suddenly maps back to overloaded sub instead of the original, thus creating an infinate self referencing call

    I am taking an educated guess, but I think this is because mod_perl runs your code in a persistent environment. It does not "restart" each time. Once you override the WSC::Image::list_images subroutine, it stays like that. Then the next time your code is called, you take a new reference to that subroutine, which is still overridden by your own sub. Thus, the infinite loop.

    You need to work out a way to store the original subroutine reference once and only once, then use that reference every time. Perhaps you could get this working with a BEGIN block, but again, I'm just taking an educated guess.

      I have other subs that work just fine with the same setup. It only happens when the $self->{apr} gets modified. The persistance is not a problem because the correct modules get loaded per request. Even if the name change is persistant for the one site that shouldn't matter (I don't think it is). Somehow the memory location where I stored the original routine gets overwritten ... you know I think I might know the problem ... I wonder if, by modifying anything in the $self variable that it changes the state of chain of references .... hmm I'll have to investigate that further.

      In the meantime I actually worked around the problem by creating a list_images() sub in the SWMetal::Buffs module, for this particular one I didn't need to call my modified routine in the entire program.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://407882]
Approved by Happy-the-monk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2024-04-19 04:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found