perltutorial
KM
I do use improper file locking in here, I will fix that code later, keep
in mind I am illustrating tie(), not proper locking (That is for another
article).<P>
<!-- LINK REV="made" HREF="mailto:root@porky.devel.redhat.com" -->
<!-- INDEX BEGIN -->
<UL>
<LI><A HREF="#The_Ties_That_Bind">The Ties That Bind</A>
<LI><A HREF="#What_is_a_tie">What is a tie</A>
<LI><A HREF="#Invoking_a_Tie">Invoking a Tie</A>
<LI><A HREF="#Method_Names">Method Names</A>
<LI><A HREF="#Constructors_Destructors">Constructors/Destructors</A>
<LI><A HREF="#Example_pm">Example.pm</A>
<LI><A HREF="#All_tied_up">All tied() up</A>
<LI><A HREF="#Scalar_Quickie">Scalar Quickie</A>
<LI><A HREF="#Summary">Summary</A>
</UL>
<!-- INDEX END -->
<HR>
<P>
<H1><A NAME="The_Ties_That_Bind">The Ties That Bind</A></H1>
<P>
Programmers are used to ``modules'' as a way of reusing code. Some modules
provide subroutines, others provide an object-oriented interface. New in
Perl 5 are ties, a way of intercepting accesses to a Perl variable (array,
hash, scalar, or filehandle). This article will show you how to provide
reusable code using ties.
<P>
<HR>
<H1><A NAME="What_is_a_tie">What is a tie</A></H1>
<P>
You have probably already met tie-like behavior in the form of
dbmopen(). This built-in Perl subroutine associates a Perl
hash with a dbm(3)-style file on disk. If you fetch a value
from the hash, Perl looks up the corresponding value in the dbm file. If
you store a value, Perl stores the value in the dbm file.
<P>
The biggest problem with dbmopen() is that the type of dbm
file that it uses was decided when your Perl interpreter was compiled.
There are many different dbm-like libraries, but dbmopen()
only allows you to use one. To solve this inflexibility, Perl 5 introduced
tie(), a more general mechanism for hiding complex behavior
behind a variable, be it a hash, array, scalar, or filehandle.
<P>
Perl basically keeps an object for each variable you tie().
Each access to the tied variable results in method calls on the object. You
can tie() a variable to just about anything; a database, a
text file, a directory, even the Windows registry. You can even use
tie() to trace accesses to a variable.
<P>
The methods automatically called by Perl have predefined names, in CAPS.
There are only a few implicitly called methods your tying class should, and
can, provide, which will be discussed later. The tying class is the
invisible glue that holds your variable and resource together.
<P>
The example I'll work through will be a tie to a hash, since that is the
most involved, the longest and best-supported, and (consequently) the most
common. By the end of this article, you should have a good understanding of
the tie() function, tying classes, and the knowlege to begin
writting your own tie() class.
<P>
<HR>
<H1><A NAME="Invoking_a_Tie">Invoking a Tie</A></H1>
<P>
Invoking a tie() is similar to other Perl functions.
tie() uses this syntax:
<P>
<code> tie VARIABLE, CLASSNAME, LIST
</code>
<P>
The VARIABLE will be the variable being tied, which could be a hash, array,
scalar, or filehandle. CLASSNAME is the class we are using to handle the
tied object. And LIST can be any optional flags or arguments that the class
may use to construct the tied variable.
<P>
If you're always tying a variable to the same resource, and this resource
is pre-defined in the class, you won't need to include the name of the
resource in LIST. If the specific resource could change (different
filename, different database, different machine, etc.) then one of the
elements of the LIST will be the name of the resource. After calling
tie(), the class will return an object, which can be either
stored or ignored. Either way, when you access the VARIABLE, methods are
called on the object.
<P>
A common misconception after looking at the syntax for the tie function is
that tie() itself 'uses' the class which is handling the tie.
This is not correct, and the programmer must remember to 'use' the
appropriate module.
<P>
Don't confuse the type of the variable and the type inside the object. You
can bless() any type of Perl variable to make your object, and
this has nothing to do with the type of variable being tied to. You can tie
a hash to a blessed hash, or tie a hash to a blessed scalar, or tie a
handle to a blessed scalar, or anything else that makes sense for your
application.
<P>
To see what this looks like in practice, below is an example of tying a
hash to the current directory using the Tie::Dir class. The result will be
a tied interface between %hash and an object in the Tie::Dir
class. The Tie::Dir class' methods will handle the manipulation of the
directory via %hash:
<P>
<code> use Tie::Dir;
tie %hash, Tie::Dir, "./";
</code>
<P>
<HR>
<H1><A NAME="Method_Names">Method Names</A></H1>
<P>
As stated earlier, Perl will implicitly call a class' methods when the
variable is accessed. When tie() is invoked it calls the
appropriate TIESCALAR, TIEARRAY, TIEHASH, or TIEHANDLE method depending on
the type of variable being tied.
<P>
Because I will talk about various uses of tying, and giving examples, below
is an outline of the methods available for each type of tie to keep in mind
while you read this article:
<DL>
<DT><STRONG><A NAME="item_Hash">Hash methods:</A></STRONG><DD>
<P>
<code> TIEHASH classname, LIST
DESTROY this
FETCH this, key
STORE this, key, value
DELETE this, key
EXISTS this, key
FIRSTKEY this
NEXTKEY this, lastkey
</code>
<DT><STRONG><A NAME="item_Array">Array methods:</A></STRONG><DD>
<P>
<code> TIEARRAY classname, LIST
DESTROY this
FETCH this, key
STORE this, key, value
</code>
<DT><STRONG><A NAME="item_Scalar">Scalar methods:</A></STRONG><DD>
<P>
<PRE> TIESCALAR classname, LIST
DESTROY this
FETCH this,
STORE this, value
</PRE>
<DT><STRONG><A NAME="item_Filehandle">Filehandle methods:</A></STRONG><DD>
<P>
<code> TIEHANDLE classname, LIST
WRITE this, LIST
PRINT this, LIST
PRINTF this, LIST
READ this, LIST
READLINE this
GETC this
CLOSE this
DESTROY this
</code>
</DL>
<P>
<HR>
<H1><A NAME="Constructors_Destructors">Constructors/Destructors</A></H1>
<P>
When a variable is tied to a class, the TIE* constructor is called.
<P>
The constructor can do almost whatever the programmer wants it to. It may
simply check to see if the resource to be tied to exists. It could create
it if it doesn't, or load the resource into memory; or add an internal
pointer to the resource in the class as object data. For example, if it is
a directory to be tied to, the constructor may check to see if that
directory exists, and if it does, it will save the location of that
directory to the class object.
<P>
Below is how Tie::Dir's TIEHASH constructor will result in a
tie() of a variable, in this case %hash, to the current
directory (.), and whether or not deleting is allowed:
<P>
<code> # tie %hash, Tie::Dir, ".", DIR_UNLINK;
sub TIEHASH {
my($class,$dir,$unlink) = @_;
$unlink ||= 0;
bless [$dir,undef,$unlink], $class;
}
</CODE>
<P>
One use of tying may be for keeping a certain variable persistent through
web-based sessions. For example, people log into your website, via basic
authentication, and a file is saved that contains the city they live in,
based from input from a web-based form. That information is used to create
dynamic pages when they return to the site. Almost like a server-side
cookie. So if I am the user and my username is kmeltz, and I am from
Fooville, you may have a file named kmeltz.city with the contents being a
string, ``Fooville''. You could tie() to this scalar (the
string Fooville) with a constructor like:
<P>
<code> sub TIESCALAR {
my $class = shift;
my $name = shift;
my $city = system("/bin/cat", "$name\.city");
return bless \$city, $class;
}
</code>
<P>
In the calling script, you would get this information like:
<P>
<code> use Tie::getCity; # If the module name was Tie/getCity.pm
tie($foo, 'Tie::getCity', $ENV{REMOTE_USER});
</code>
<P>
Now, $foo's value is the string ``Fooville'' and this information can be
used to create dynamic content across your website.
<P>
When the variable is untied (see later), goes out of scope, or the program
ends, the object associated with the variable will be destroyed. The
regular object destructor method DESTROY will be called. It is a very good
habit to untie() tied variables when they are no longer
needed. $foo would be untie()'d from the above
example like so:
<P>
<code> untie $foo;
</code>
<P>
<HR>
<H1><A NAME="Example_pm">Example.pm</A></H1>
<P>
Now, on with our module! For the example, we will create a module that ties
to a password file for Apache, which means there will be a file with
entries like '<name>:<encrypted password>'. There will also be
a simple command-line script using the Example class. The script will let
you enter names and passwords which will be stored in the password file
through the magic of tie().
<P>
The start of the module is fairly uninteresting:
<P>
<code> package Example;
use strict;
use vars qw($VERSION);
# pull $VERSION from RCS version identifier
($VERSION = substr(q$Revision: 0.7 $, 10)) =~ s/\s+$//;
sub Version {return $VERSION;}
use Carp;
</code>
<P>
As explained earlier, the TIEHASH method is implicitly called when you
invoke tie(). The first three lines tell our method a) what
the class object is, as well as the LIST elements passed during our
invocation; b) the file we are tying to; c) the permissions we are opening
the file with. If the permission ($mode) argument isn't given, it defaults
to 'r', read only. Or it can be given 'rw' which will allow for read and
write operations. It is good practice to add some sort of permission
argument to a tie() class, this is to make sure noone
accidentally stores/removes information from the resource. It is also good
practice to have the default permission be the most restrictive.
<P>
Next, check to make sure no extra arguments are passed to the module. If
there are, it croaks with a usage/syntax message.
<P>
You will notice the scalar variable $clobber. Clobber is a term that tells
the class if the caller can or can't make changes to the tied resource. It
is directly affected by the permissions it was tied with. If the mode is
'r', clobber will be 0, and if it is 'rw' it will be 1. The
$clobber variable will be used later to make sure the file is
being opening with correct permissions, and not allow changes to the file
if it wasn't tie()'d with read-write permissions.
<P>
An anonymous hash, %node, is then built. This anonymous hash will house
some instance data that will be refered to throughout the module. This hash
will contain the current settings of the path to the file, if it can
clobber or not, and the current hash values. The value of the current hash
is held in memory because otherwise some later methods would have to open
and read the file to build the hash again.
<p>
NOTE: This may not be what
you would want for your application. If an application would be used
concurrently by multiple users, each method should get the current values
from your tied resource so no data is lost. It is being done this way to
save space and resources for this example.
<p>This allows for the current
hash values to be in memory at all times. Speaking of which, the next few
lines build that hash, and store it in the CURRENT field
($self->{CURRENT}).
<P>
You may choose to store the information from your resource in memory if it
is a resource that may be changed frequently <EM>only</EM> by your current process. If the application is used frequently by many
users, each method should get the current data from the resource in some
fashion. An alternative is to have a private method, which reads in the
resources data. This method can then be called from within your class'
methods to get the latest values. I suggest adding that functionality later
when playing around with the Example class.
<P>
Finally, the bless() function is called, which tells
%node that it is now an object in the class. We are tied and
ready to go!
<P>
<code> # Create tied hash
sub TIEHASH {
my $self = shift;
my $path = shift;
my $mode = shift || 'r';
if (@_) {
croak ("usage: tie(\%hash, \$file, [mode])");
}
my $clobber = ($mode eq 'rw' ? 1 : 0);
my $node = {
PATH => $path,
CLOBBER => $clobber,
CURRENT => {}
};
open(FH, "$path");
my @lines = <FH>;
close FH;
my ($line, $id, $pass);
foreach $line (@lines) {
($id, $pass) = split(/\:/,$line);
$node->{CURRENT}{$id} = $pass;
}
return bless $node => $self;
}
</code>
<P>
The next method to create is STORE. The STORE method is what will handle
the actual writting of data to the resource, when a value in the tied hash
changes. This method will also perform any complex behavior needed to be
done before the data should be written, such as encrypting a
password.
<p>NOTE: ``Complex behavior'' can be any preprocessing tasks
needed to ready data, or possibly logging the tied variables accesses.
This method is called when doing something such as:
<P>
<code> $hash{FOO} = "bar";
</code>
<P>
The call above tells the class to store the value ``bar'' under key-name
FOO in %hash. This is what will set the name/value pairs in CURRENT and,
eventually, in the password file. This STORE method, since we are writing
to a htpasswd-like password file, will also do the encryption (which can be
considered hidden complex behavior). The method begins by defining
variables. If the call noted above were used, $id would be
FOO, and $passwd would be ``bar''. It grabs our path, as well
as checks for clobbering, from the class data that was saved in the
constructor. If clobbering isn't allowed, it returns with an error message.
For good programming measure, we are taking into account the fact that
STORE is called after the upcoming method CLEAR finishes.
<P>NOTE: This is
part of the internal workings of the tie() function, and not
something done programatically
<P>When STORE is called via CLEAR there will
be no arguments (besides $self, of course). So, STORE will return before
writting an entry with no username or password.
<P>
<code> # Store an entry
sub STORE {
my $self = shift;
my ($id) = shift;
my ($passwd) = shift;
my ($passwdFile) = $self->{PATH};
my ($return)=0;
my (@cache);
my ($cryptedPass);
unless ($self->{CLOBBER}) {
carp ("No write access for $self->{PATH}");
return;
}
if (!$id && !$passwd) {return 1;}
}
</code>
<P>
The next step is to create the new encrypted password. The 'salt' for
encryption is obtained by getting the first two letters of the systems
hostname by the hostname() method of the Sys::Hostname module
(distributed with Perl). Before crypt() is called,
we want to make sure that
there is a password to crypt. The case where there wouldn?t be a password
is when the function is called intending to delete the password like:
<p>NOTE:
Win32 ports of Perl may not implement crypt(). To make sure
your Perl is compiled to support crypt() a test can be done
from the command line with 'perl -e ``print
crypt(''ab``,''test``)'';'. You may want to look into Crypt.pm
if crypt() is unavailable to you.
<P>
<code> $hash{name} = "";
# or
$hash{name} = undef;
</code>
<P>
The method takes the situation of a blank password into account so a
username isn't written with no password. If there is a password, we then
encrypt it.
<P>
<code> if ($passwd eq "") {
$cryptedPass = "";
} else {
$cryptedPass = crypt($passwd, $salt);
}
</code>
<P>
The new name/password pair is ready to be written to the password file. The
file is opened and locked with flock()
<P>NOTE: Win32 may
not support the Perl flock() function. If it is not
implemented on your machine, you can look into the Fcntl module
(distributed with Perl)
to make sure that the file isn't modified before
the method finishes writting the new data. A check is made against the
existing name/password pairs stored in memory to see if there's already a
password for this userame. If the entry exists, the method runs through the
file and replaces the old entry with the new one. If it doesn't exist, it
will append the new entry to the file. Finally, it closes our file and adds
the new entry into the hash in memory so the saved hash is in synch with
the password file.
<P>
<code>
# Warning, possible race condition ahead
# I need to update this opening a locking!
if (!open(FH,"<$passwdFile")) {
carp("Cannot open $passwdFile: $!");
return;
}
flock(FH, 2);
if (!exists $self->{CURRENT}{Id}) {
while (<FH>) {
if ( /^$Id\:/ ) {
push (@cache, "$Id\:$cryptedPass\n")
unless $cryptedPass eq "";
$return = 1;
} else {
push (@cache, $_);
}
}
}
close FH;
if ($return) {
if (!open(FH, ">$passwdFile")) {
carp("Cannot open $passwdFile: $!");
return;
}
flock(FH, 2);
while (@cache) {
print FH shift (@cache);
}
} else {
if (!open(FH, ">>$passwdFile")) {
carp("Cannot open $passwdFile: $!");
return;
}
flock(FH, 2);
print FH "$Id\:$cryptedPass\n" unless $cryptedPass eq "";
$foo = $hash{FOO};
}
</code>
<P>
The FETCH method has a very specific function, to get a value. To do this
it first checks to see if the username ($Id) being looking for exists in
the current hash. If so, it returns that value, if not, it returns a
message saying it doesn't exist. The FETCH method is very simple and
straight forward because it isn't performing any magic in the background.
<P>
<code> sub FETCH {
my $self = shift;
my $Id = shift;
if (exists $self->{CURRENT}{$Id}) {
return $self->{CURRENT}{$Id};
} else {
return "$Id doesn't exist";
}
}
</code>
<P>
Here is a fast quiz. Judging by the names of the methods so far, what would
you guess the method name for deleting an entry in the file is (faint
sounds of the Jeopardy theme). If you said DELETE, you are correct!
<P>
The DELETE method does just that, it deletes an entry in the hash, and in
turn the tied resource. It doesn't delete just the value, but the key/value
pair. The DELETE method is only called when the delete()
fuction is called. Assigning undef or ``'' to an entry in the hash doesn't
delete that entry, so DELETE is not called.
<P>
<code> delete $hash{FOO};
</code>
<P>
The above DELETE call will delete all instances of entry FOO. What you see
in the example DELETE method should look familiar now. First a check for
clobbering is done. If it is ok to clobber a check is made in the local
hash to make sure the entry wanted to be deleted exists. If it does, the
file is opened and its entries are read, removing the entry marked for
deletion. Finally, the new file is written. I added in a return of 1 when
the entry doesn't exist, since the user may have thought it did, I didn't
think it warranted the script exiting. It also returns a 1 when an entry is
successfully deleted. The only error, aside from not being able to open a
password file, that can make the subroutine die() is if the
file was opened with read-only permissions.
<P>
<code> sub DELETE {
my $self = shift;
my ($Id) = shift;
my ($passwdFile) = $self->{PATH};
my (@cache);
unless ($self->{CLOBBER}) {
carp ("No write access for $self->{PATH}");
return;
}
if (!exists $self->{CURRENT}{$Id}) {return 1;}
delete $self->{CURRENT}{$Id};
if (!open(FH,"<$passwdFile")) {
carp("Cannot open $passwdFile: $!");
return;
}
flock(FH, 2);
while (<FH>) {
if ( /^$Id\:/ ) {
next;
} else {
push (@cache, $_);
}
}
close FH;
if (!open(FH,">$passwdFile")) {
carp("Cannot open $passwdFile: $!");
return;
}
flock(FH, 2);
while (@cache) {
print FH shift (@cache);
}
close FH;
return 1;
}
</code>
<P>
The module is almost complete. Our next method, CLEAR, will clear the
entire hash, as well as clearing all the data out of the tied resource.
CLEAR is generally called when you assign an empty list as the value of
your tied hash. This occurs when assigning a null string, to another hash,
or undef. This, of course, can be very dangerous in a situation where a
programmer isn't paying attention and makes a calls to invoke CLEAR by
accident. Below illustrates ways in which CLEAR will be invoked.
<P>
<code> %hash = "";
%hash = %newHash;
%hash = {};
undef %hash;
</code>
<P>
To help prevent against this sort of mishap, the module can be made more
foolproof by adding another level of 'mode' to tie() with that
will set the $clobber variable to something higher than 1
(like 2). Then the TIE* method can be written to understand not only 'r'
and 'rw', but something like 'rwe' as well. This way, an extra level of
security is added, and the user knows that they can erase the resource
because they used the proper permissions when they invoked the
tie(). I didn't add that into this example, but left it as an
exercise for the reader. The example's CLEAR method will CLEAR the local
hash, as well as the password-file.
<P>
<code> sub CLEAR {
my $self = shift;
my ($passwdFile) = $self->{PATH};
unless ($self->{CLOBBER}) {
carp ("No write access for $self->{PATH}");
return;
}
if (!open(FH,">$passwdFile")) {
carp("Cannot open $passwdFile: $!");
return;
}
close FH;
$self->{CURRENT} = {};
}
</code>
<P>
Now we get into the last few methods. These are very simple methods, and
very short! The next is FIRSTKEY. This method is invoked when a call is
made to iterate through the hash, generally with the keys() or
each() functions.
<P>
<code> sub FIRSTKEY {
my $self = shift;
my $a = keys %{$self->{CURRENT}};
each %{$self->{CURRENT}};
}
</code>
<P>
One of the last methods is NEXTKEY. This method is also invoked during an
each() or keys() iteration. Behind the scenes it
is given 2 arguments 'this' and 'lastkey', which are the object and the
last key iterated through, respectively. It is very similar to FIRSTKEY,
but it returns all the keys, as opposed to the first key in the hash. Here
arguments are ignored because this method using the
each() function behind the scenes to iterate over the hash in
$self->{CURRENT}.
<P>
<code> sub NEXTKEY {
my $self = shift;
return each %{$self->{CURRENT}};
}
</code>
<P>
The last method is the class destructor, DESTROY. This method is invoked
when the tied variable is to be destroyed. Unless the return value of
tie() has been saved, this can be done with the
untie() function. If the tied variable hasn't been
untie()'d, DESTROY will be called when the script exits. In
general, you don't need to have anything in a DESTROY method, unless you
are doing some special debugging, or you possibly have some cleanup that
you want to do. In fact, you do not need to have a DESTROY method at all,
and our Example.pm will not have this method.
<P>
For example's sake, if you created a temp file, for whatever reason, and
wanted to delete it only when you know the tie() is finished
being used, the below would do this:
<P>
<code> sub DESTROY { unlink "/tmp/tie.txt";}
</code>
<P>
And there you have it! A module that will bind a variable from a script to
a password (or whatever) file. Now that it is written, let's use it. This
is a quick command line program to test this module out. Try adding,
deleting, and getting passwords.
<P>
<code> #!/usr/bin/perl
use Example;
tie(%hash, "Example", "example", "rw") || die "Can't tie : $!";
&ask;
sub ask {
print "(A)dd, (D)elete, or (G)et user:";
$ans = <STDIN>;
if ($ans =~ /a/i) { &add; }
elsif ($ans =~ /d/i) { &delete;}
elsif ($ans =~ /g/i) {&get;}
else { print "Try again\n"; &ask;}
}
sub add {
print "User Name:";
$name = <STDIN>;
print "\nPassword:";
$pass = <STDIN>;
chop $name;
chop $pass;
$hash{$name} = $pass;
print "\nAdded\nAgain (Y/N)?";
$again = <STDIN>;
if ($again !~ /y/i) { untie %hash; exit;}else{&ask;}
}
sub delete {
print "User Name:";
$name = <STDIN>;
chop $name;
delete $hash{$name};
print "\nDeleted\nAgain (Y/N)?";
$again = <STDIN>;
if ($again !~ /y/i) { untie %hash; exit;}else{&ask;}
}
sub get {
print "User Name:";
$name = <STDIN>;
chop $name;
if (!exists $hash{$name}) {
print "$name isn't valid";
} else {
print "$name\'s encrypted password is " . $hash{$name};
}
print "\nAgain (Y/N)?";
$again = <STDIN>;
if ($again !~ /y/i) { untie %hash; exit;}else{&ask;}
}
</code>
<P>
<HR>
<H1><A NAME="All_tied_up">All tied() up</A></H1>
<P>
A sister function of tie() is Perl's tied()
function. By using this function, you can access a reference to the
underlying object. If you were to expand this example class, you may want
to add the flexibility to use a second password file midway in your
program. Instead of creating a new tied object, you can change your
existing one like so:
<P>
<code> tied(%hash)->newPwdFile('/usr/local/apache/.passwds');
</code>
<P>
Which yields the same result as:
<P>
<code> $obj = tie(%hash, 'Tie::Class', 'rw');
$obj->newPwdFile('/usr/local/apache/.passwds');
</code>
<P>
This will call (explicitly) the method newPwdFile() on the
underlying tied object and allow you to use your objects methods on the new
file. This is what the newPwdFile() method could look like:
<P>
<code> sub newPwdFile {
my $self = shift;
$self->{PATH} = @_ ? shift : die "No new file given";
unless (-e $self->{PATH}) {
if ($self->{CLOBBER}) {
unless (open(FH,">$self->{PATH}")) {
croak("Can't create $self->{PATH}: $!");
}
} else {
croak("$self->{PATH} does not exist");
}
}
close FH;
my ($line, $id, $pass, @lines);
foreach $line (@lines) {
($id, $pass) = split(/\:/,$line);
$self->{CURRENT}{$id} = $pass;
}
}
</code>
<P>
<HR>
<H1><A NAME="Scalar_Quickie">Scalar Quickie</A></H1>
<P>
This is a fast example of a class that will tie() a scalar
variable to a text file. This is different from the other example (given in
the Constructors/Destructors section) of a string value living in a text
file being assigned to the tying scalar. In this case, the text file is
going to keep a log of all the times the scalar variable is changed, or
it's value is FETCHED. Take what you have learned about tying with hash's
and apply it to the module below.
<P>
<code> # Usage: tie($VARIABLE,'TrackScalar', FILE, "\$VARIABLE name/description");
# use TrackScalar;
# my $var;
# tie($var, 'TrackScalar', 'track.txt', "\$var (keeps count)");
package TrackScalar;
use strict;
use vars qw($VERSION @ISA);
# Get Revision number from RCS
($VERSION = substr(q$Revision: 0.2 $, 10)) =~ s/\s+$//;
sub Version {return $VERSION;}
use IO::File;
# Create tied scalar
sub TIESCALAR {
my $class = shift;
my $log = shift;
my $var = shift || "(undefined)";
my $fh = new IO::File ">> $log"
or die "Cannot open $log: $!\n";
# Notice that the variable being blessed in the object is
# an anonymous hash, and this is tied to the scalar
return bless {FH => $fh, VAL => 0, VAR => $var}, $class;
}
sub FETCH {
my $self = shift;
my ($package, $filename, $line) = caller();
my $fh = $self->{FH};
print $fh "package $package, ",
"$filename line $line FETCHED $self->{VAR}\n";
return $self->{VAL};
}
sub STORE {
my $self = shift;
my $var = shift;
my $fh = $self->{FH};
my ($package, $filename, $line) = caller();
print $fh "package $package, ",
"$filename line $line changed $self->{VAR} to $var\n";
$self->{VAL} = $var;
}
sub DESTROY {
undef %{$_[0]};
}
1;
</code>
<P>
<HR>
<H1><A NAME="Summary">Summary</A></H1>
<P>
Through this article, we created a tie() class, and a program
that used it. You could customize this tie() class as much as
you wish: adding multiple 'mode' levels, adding manually invoked methods to
change the password file or level of clobbering, making reading/writing
from the resource more efficent, file syncing, real-time read/write
operations, or almost anything else you can think up.
<P>
I encourage you to write your own class that ties to a directory, the
Windows registry, config files, remote machines, or system commands. All
are straightforward applications of the techniques in this article.
<P>
I hope you now have a better understanding of how tie() works,
how to use it, have enough information to write your own tie module, and
now have another tool of Perl to use in your programming.
<P>
__END__