CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
Sessions variables

 



Fishhead
Novice

Feb 11, 2016, 4:25 PM

Post #1 of 16 (4332 views)
Sessions variables Can't Post

I am attempting to set-up a session variable which will pass a string from one sub routine to another.

In the common area of the program I have:

use CGI;
use CGI::Session;

In the first sub routine I have:

$delfile = $form{'FILENAME'};
$session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
$sid = $session->id();
$cookie = $cgi->cookie(CGISESSID => $session->id);
print $cgi->header(-cookie=>$cookie);
$session->param('deletefile', $delfile);

In the second sub routine I have:

$sid = $cgi->ookie("CGISESSID") || undef;
$session = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
$deletefile = $session->param("deletefile");

When this runs I receive this error:

"Can't call method "cookie" on an undefined value at ...", the location referred to in this error is in the first sub routine.

This code is coming straight out of the CPAN section on Session variables.

What am I doing wrong?

Any help would be welcome.

Pete


FishMonger
Veteran / Moderator

Feb 12, 2016, 7:26 AM

Post #2 of 16 (4310 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

You should always copy/paste your code instead of retyping it in your post and it should be inside the code tags.

You have a typo in this line:

Code
$sid = $cgi->ookie("CGISESSID") || undef;


The error message is referring to this line:

Code
$cookie = $cgi->cookie(CGISESSID => $session->id);

And is telling you that $cgi is not defined.

Please post a more complete sample of your code


Fishhead
Novice

Feb 12, 2016, 8:13 AM

Post #3 of 16 (4306 views)
Re: [FishMonger] Sessions variables [In reply to] Can't Post

Thanks FishMonger

I caught the typo this morning and fixed it.

Then in the first subroutine I added:

Code
$cgi = new CGI;

to define $cgi.

The section of first subroutine code is now:

Code
$delfile = $form{'FILENAME'}; 
$cgi = new CGI;
$session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
$sid = $session->id();
$cookie = $cgi->cookie(CGISESSID => $session->id);
print $cgi->header(-cookie=>$cookie);

$session->param('deletefile', $delfile);


This change eliminated the error, but is still not passing the variable to the second subroutine.

Pete


FishMonger
Veteran / Moderator

Feb 12, 2016, 8:26 AM

Post #4 of 16 (4301 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

What subroutine? You haven't shown any subs.

Are they both in the same script, or different scripts?

How are they being called?

You need to post more code.


(This post was edited by FishMonger on Feb 12, 2016, 8:27 AM)


Fishhead
Novice

Feb 12, 2016, 9:08 AM

Post #5 of 16 (4295 views)
Re: [FishMonger] Sessions variables [In reply to] Can't Post

FishMonger

I am not trying to be difficult. I was trying to narrow my question to the issue of defining a variable in one subroutine so that it can be used else where in another subroutine.

My program is an online auction for the commercial fishing industry and has been up and running since 2010. Everything works fine. However, I would like to make an improvement.

Currently, when an listed item does not sell, the seller receives an email with a link that he can follow and repost his item. He will be displayed a list of all of his recent listings and he can choose which one he wants to relist.

The form is re-populated with the data from the original listing. He can make edits if he wishes and then repost.

Again currently, the display he sees of his unsold listing includes those that he has relisted.

I want to add code that will delete the old listing once it has been reposted.

Therefore I need to capture the original file name and hold it until the new listing has been posted. Deleting the old listing prior to reposting will not allow the re-populating of the new form.

There is around 15,000 line of code in the main program with many subroutines. The two subroutines that I mention are both contained in the main script. I could post a lot of code but doubt that it changes the issue of how to define a variable in one subroutine and pass it to another.

If you want to see complete subroutine code let know. I will be traveling today and through the weekend so I may be slow in getting back to you.

Thanks again for your help.

Pete


FishMonger
Veteran / Moderator

Feb 12, 2016, 9:33 AM

Post #6 of 16 (4293 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

Are you using the strict pragma i.e., does your script have this line?

Code
use strict;


Based on the code you've posted your script doesn't have that line.

If you are using the strict pragma, then a var declared/defined in one sub would be local to that sub and will not be accessible in another sub.

If you are not using the strict pragma, then all vars defined are global and accessible from any location. However, doing that is a very very bad coding practice and makes troubleshooting much more difficult.

Instead of posting your entire 15,000 line script, write a short but complete test script that demonstrates the problem and post that script. With that I will be able to tell you what's wrong.


(This post was edited by FishMonger on Feb 12, 2016, 9:34 AM)


FishMonger
Veteran / Moderator

Feb 12, 2016, 9:48 AM

Post #7 of 16 (4290 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

In case it's not obvious to you I'll point out that if a global var is defined in a subroutine and you want to use that same var somewhere else, then the sub that defines the var will need to be executed prior to trying to use it in another location.


Zhris
Enthusiast

Feb 13, 2016, 1:56 PM

Post #8 of 16 (4244 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

Hi,

Unimportantly, I haven't used CGI::Session for a few years, my preference is to use either my own session handling module or Data::Session, both have various improvements and simplifications.

When handling sessions, I use a particular design pattern:

- Create or load a single persistent session. Don't destroy the old / create a new session in order to destroy the old / create a new variable, the overhead has never been an issue. Instead handle this at a higher level via the param and clear methods, testing for definedness of relevant parameters. I mention this in particular because you pass an undefined value as the second argument to new in your "first sub", which as far as I can tell from the tutorial, will force CGI::Session to create a new session each time.
- Write a maintainable / reusable session instantation function or object method to instantiate the session no matter your later intentions. This section of your session code will commonly be reused.
- It is recommended to call flush at minimum before your program exits because automatic flushing can be unreliable.
- In your case its easiest just to pass a CGI object to new and use CGI::Sessions header method to handle setting the cookie and header. But CGI Session remains flexible in that you don't necessarily have to use CGI or CGI::Cookie, or even the HTTP protocol.

I can't tell from your code, but my best guess is its purpose is to provide an intermediate interface for the user to confirm if they would like to delete a particular file. If this is the case then you might not need a session at all, simply pass the "FILENAME" parameter down the chain via a hidden input field. Of course this is dependent on the actual complexity of your application.

Below is a very rough standalone example that creates / loads a persistent session using CGI::Session, providing functions to get, set and reset a particular parameter. Its purpose is to demonstate a basic session process, the general application itself is not particularly well written. A demo is available here, change the do url parameter to get, set or reset the session parameter:


Code
use strict; 
use warnings FATAL => qw/all/;
use CGI::Carp qw/fatalsToBrowser/;
use CGI;
use CGI::Session;

my $cgi_session_directory = '/tmp';
my $do_lookup =
{
set => \&set,
get => \&get,
reset => \&reset,
};

my $cgi = CGI->new;
my $cgi_session = CGI::Session->new( 'driver:File', $cgi, { Directory => $cgi_session_directory } );
print $cgi_session->header;
$do_lookup->{$cgi->param( 'do' )}->( );

sub set
{
$cgi_session->param( 'key', 'val' );
$cgi_session->flush;

print 'key has been set to the value of val';
}

sub get
{
my $val = $cgi_session->param( 'key' ) // 'undef';

print "key has the value of $val";
}

sub reset
{
$cgi_session->clear( 'key' );
$cgi_session->flush;

print 'key has been reset';
}


Chris


(This post was edited by Zhris on Feb 13, 2016, 1:59 PM)


Fishhead
Novice

Feb 14, 2016, 4:06 PM

Post #9 of 16 (4196 views)
Re: [FishMonger] Sessions variables [In reply to] Can't Post

FishMonger, sorry for the slow reply, but I have been traveling.

This is the general form of my script at this time. At the beginning I have:

Code
use strict; 
use warnings;
use CGI::Carp qw(fatalsToBrowser);
use CGI;
use CGI::Session;
use Data::Dumper;
use DBI;


A user would would receive an email notice telling him that an item listed on the auction did not sell. They would click the provided link and be taken to a table on the website showing them a list of the items that they had listed but that did not sell. They would click on the item that they would like to relist. This takes them to a display of the item showing them all of the details they had originally provide when they first listed the item. It is in this subroutine that I wish to capture the file name for the session. The file name is there and and available. I have tested this by printing it out on this page. He is the code that I have in this subroutine:


Code
my $delfile; 
my $session;
my $cookie;
my $sid;
my $cgi;


$delfile = $form{'BIDTOVIEW'};
$cgi = new CGI;
$session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
$sid = $session->id();
$cookie = $cgi->cookie(CGISESSID => $session->id);
$session->param('deletefile', $delfile);


On this page there is a button labeled "Re-post". When they click this button they are taken to a page with the listing form. All of the original data have populated the form. Here they can edit the information if they like. From here they click a but to "preview" the listing. If they are happy with the listing they then click a button labeled "sell it" and the item is listed on the auction.

Is is here, when they click "sell it" that I wish to to use the session variable holding the file name to delete the old file for the listing. That way if they were to return to the table showing the listing of their unsold items, the old listing would no longer be shown. The code that I have to obtain the session variable is:


Code
my $deletefile; 
my $session;
my $cookie;
my $sid;
my $cgi;
$cgi = new CGI;
$sid = $cgi->cookie("CGISESSID") || undef;
$session = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
$deletefile = $session->param("deletefile");
unlink ("$config{'basepath'}$config{'closedir'}/$deletefile.dat");


Thank you for your help.

Pete


FishMonger
Veteran / Moderator

Feb 15, 2016, 8:51 AM

Post #10 of 16 (4162 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

Your main problem is due to improper scoping.

The $cgi and $session objects should have file scope. Instead of declaring and assigning them inside the subs, they should be declared and assigned shortly after the use statements so that they can be used anywhere in the script.

Have you compared the session id between each of your subs to make sure they are identical? I suspect that they are not, which would explain why the session var you need isn't being carried over.


(This post was edited by FishMonger on Feb 15, 2016, 8:52 AM)


Fishhead
Novice

Feb 15, 2016, 4:05 PM

Post #11 of 16 (4150 views)
Re: [FishMonger] Sessions variables [In reply to] Can't Post

Thanks FishMonger

I have declared $sid and $session earlier following the use statements and tested the script. It still is not carrying the file name to the next subroutine.

I have not compared session id's. From your comment I gather that they should be identical. I did not see how the id in the second subroutine would be set identical to the id generated in the first subroutine. Can you help me with code?

Pete


Zhris
Enthusiast

Feb 16, 2016, 12:23 AM

Post #12 of 16 (4135 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

Pete,

Your problem is possibly as a result of something you have done elsewhere in your code, not in the session code you have provided.
When I take your latest two snippets of code, make a couple of adjustments ( define form hash, print header ), put them in two separate scripts, they work just fine.
Your best bet is to start with a fresh slate, write a new script which contains just the necessary code, that demonstrates your problem.
Use an inspector tool, if using Mozilla Firefox, get the Firebug extension. It has a cookies section where you can check if the session id cookie has been set properly and is maintained as you navigate. You can also check the headers of requests and responses.

Here are some further suggestions as to what could be wrong with your code:

- Your description of the problem "but is still not passing the variable to the second subroutine" is a little vague based on the code we have, how are you confirming this, if its simply because the file is not being deleted perhaps this is where your problem lies, you should use the syntax unlink $file or die $! to help confirm this.
- The value of $form{'BIDTOVIEW'} is not what you think, you haven't shown us how this is defined. Thus its value is undefined.
- You have forgotten to print the header. Your original snippet does this, but not your latest. Thus the cookie is not being set.
- You are already printing a header beforehand and not seeing the output of the later header due to the positioning of html elements. Thus the header that sets the cookie is not working.
- You describe your two snippets of code as being part of two subroutines, perhaps one or the other subroutine is not being called, or returns before reaching your session code.
- ( long shot ) You are doing something that changes the behaviour of CGI::Carp i.e. re-adjusted %SIG. Thus fatals and/or warnings are not being displayed in browser.

Chris


(This post was edited by Zhris on Feb 16, 2016, 12:32 AM)


Fishhead
Novice

Feb 16, 2016, 11:02 PM

Post #13 of 16 (4088 views)
Re: [Zhris] Sessions variables [In reply to] Can't Post

Chris thanks for the reply. Following your advise I installed Firebug in Firefox and I do not see cookies being set by my code. Let me respond to some of your suggestions.

1)

Quote
- Your description of the problem "but is still not passing the variable to the second subroutine" is a little vague based on the code we have, how are you confirming this, if its simply because the file is not being deleted perhaps this is where your problem lies, you should use the syntax unlink $file or die $! to help confirm this.



I placed the variable "$deletefile" in the text of page generated in the second subroutine. If the value was being passed to the routine it would be seen in the text.

2)

Quote
- The value of $form{'BIDTOVIEW'} is not what you think, you haven't shown us how this is defined. Thus its value is undefined.


Using the same approach as in 1) I place "$delfile" in the text of the page generated in first subroutine. The file name prints in the text. Similarly, I placed $gci, $sid, $session, and $cookie in the text and they printed out as:
$cgi - CGI=HASH(0x2310ae0)
$sid - 0c8c3cf354e7bbf62650e4c8bf4133cc
$cookie - CGISESSID=0c8c3cf354e7bbf62650e4c8bf4133cc; path=/
$session - CGI::Session=HASH(0x235f938)

3) I replaced the print header code and obtained the same result.

4) I am still exploring this comment.

5) both subroutines are part of the original code and they work fine reposting unsold items on the auction.

6) I am not sure how to go about exploring this comment.

Pete


FishMonger
Veteran / Moderator

Feb 17, 2016, 9:43 AM

Post #14 of 16 (4065 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

We're not going to be able to solve the problem based on your incomplete code snippets which don't give us enough context on what your code is doing.

It would be best if you could write a short but complete test script which demonstrates the problem so that we can review it and run some tests.

If you must, you could post your complete script and assuming it's not a huge mess, I might be willing to look it over to see if I can spot the problem. However, since it's about 15,000 lines, I'm not going to spend a lot of time troubleshooting that much code.


Fishhead
Novice

Feb 17, 2016, 2:42 PM

Post #15 of 16 (4054 views)
Re: [FishMonger] Sessions variables [In reply to] Can't Post

FishMonger, I want to thank you and Chris for your help. I will shift my efforts to writing some test scripts and see where that may take me.

In the mean time I simply inserted some code to write the file name to a file and then later read the file. Seems to work, though it certainly is not elegant.

In the first subroutine I inserted:

Code
my $delfile=$form{'BIDTOVIEW'}; 
open (DELFILE,">./Deletefile.txt");
print DELFILE "$delfile";
close (DELFILE);


And then in the second subroutine I placed:

Code
open (DELFILE,"<./Deletefile.txt"); 
chomp (my $delfile = <DELFILE>);
close (DELFILE);
unlink ("$config{'basepath'}$config{'closedir'}/$delfile.dat");


I read somewhere that people either love Perl or hate Perl. I can honestly say that I have experienced both emotions.

Pete


Zhris
Enthusiast

Feb 18, 2016, 5:53 AM

Post #16 of 16 (4042 views)
Re: [Fishhead] Sessions variables [In reply to] Can't Post

Pete,

Stick to writing your test scripts, they will be most valuable, but just another thought. If its an auction script with a user system already in place, does it already use a stateful session via CGI::Session, if so then this would likely collide with your new session if both use the default cookie name.

Also, your I/O code inevitably works for a single user, but multiple users would share the same file. You would need a file per user identified by a unique id. This is effectively a session and the process CGI::Session is designed to handle.

Its regularly difficult when modifying a large script, particularly one someone else wrote carelessly. Perl scripts are especially difficult with their vast range of syntax styles. It gets easier the more you do it and hitting problems only helps to develop your knowledge and understanding in areas you would otherwise never cover.

Chris


(This post was edited by Zhris on Feb 18, 2016, 5:56 AM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives