A question came up at work the other day, if you could override the Perl open() function. There’s many possible uses for this, but work’s was simple: we wanted a log of all files accessed during a program run. The program is large, complex, and uses many modules. An override of open() would let us record the file and open mode, then call CORE::open.
We couldn’t do it. After a second look, I still can’t do it. Not for all cases, anyway.
perlsub says lists a prototype of (*;$) for open, and discusses being careful not to override open by accident. It also suggests prototype can show the prototypes for overridable items.
prototype ‘CORE::open’ returns “*;$@”, which is interesting, but not a match for perlsub. “*;$@” lets the function get called, though, and matches all the things open has to do.
I wrote a really simple wrapper for open:
use Data::Dumper;
sub open(*;$@) {
say Dumper(\@_);
}
I wrote a function to call it a couple of ways:
say "test 1:";
open (FH, '>file.txt') or die "Can't >file.txt: $!";
close FH;
say "test 2:";
open (FH, '>', 'file.txt') or die "Can't > file.txt: $!";
close FH;
say "test 3:";
open (my $fh, '>', 'file.txt') or die "Can't my > file.txt: $!";
close $fh;
Now I can see what those look like:
test 1:
$VAR1 = [
'FH',
'>file.txt'
];
test 2:
$VAR1 = [
'FH',
'>',
'file.txt'
];
test 3:
$VAR1 = [
undef,
'>',
'file.txt'
];
Okay. The two calls with typeglobs get the name of the glob. I can work with that.
The call with a lexical, though, gets the value. Where/how do I return the handle?
I messed around some more, and when using lexicals, a Perl open() function is called with the value of the lexical, not with any way to identify the variable itself. I can’t figure out how to get the variable to pass to CORE::open, or to return something into it.
And there I’m stumped. Is it possible that the docs in perlsub are from the 5.5 days and don’t work since Perl 5.6 introduced lexicals as file handles? Or am I missing something?
I could go dig in to CPAN and see if any other modules do this right. How does Safe.pm do it? Can it be done in an XS module but not in Perl?
Back to useful work instead of banging my head against this.
Tags: Perl
USe $_[0], its an alias for the variable passed in
sub open(*;$@) {
print Dumper(\@_), “\n”;
CORE::open $_[0], $_[1], $_[2];
}
It works when I use $_[0]. It’s just like any other modification of the elements of @_: what you put there affects the variable that you passed:
sub open(*;$@) {
unless( defined $_[0] ) {
CORE::open( $_[0], $mode, $filename );
}
}
Two smart commenters with the same idea. I forgot the alias effect there; I almost never depend on it.
I’ll have to give that a try and see what I can see. It really shouldn’t matter how it’s called then, as open will cope with whatever it gets.
I’ll have to fiddle with it when I have a moment. Traveling may put that off a bit. =)
Hey Laufey, did you find a way that works?
I’m trying to do the same thing in Perl 5.12.3 and am at my wits’ end :(
The suggestions in the comments above were enough to get me unblocked.
I’m on a wildly different machine now, and can’t get the open() replacement called. Hrm. Let me fiddle with it…
And now, no, I can’t get it to work either. I tried Perl 5.10.0, as installed by Apple, and a clean install of 5.12.3. It’s something I’m doing wrong, not Perl.