Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: ModPerl: ModPerl

Is it me or is mod_perl extremely dangerous?

 

 

ModPerl modperl RSS feed   Index | Next | Previous | View Threaded


Alain.Desilets at nrc-cnrc

Dec 5, 2011, 1:06 PM

Post #1 of 16 (1682 views)
Permalink
Is it me or is mod_perl extremely dangerous?

I'm a complete newbie to mod_perl, and after reading the following documentation:

http://perl.apache.org/docs/1.0/guide/porting.html

I am scared witless by the fact that many variables don't get reinitialized between calls to the CGI scripts.

Particularly scary is the example provided on that page, where the authentication status is stored in a global variable that doesn't get reinitialized. In that example, if Joe logs into the system, and Jane then runs the script, she can get access to the system also without every logging in, because Joe's authentication status is still there. YIKES!

The document says that this kind of problem can easily be avoided by putting all of the functionality into packages, and having the scripts do little more than load a package and invoke a run() method or something.

But I'm not convinced, because package variables are not reinitialized either!

For example, suppose I have a script login.cgi, which does something like this:

----
Use Login;
Login::run();
----

And suppose that a package variable $Login::credentials is used to store the user's credentials (instead of passing them around from function to function inside the package). In that situation, you could also end up with the same problem, because $Login::credentials does not get reinitialized between invocations of the script.

Fortunately for me and my team, we mostly use an object oriented approach, and we would never implement the login in that way. Instead, we would have a class Login, and the credentials would be an instance variable of the Login class. Since instance variables are necessarily reinitialized upon construction, the credentials would not be carried over from script invocation to script invocation.

But... we don't have control over how third party modules were implemented, and we use A LOT OF THEM. So I am still very concerned about that, because we could end up using a third party module that makes use of package variables in a way that is not mod_perl friendly.

Even with our object oriented approach, there are cases where we still use package variables to store a single instance of a class object that:


- Is expensive to instantiate

- Never needs to have more than one instance in the system


In other words, in some limited cases, we use package variables to implement the Singleton design pattern. In those situations, our Singleton would not get reinitialized between script invocations.

Am I being too conservative here, or am I right to be that nervous?

What precautions can we take to prevent this sort of thing from happening?

Thx.

Alain



Alain Désilets
Agent de recherche | Research Officer
Institut de technologie de l'information | Institute for Information Technology Conseil national de recherches du Canada | National Research Council of Canada


Alain.Desilets at nrc-cnrc

Dec 5, 2011, 1:33 PM

Post #2 of 16 (1673 views)
Permalink
RE: Is it me or is mod_perl extremely dangerous? [In reply to]

Hum... I'm puzzled.

According to this page:

http://docstore.mik.ua/orelly/weblinux2/apache/ch17_05.htm

One of the ways to avoid these kinds of problems is to:

"Put your code into modules as subroutines and call it from the main script - for some reason global variables in the module will be initialized"

But this is not what I am experiencing. It seems to me that module variables are NOT being reinitialized.

Towards the end of this email is a script (try_mode_perl.cgi) and a module (TryModPerl.pm) that I wrote to test how different types of variables get reinitialized or not.

When I run it say, 6 times, I get the output which is at the end of this email.

The section of the output that says:

---
$TryModPerl::package_var_noprefix=6
$TryModPerl::package_var_my=5
$TryModPerl::package_var_local=5
$TryModPerl::package_var_our=5
---

Seems to indicate that the package level variables are not getting reinitialized, whether they are declared in any of the following forms.

----
package TryModPerl;

$package_var_noprefix = 1;
my $package_var_my = 1;
local $package_var_local = 1;
our $package_var_local = 1;
----

Is this normal behavior? Note that I am running on Windows 7.

Thx

Alain

=== try_mod_perl.cgi:
#!perl -w

use CGI;

$script_var_noprefix = 1;
my $script_var_my = 1;
local $script_var_local = 1;
our $script_var_our = 1;

sub run {

my $cgi = CGI->new();
print $cgi->header();
print $cgi->start_html();

print_various_vars($cgi);

print $cgi->end_html();

}

sub print_various_vars {
my ($cgi) = @_;
my $txt =
"With a normal CGI environment, all the variables below should always have a value of 1.";
print $cgi->p($txt);

$txt = "But in a mod_perl environment, some of them may keep increase in value, because they are not reinitialized from one call to the next.";
print $cgi->p("$txt");

print "\$script_var_noprefix=$script_var_noprefix<br>\n";
print "\$script_var_my=$script_var_my<br>\n";
print "\$script_var_local=$script_var_local<br>\n";
print "\$script_var_our=$script_var_our<br>\n";
print "<br>\n";

$script_var_noprefix++;
$script_var_my++;
$script_var_local ++;
$script_var_local ++;

$script_sub_var_noprefix = 1;
my $script_sub_var_my = 1;
local $script_sub_var_local = 1;
our $script_sub_var_our = 1;

print "\$script_sub_var_noprefix=$script_sub_var_noprefix<br>\n";
print "\$script_sub_var_my=$script_sub_var_my<br>\n";
print "\$script_sub_var_local=$script_sub_var_local<br>\n";
print "\$script_sub_var_our=$script_sub_var_our<br>\n";
print "<br>\n";

$script_sub_var_noprefix ++;
$script_sub_var_my ++;
$script_sub_var_local ++;
$script_sub_var_our ++;

print "\$TryModPerl::package_var_noprefix=$TryModPerl::package_var_noprefix<br>\n";
print "\$TryModPerl::package_var_my=$TryModPerl::package_var_my<br>\n";
print "\$TryModPerl::package_var_local=$TryModPerl::package_var_local<br>\n";
print "\$TryModPerl::package_var_our=$TryModPerl::package_var_our<br>\n";
print "<br>\n";

$TryModPerl::package_var_noprefix++;
$TryModPerl::package_var_my++;
$TryModPerl::package_var_local++;
$TryModPerl::package_var_our++;

TryModPerl::print_various_vars($cgi);
}

run();

=== TryModPerl.pm:
package TryModPerl;

$package_var_noprefix = 1;
my $package_var_my = 1;
local $package_var_local = 1;
our $package_var_local = 1;

sub print_various_vars {
my ($cgi) = @_;

$package_sub_var_noprefix = 1;
my $package_sub_var_my = 1;
local $package_sub_var_local = 1;
our $package_sub_var_our = 1;

print "\$package_sub_var_noprefix=$package_sub_var_noprefix<br>\n";
print "\$package_sub_var_my=$package_sub_var_my<br>\n";
print "\$package_sub_var_local=$package_sub_var_local<br>\n";
print "\$package_sub_var_our=$package_sub_var_our<br>\n";
print "<br>\n";

$package_sub_var_noprefix++;
$package_sub_var_my++;
$package_sub_var_local++;
$package_sub_var_our++;
}

1;

=== After 6 invocations, generates output:

With a normal CGI environment, all the variables below should always have a value of 1.

But in a mod_perl environment, some of them may keep increase in value, because they are not reinitialized from one call to the next.
$script_var_noprefix=1
$script_var_my=6
$script_var_local=1
$script_var_our=1

$script_sub_var_noprefix=1
$script_sub_var_my=1
$script_sub_var_local=1
$script_sub_var_our=1

$TryModPerl::package_var_noprefix=6
$TryModPerl::package_var_my=5
$TryModPerl::package_var_local=5
$TryModPerl::package_var_our=5

$package_sub_var_noprefix=1
$package_sub_var_my=1
$package_sub_var_local=1
$package_sub_var_our=1


fred at redhotpenguin

Dec 5, 2011, 1:45 PM

Post #3 of 16 (1635 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

On Mon, Dec 5, 2011 at 1:06 PM, Desilets, Alain
<Alain.Desilets [at] nrc-cnrc> wrote:
> I’m a complete newbie to mod_perl, and after reading the following
> documentation:
>
> http://perl.apache.org/docs/1.0/guide/porting.html
>
> I am scared witless by the fact that many variables don’t get reinitialized
> between calls to the CGI scripts.
>
> Particularly scary is the example provided on that page, where the
> authentication status is stored in a global variable that doesn’t get
> reinitialized. In that example, if Joe logs into the system, and Jane then
> runs the script, she can get access to the system also without every logging
> in, because Joe’s authentication status is still there. YIKES!

If you read through the entire example, you will see the point of the
example is to show what can happen from bad programming.

"Because of sloppy programming, a global variable was not reset at the
beginning of the program and voila, you can easily peek into someone
else's email! Here is an example of sloppy code"

> But I’m not convinced, because package variables are not reinitialized
> either!

"The solution is trivial--reset $authenticated to 0 at the beginning
of the program."

> But… we don’t have control over how third party modules were implemented,
> and we use A LOT OF THEM. So I am still very concerned about that, because
> we could end up using a third party module that makes use of package
> variables in a way that is not mod_perl friendly.

You're always free to write your own modules which you have complete
control over. Most developers find it useful to use CPAN modules
which are generally high quality.


> Am I being too conservative here, or am I right to be that nervous?

I do not think you are justified in stating that mod_perl is
'extremely dangerous'.


> What precautions can we take to prevent this sort of thing from happening?

If you are just starting out with mod_perl, I would skip over the
porting documentation and go straight to the mod_perl2 specific
documentation. I would also suggest reviewing the following links for
mod_perl development best practices

http://perl.apache.org

http://www.modperlcookbook.org/

http://modperlbook.org/


aw at ice-sa

Dec 5, 2011, 2:43 PM

Post #4 of 16 (1626 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

Desilets, Alain wrote:
> I'm a complete newbie to mod_perl, and after reading the following documentation:
>
> http://perl.apache.org/docs/1.0/guide/porting.html
>
> I am scared witless by the fact that many variables don't get reinitialized between calls to the CGI scripts.
>
> Particularly scary is the example provided on that page, where the authentication status is stored in a global variable that doesn't get reinitialized. In that example, if Joe logs into the system, and Jane then runs the script, she can get access to the system also without every logging in, because Joe's authentication status is still there. YIKES!
>
...
Well, yes. mod_perl does not like sloppy programming.
On the other hand, if you take care to always initialise your variables at the beginning
of the script, you will avoid such problems.
And you do initialise your variables always, don't you ?

On the other hand, these global variables which do not get re-initialised automatically,
are extremely useful when what you want is just that : cheap persistence between calls.
Imagine a big table that you need to load once, and then just read repeatedly within your
script. Instead of repeatedly reloading the table at each call to your script, you can
just read it once (when it is still undefined), and then forever use the content across
your cgi-bin's executions.

our $big_table;
unless (defined $big_table) {
$big_table = go_fill_it($from_file) or die "cannot fill the table";
}

Just remember that your script can live in several separate Apache children, and each one
has its own copy of your script (and of the table).
So it is not really an Apache-global table. It is only persistent within this Apache
child, as long as this child lives.


perrin at elem

Dec 5, 2011, 2:51 PM

Post #5 of 16 (1629 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

On Mon, Dec 5, 2011 at 4:06 PM, Desilets, Alain
<Alain.Desilets [at] nrc-cnrc> wrote:
> I am scared witless by the fact that many variables don’t get reinitialized
> between calls to the CGI scripts.

It's harder to write safe code for persistent environments than it is
for CGI. There's no question about that. Unfortunately, you'll have
to get used to it, because nearly all web code written now is for
persistent environments. The performance benefit is big enough to
outweigh the danger.

Keep in mind that the danger here is no greater than it would be for
someone using FastCGI, Java servlets, etc. If you create persistent
variables (i.e. package variables declared without "my") you will have
to be responsible for clearing them when you want them cleared.

- Perrin


fred at redhotpenguin

Dec 6, 2011, 11:09 AM

Post #6 of 16 (1634 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

(cc'ing mod_perl list)

On Tue, Dec 6, 2011 at 5:35 AM, Desilets, Alain
<Alain.Desilets [at] nrc-cnrc> wrote:
> Thx to Fred, Andre and Jon for answering. BTW: I hope I didn't offend anyone. What I really meant was:

No offense taken at all. Welcome to mod_perl!

>
>   "Is it me or is mod_perl *much more slippery than standard CGI*?"
>
> I know that there are lots of folks out there using mod_perl, so I assume it can be used in a safe way. I am just trying to figure out where the slippery patches are, and how easy it will be to slip on them for me, my team, my external collaborators, and even writers of CPAN modules.
>
> The last point is important. Fred wrote:
>
> " Most developers find it useful to use CPAN modules which are generally high quality."
>
> That's what I do also. In fact, I don't think I have ever used a third party module that wasn't from CPAN, and they are, as you say, generally high quality.
>
> However, even those high quality CPAN modules can fall flat on their face when they are used in contexts that they weren't designed for. For example, I recently discovered that several CPAN modules cannot be used safely in a multithreaded or forked environment. Some of them fail "nicely" (i.e. they KNOW they aren't supposed to be used in this kind of environment and tell you at run time), but others just crash the process upon spawning a new fork or thread, and leave you with no indication as to what caused this. I am concerned that similar issues might arise when using certain CPAN modules in a mod_perl context.
>
> ---
> Question: Is this something that has been known to happen (CPAN modules that don't work well in mod_perl), and if so, how common is it?
> ---
>
> Coming back to where the slippery patches are. Initially, it looked to me that the main thing to watch out for was global variables. By this, I mean variables that can be seen from anywhere in the code. I was OK with that, because I never use global variables, and I have taught everyone in my team to stay away from them. Also, I don't see global variables being used in CPAN modules.
>
> But after doing a couple experiments, I now realize that package variables are also unsafe in a mod_perl environment. Although I try to avoid them, I don't consider package variables to be "sloppy". Even though they are public and can be seen outside of the package, they can only be seen by those files which actually load the package. So, I am a bit more uncomfortable with that particular slippery patch, because I know that I and some of my team members use package variables occasionally. I also see lots of CPAN modules that use them as well.
>
> One thing that I think will greatly mitigate the risk for us is that we use Test Driven Development, and have PerlUnit tests for every class in our application. This forces us to write our classes that don't assume they will be used in a single CGI script call context. That's because  the tests process instantiated and uses a the class several times in a number of tests and scenarios. But I think this will only help to a point. It's one thing to have a process that runs dozens of tests on a class, but it's a completely different thing to have a process which uses that class to execute thousands of requests per day, and which stays up and running for months at a time.
>
> I guess the only way to find out whether mod_perl works for us is to try it, while keeping the door for going back to standard CGI in case our app is too brittle for it.
>
> Time to buy a mod_perl cookbook I guess ;-).
>
> Again, thx for your useful answers.
>
> Alain
>
> -----Original Message-----
> From: Fred Moyer [mailto:fred [at] redhotpenguin]
> Sent: Monday, December 05, 2011 4:45 PM
> To: Desilets, Alain
> Cc: mod_perl list
> Subject: Re: Is it me or is mod_perl extremely dangerous?
>
> On Mon, Dec 5, 2011 at 1:06 PM, Desilets, Alain
> <Alain.Desilets [at] nrc-cnrc> wrote:
>> I'm a complete newbie to mod_perl, and after reading the following
>> documentation:
>>
>> http://perl.apache.org/docs/1.0/guide/porting.html
>>
>> I am scared witless by the fact that many variables don't get reinitialized
>> between calls to the CGI scripts.
>>
>> Particularly scary is the example provided on that page, where the
>> authentication status is stored in a global variable that doesn't get
>> reinitialized. In that example, if Joe logs into the system, and Jane then
>> runs the script, she can get access to the system also without every logging
>> in, because Joe's authentication status is still there. YIKES!
>
> If you read through the entire example, you will see the point of the
> example is to show what can happen from bad programming.
>
> "Because of sloppy programming, a global variable was not reset at the
> beginning of the program and voila, you can easily peek into someone
> else's email! Here is an example of sloppy code"
>
>> But I'm not convinced, because package variables are not reinitialized
>> either!
>
> "The solution is trivial--reset $authenticated to 0 at the beginning
> of the program."
>
>> But... we don't have control over how third party modules were implemented,
>> and we use A LOT OF THEM. So I am still very concerned about that, because
>> we could end up using a third party module that makes use of package
>> variables in a way that is not mod_perl friendly.
>
> You're always free to write your own modules which you have complete
> control over.  Most developers find it useful to use CPAN modules
> which are generally high quality.
>
>
>> Am I being too conservative here, or am I right to be that nervous?
>
> I do not think you are justified in stating that mod_perl is
> 'extremely dangerous'.
>
>
>> What precautions can we take to prevent this sort of thing from happening?
>
> If you are just starting out with mod_perl, I would skip over the
> porting documentation and go straight to the mod_perl2 specific
> documentation.  I would also suggest reviewing the following links for
> mod_perl development best practices
>
> http://perl.apache.org
>
> http://www.modperlcookbook.org/
>
> http://modperlbook.org/


perrin at elem

Dec 6, 2011, 11:27 AM

Post #7 of 16 (1623 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

> On Tue, Dec 6, 2011 at 5:35 AM, Desilets, Alain
> <Alain.Desilets [at] nrc-cnrc> wrote:
>> However, even those high quality CPAN modules can fall flat on their face when they are used in contexts that they weren't designed for. For example, I recently discovered that several CPAN modules cannot be used safely in a multithreaded or forked environment.

If you're running on a unix environment, you probably want to run
multi-process, not multithreaded. Most modules that work in CGI will
work here, but you need to be aware of issues with opening files and
sockets before the parent process forks. This is described in detail
in the docs. Is there a specific module that you need that isn't
working for you?

>> Coming back to where the slippery patches are. Initially, it looked to me that the main thing to watch out for was global variables. By this, I mean variables that can be seen from anywhere in the code. I was OK with that, because I never use global variables, and I have taught everyone in my team to stay away from them. Also, I don't see global variables being used in CPAN modules.
>>
>> But after doing a couple experiments, I now realize that package variables are also unsafe in a mod_perl environment.

Well, there's no real distinction between "globals" and "package
variables" in perl. I think that by globals you mean package
variables that are in the current package so they can be referenced
without a package prefix.

Package variables are indeed persistent in mod_perl and similar web
environments. You should only use them for things that you want to
persist between requests. Everything else goes in lexically-scoped
("my") variables.

- Perrin


fred at redhotpenguin

Dec 6, 2011, 11:30 AM

Post #8 of 16 (1622 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

(cc'ing the mod_perl list on this one also, please remember to reply all :)

On Tue, Dec 6, 2011 at 11:28 AM, Dr James A Smith <js5 [at] sanger> wrote:
> On 06/12/2011 19:09, Fred Moyer wrote:
>> (cc'ing mod_perl list)
>>
>> On Tue, Dec 6, 2011 at 5:35 AM, Desilets, Alain
>> <Alain.Desilets [at] nrc-cnrc> wrote:
>>> Thx to Fred, Andre and Jon for answering. BTW: I hope I didn't offend anyone. What I really meant was:
>>
>> No offense taken at all.  Welcome to mod_perl!
>>
>>>
>>>   "Is it me or is mod_perl *much more slippery than standard CGI*?"
>>>
>>> I know that there are lots of folks out there using mod_perl, so I assume it can be used in a safe way. I am just trying to figure out where the slippery patches are, and how easy it will be to slip on them for me, my team, my external collaborators, and even writers of CPAN modules.
>>>
>>> The last point is important. Fred wrote:
>>>
>>> " Most developers find it useful to use CPAN modules which are generally high quality."
>>>
>>> That's what I do also. In fact, I don't think I have ever used a third party module that wasn't from CPAN, and they are, as you say, generally high quality.
>>>
>>> However, even those high quality CPAN modules can fall flat on their face when they are used in contexts that they weren't designed for. For example, I recently discovered that several CPAN modules cannot be used safely in a multithreaded or forked environment. Some of them fail "nicely" (i.e. they KNOW they aren't supposed to be used in this kind of environment and tell you at run time), but others just crash the process upon spawning a new fork or thread, and leave you with no indication as to what caused this. I am concerned that similar issues might arise when using certain CPAN modules in a mod_perl context.
>>>
>>> ---
>>> Question: Is this something that has been known to happen (CPAN modules that don't work well in mod_perl), and if so, how common is it?
>>> ---
>>>
>>> Coming back to where the slippery patches are. Initially, it looked to me that the main thing to watch out for was global variables. By this, I mean variables that can be seen from anywhere in the code. I was OK with that, because I never use global variables, and I have taught everyone in my team to stay away from them. Also, I don't see global variables being used in CPAN modules.
>
> Perl::Critic is your friend in number of places - there are some subtle
> gotchas that people don't realise that are more subtle than the ones
> you mentioned - and perlcritic will point a lot of these out....
>
> In a mod_perl registry script, what is wrong with the following:
>
>  my $user = $self->user_from_cookie( $user_cookie ) if $user_cookie;
>
> ?


Alain.Desilets at nrc-cnrc

Dec 6, 2011, 12:11 PM

Post #9 of 16 (1625 views)
Permalink
RE: Is it me or is mod_perl extremely dangerous? [In reply to]

> > On Tue, Dec 6, 2011 at 5:35 AM, Desilets, Alain
> > <Alain.Desilets [at] nrc-cnrc> wrote:
> >> However, even those high quality CPAN modules can fall flat on their face when > they are used in contexts that they weren't designed for. For example, I recently > discovered that several CPAN modules cannot be used safely in a multithreaded or
> forked environment.
>
> If you're running on a unix environment, you probably want to run
> multi-process, not multithreaded. Most modules that work in CGI will
> work here, but you need to be aware of issues with opening files and
> sockets before the parent process forks. This is described in detail
> in the docs. Is there a specific module that you need that isn't
> working for you?

I'm using Windows. In my experience, modules that don't work well with multithread, also don't work well with fork, and vice-versa.

The one module that I know for sure does not work in a forked or multi-threaded environment is KinoSearch. Because of that, we had to move to the Lucy module instead.

Last summer, I had also concluded (possibly falsely) that there were other modules in my application which were not thread-safe. But I could not identify which ones, because when I ran the app in multi-thread fashion, all I got was a one line error message about not being able to free address such and such. I assumed that this was due to some perl module that was accessing an external DLL, but I can't be sure. I had googled the error message, and I seem to recall that it had been linked to problems with non-thread safe modules. Can't remember the details.

> Package variables are indeed persistent in mod_perl and similar web
> environments. You should only use them for things that you want to
> persist between requests. Everything else goes in lexically-scoped
> ("my") variables.

Not sure what you mean by "lexically scoped".

But declaring a variable as "my" is not sufficient to prevent it from being persistent. You can declare a package variable with "my" and it will persist.

Alain


perrin at elem

Dec 6, 2011, 12:40 PM

Post #10 of 16 (1624 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

On Tue, Dec 6, 2011 at 3:11 PM, Desilets, Alain
<Alain.Desilets [at] nrc-cnrc> wrote:
> I'm using Windows. In my experience, modules that don't work well with multithread, also don't work well with fork, and vice-versa.

I wouldn't expect that. In a multi-process situation, files and
sockets are not shared unless you opened them before forking. DBI is
a classic example of something that works fine in multi-process
situations but may fail in multithreaded ones.

> The one module that I know for sure does not work in a forked or multi-threaded environment is KinoSearch.

I don't use KinoSearch so I don't know why it doesn't work, but I'd
suggest that when something seems to be not working it's worth
checking with the developer or mailing list for the module and
checking on this list if anyone is using it.

> Not sure what you mean by "lexically scoped".

You probably know all about them but haven't heard that term before.
Try http://perl.plover.com/FAQs/Namespaces.html for some explanations
and terminology.

> But declaring a variable as "my" is not sufficient to prevent it from being persistent. You can declare a package variable with "my" and it will persist.

That's true! I assume you mean this:

my $foo = 7;

sub print_foo {
print $foo;
}

That's called a closure. Don't do that unless you want persistence.

Using locally declared variables in subs and passing in arguments
instead of grabbing data directly from variables in a larger scope is
good programming practice, and it's the way to avoid unwanted variable
persistence in mod_perl and FastCGI.

- Perrin


Alain.Desilets at nrc-cnrc

Dec 6, 2011, 12:55 PM

Post #11 of 16 (1626 views)
Permalink
RE: Is it me or is mod_perl extremely dangerous? [In reply to]

> > But declaring a variable as "my" is not sufficient to prevent it from being
> > persistent. You can declare a package variable with "my" and it will persist.
>
> That's true! I assume you mean this:
>
> my $foo = 7;
>
> sub print_foo {
> print $foo;
> }
>
> That's called a closure. Don't do that unless you want persistence.
>
> Using locally declared variables in subs and passing in arguments
> instead of grabbing data directly from variables in a larger scope is
> good programming practice, and it's the way to avoid unwanted variable
> persistence in mod_perl and FastCGI.

No, I meant more things like this:

-----------

package MyClass;

my $class_level_attribute;

sub new {
my ($class) = @_;
$self->{instance_level_attribute} = undef;
bless $self, $class;
return $self;
}
------------

The idea here is to emulate the concept of "static" (in Java or C++ parlance) class level attributes, i.e., attributes which are tied to the the class itself as opposed to instances of it.

In the above example, every instance of MyClass can have its own value of instance_level_attribute. But there is only one shared value of $class_level_attribute.

This is something that I use sparingly (for example, to track how many instances of a particular class are generated) and AFAIK, it's not considered "bad manners". But it seems like it could get me in trouble in the context of mod_perl.

Note that the above not exactly the same as a static attribute in Java, in the sense that the instance does not actually "inherit" the attribute. In other words, you can't access $class_level_attribute by looking at $self->{class_level_attribute}. But it's the closest thing I have found in Perl for emulating Java style static class attributes.

Alain


perrin at elem

Dec 6, 2011, 1:05 PM

Post #12 of 16 (1623 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

On Tue, Dec 6, 2011 at 3:55 PM, Desilets, Alain
<Alain.Desilets [at] nrc-cnrc> wrote:
> No, I meant more things like this:
>
> -----------
>
> package MyClass;
>
> my $class_level_attribute;
>
> sub new {
>        my ($class) = @_;
>        $self->{instance_level_attribute} = undef;
>        bless $self, $class;
>        return $self;
> }

Well, in this case, $lcass_level_attribute is technically not
persistent, because you didn't use it inside your sub. If you had
though, it would be persistent, just like my example. Both would be
closures.

> This is something that I use sparingly (for example, to track how many instances of a particular class are generated) and AFAIK, it's not considered "bad manners". But it seems like it could get me in trouble in the context of mod_perl.

It's fine to do that, but persistent is the desired effect there
because it's class-level data, so it shouldn't be unexpected.

> Note that the above not exactly the same as a static attribute in Java, in the sense that the instance does not actually "inherit" the attribute. In other words, you can't access $class_level_attribute by looking at $self->{class_level_attribute}. But it's the closest thing I have found in Perl for emulating Java style static class attributes.

If you really want that, you should look at Moose on CPAN.

- Perrin


Tuomo.Salo at cybercom

Dec 7, 2011, 3:32 AM

Post #13 of 16 (1629 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

On Tue, Dec 06, 2011 at 03:55:54PM -0500, Desilets, Alain wrote:
> package MyClass;
>
> my $class_level_attribute;
>
> sub new {
> my ($class) = @_;
> $self->{instance_level_attribute} = undef;
> bless $self, $class;
> return $self;
> }
> ------------
>
> The idea here is to emulate the concept of "static" (in Java or C++ parlance) class level attributes, i.e., attributes which are tied to the the class itself as opposed to instances of it.

Please do not do that.

When you use a "my" variable outside a subroutine, you create a variable
that is completely invisible from anywhere outside of that file. This
violates some core principles of object oriented programming, and perl
itself:

* Do not attempt to verify the type of $self. (Tenet #1 from "man perlbot")
* The object is the namespace. (#6, same source)
* Don't assume you know the home package of a method (#9)
* There's more than one way to do it (Perl motto from "man perl")

The first one is violated, since you have a variable that can only be
seen in your file, and therefore only one type of objects will ever
be able to see it. Same goes for the third: any method accessing the
variable is by necessity defined in that very package.
Violating the second one is the cause for the other two.

The final one is violated, since if someone has decided to do it,
there is really no way around it.

A demonstration follows.


This is the file MyClass.pm, let's say it is downloaded from CPAN.
It contains a "my" variable at the package level. It also uses the
variable. (Kind of obvious, since nobody else can see it.)

--------------
package MyClass;

my $class_level_attribute = "I Am MyClass";

sub new { my ($class) = @_; bless {}, $class; }
sub output {
print "My type is @{[ref $_[0]]}";
print "The attribute is '$class_level_attribute'";
}
-------------

Now I really like the way MyClass works, but I would like to change just the
bit that is in the variable $class_level_attribute. So I create the file
SubClass.pm:

-------------
package SubClass;

use base MyClass;

my $class_level_attribute = "I want to be SubClass";
$::MyClass::class_level_attribute =
"I want to be SubClass so much that I am ready to break class boundaries";
-------------

Then I'll try to run it by creating demo.pl

-------------
#!/usr/bin/perl -Twl
use strict;
use lib ".";

use SubClass;

my $object = new SubClass();
$object->output();
-------------

..but I won't be happy:

-------------
$ chmod +x demo.pl; ./demo.pl
My type is SubClass
The attribute is 'I Am MyClass'
-------------


Because I am not in control of MyClass.pm, I cannot make changes to that
file. If I do, my software will break whenever MyClass.pm is updated.

My other option is to override every subroutine that uses the class level
attribute. This may also break when the package is updated so that a new
subroutine starts to access the attribute.

My final option is to copy all functionality from the file to some
other file; that is, not to use MyClass after all. This is the only
way I can get the functionality I want. I won't be automatically
benefiting from any updates to MyClass, but that is the price I have to pay.


The correct way to use the scopes for variables is (in my opinion):

- Explicitly global variables ($::Package::variable) in the package's own
namespace for "final" variables (defaults names of existing instance
variables, for example) and anything that you absolutely have to save
to future invocations.
- Accessors for any instance variables ($object->variable()).
Never access an instance variable directly, even if you know the
structure of the object.
- Lexical variables ("my @args" inside a sub) for everything else,
and finally:
- Argument passing ($object->method($variable)) for showing your lexicals
to other subs.

If you do it this way, then easy things are easy, and hard things are possible.

(bonus: if, in addition, you never "use Exporter" to export anything,
you'll also get code where every variable name and function call is
easily traced back to its definition, even without special tools.)

-Tuomo


benvanstaveren at gmail

Dec 7, 2011, 3:42 AM

Post #14 of 16 (1619 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

You're also forgetting:

* use strict
* use warnings

HTH...

On 12/07/2011 06:32 PM, Tuomo Salo wrote:
> On Tue, Dec 06, 2011 at 03:55:54PM -0500, Desilets, Alain wrote:
>> package MyClass;
>>
>> my $class_level_attribute;
>>
>> sub new {
>> my ($class) = @_;
>> $self->{instance_level_attribute} = undef;
>> bless $self, $class;
>> return $self;
>> }
>> ------------
>>
>> The idea here is to emulate the concept of "static" (in Java or C++ parlance) class level attributes, i.e., attributes which are tied to the the class itself as opposed to instances of it.
> Please do not do that.
>
> When you use a "my" variable outside a subroutine, you create a variable
> that is completely invisible from anywhere outside of that file. This
> violates some core principles of object oriented programming, and perl
> itself:
>
> * Do not attempt to verify the type of $self. (Tenet #1 from "man perlbot")
> * The object is the namespace. (#6, same source)
> * Don't assume you know the home package of a method (#9)
> * There's more than one way to do it (Perl motto from "man perl")
>
> The first one is violated, since you have a variable that can only be
> seen in your file, and therefore only one type of objects will ever
> be able to see it. Same goes for the third: any method accessing the
> variable is by necessity defined in that very package.
> Violating the second one is the cause for the other two.
>
> The final one is violated, since if someone has decided to do it,
> there is really no way around it.
>
> A demonstration follows.
>
>
> This is the file MyClass.pm, let's say it is downloaded from CPAN.
> It contains a "my" variable at the package level. It also uses the
> variable. (Kind of obvious, since nobody else can see it.)
>
> --------------
> package MyClass;
>
> my $class_level_attribute = "I Am MyClass";
>
> sub new { my ($class) = @_; bless {}, $class; }
> sub output {
> print "My type is @{[ref $_[0]]}";
> print "The attribute is '$class_level_attribute'";
> }
> -------------
>
> Now I really like the way MyClass works, but I would like to change just the
> bit that is in the variable $class_level_attribute. So I create the file
> SubClass.pm:
>
> -------------
> package SubClass;
>
> use base MyClass;
>
> my $class_level_attribute = "I want to be SubClass";
> $::MyClass::class_level_attribute =
> "I want to be SubClass so much that I am ready to break class boundaries";
> -------------
>
> Then I'll try to run it by creating demo.pl
>
> -------------
> #!/usr/bin/perl -Twl
> use strict;
> use lib ".";
>
> use SubClass;
>
> my $object = new SubClass();
> $object->output();
> -------------
>
> ..but I won't be happy:
>
> -------------
> $ chmod +x demo.pl; ./demo.pl
> My type is SubClass
> The attribute is 'I Am MyClass'
> -------------
>
>
> Because I am not in control of MyClass.pm, I cannot make changes to that
> file. If I do, my software will break whenever MyClass.pm is updated.
>
> My other option is to override every subroutine that uses the class level
> attribute. This may also break when the package is updated so that a new
> subroutine starts to access the attribute.
>
> My final option is to copy all functionality from the file to some
> other file; that is, not to use MyClass after all. This is the only
> way I can get the functionality I want. I won't be automatically
> benefiting from any updates to MyClass, but that is the price I have to pay.
>
>
> The correct way to use the scopes for variables is (in my opinion):
>
> - Explicitly global variables ($::Package::variable) in the package's own
> namespace for "final" variables (defaults names of existing instance
> variables, for example) and anything that you absolutely have to save
> to future invocations.
> - Accessors for any instance variables ($object->variable()).
> Never access an instance variable directly, even if you know the
> structure of the object.
> - Lexical variables ("my @args" inside a sub) for everything else,
> and finally:
> - Argument passing ($object->method($variable)) for showing your lexicals
> to other subs.
>
> If you do it this way, then easy things are easy, and hard things are possible.
>
> (bonus: if, in addition, you never "use Exporter" to export anything,
> you'll also get code where every variable name and function call is
> easily traced back to its definition, even without special tools.)
>
> -Tuomo
>

--
Ben van Staveren
phone: +62 81 70777529
email: benvanstaveren [at] gmail


torsten.foertsch at gmx

Dec 7, 2011, 5:05 AM

Post #15 of 16 (1619 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

On Wednesday, 07 December 2011 13:32:01 Tuomo Salo wrote:
> Please do not do that.
>
> When you use a "my" variable outside a subroutine, you create a variable
> that is completely invisible from anywhere outside of that file.

Just my 2¢: Of course it is completely valid to use a my variable outside a
function. Just keep in mind that it keeps its state across invocations.

As for your example, why don't you reimplement output() in your subclass?

If I understand you correctly, you'd rather:

--------------
package MyClass;

our $class_level_attribute = "I Am MyClass";
...
--------------

and later:

-------------
package SubClass;

use base MyClass;

$MyClass::class_level_attribute = "I want to be SubClass";
-------------


Now suppose for a moment another subclass:

-------------
package SubClass2;

use base MyClass;

$MyClass::class_level_attribute = "I want to be SubClass2";
-------------


What do you expect MyClass->new->output to print?


You can prevent confusion by:

-------------
package SubClass;

use base MyClass;

sub output {
local $MyClass::class_level_attribute = "I want to be SubClass";
shift->SUPER::output(@_);
}
-------------

Now you really rely on an implementation detail of MyClass. Let's hope this
usage of $MyClass::class_level_attribute is documented.

Torsten Förtsch

--
Need professional modperl support? Hire me! (http://foertsch.name)

Like fantasy? http://kabatinte.net


merlyn at stonehenge

Dec 7, 2011, 10:07 AM

Post #16 of 16 (1622 views)
Permalink
Re: Is it me or is mod_perl extremely dangerous? [In reply to]

>>>>> "Tuomo" == Tuomo Salo <Tuomo.Salo [at] cybercom> writes:

Tuomo> When you use a "my" variable outside a subroutine, you create a variable
Tuomo> that is completely invisible from anywhere outside of that file. This
Tuomo> violates some core principles of object oriented programming, and perl
Tuomo> itself:

No it doesn't. It's common practice. It may not be the way *you* code
in Perl, but it's perfectly acceptable Perl to have a file-lexical
variable. Ever heard of "inside-out objects"? That's precisely the
mechanism by which this was accomplished.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn [at] stonehenge> <URL:http://www.stonehenge.com/merlyn/>
Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
See http://methodsandmessages.posterous.com/ for Smalltalk discussion

ModPerl modperl RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.