/    Sign up×
Community /Pin to ProfileBookmark

Session variables intermittently lost

This one has me tearing my hair out. The problem is intermittent, and so far I haven’t been able to locate it.

A few times a day my client’s web site loses everything in $_SESSION. I’ve searched for an obscure bit of code that creams $_SESSION or kills the session; I’ve found none.

Since the problem is very rare, the only way I can investigate it is to put lots of logging statements in my code and try to reconstruct what happened after a problem occurs. It makes the testing cycle very slow, and going through the data is slow, too.

The problem seems to be associated with two of the site’s scripts; it always happens when those two scripts are loaded (or shortly before, after my last test of $_SESSION on the preceding page, or shortly after, before I can test it again). I haven’t identified any common factor that would point toward a guilty party, though.

I found a couple of references to similar problems on the web; one is associated with PHP 5.2.6, and the other with redirecting by writing a header without closing the session first. Neither one seems to apply here: the server is running 5.2.17, and the site doesn’t do redirection by writing headers.

Does anyone have suggestions about what to look for?

Or general advice on how to approach a problem like this one?

to post a comment
PHP

15 Comments(s)

Copy linkTweet thisAlerts:
@NogDogJan 15.2012 — One possible issue can be if some links use the "www." sub-domain and others don't, which can cause the session cookie to not be sent by the client in some cases. (I don't remember off the top of my head whether it's when you go from "www.domain.com" to "domain.com", or vice versa that causes the problem.) The fix is to set the session.cookie_domain setting in your PHP config to ".domain.com" (note the leading dot), so that the session ID cookie gets sent regardless of whether or not you specify the "www." sub-domain or not.
Copy linkTweet thisAlerts:
@jhsachsauthorJan 15.2012 — if some links use the "www." sub-domain and others don't[/QUOTE]

That's another possible cause, but not here. The site uses only relative URLs for page loads.
Copy linkTweet thisAlerts:
@criterion9Jan 15.2012 — If you are on a shared host there might be a problem with garbage collection with another site. If you can choose a folder only you are supposed to have access to then usually that problem will become a non-issue.
Copy linkTweet thisAlerts:
@NogDogJan 15.2012 — If you are on a shared host there might be a problem with garbage collection with another site. If you can choose a folder only you are supposed to have access to then usually that problem will become a non-issue.[/QUOTE]

Good point. It can even be another app on your domain that messes with the session.gc_* settings.
Copy linkTweet thisAlerts:
@jhsachsauthorJan 16.2012 — If you are on a shared host there might be a problem with garbage collection with another site.[/QUOTE]

That is a possibility. Is there a way to determine whether it is the cause? If it is, what can be done about it, short of getting off the shared host? (That would not be an unreasonable move, but it's not a viable short-term solution; it would require too much planning.)

It can even be another app on your domain that messes with the session.gc_* settings. [/QUOTE]

That's one we can rule out. There are no other apps running in the domain, and the one that's there does not even refer to those settings.
Copy linkTweet thisAlerts:
@NogDogJan 16.2012 — If you can change the [url=http://www.php.net/manual/en/session.configuration.php#ini.session.save-path]session.save_path[/url] (e.g. in php.ini or .htaccess) to some directory in your domain's file system, you should avoid any other domain's settings conflicting with yours. (A properly configured shared host should already do this, but not all hosting companies are created equal, eh? ? ) Most likely you'll need to make sure that the specified directory has global read/write access.
Copy linkTweet thisAlerts:
@jhsachsauthorJan 16.2012 — NogDog, the server's session.save_path points to /tmp. If I interpreted your message correctly, that's a possible source of the problem because there could be conflicts between my client's temporary files, including the session files, and others on the server.

I found that I can't change the setting through php.ini or .htaccess, but I can do it through code. I tried that to see if it would help.

I defined an appropriate temp directory and set its permissions to 666. Then I added the following line to the .inc file that initializes the session:

[CODE]session_save_path( $_SERVER['DOCUMENT_ROOT'] . '/tmp' );[/CODE]

I got a tremendous number of errors, starting with one that said "session_start()... failed: Permission denied (13)" on a file in my temp directory.

I changed the directory's permission to 777, and it worked. After some experimentation, I found that 700 also works. I don't understand why PHP needs execute permission to write to the directory, but I'm comfortable with the result.

Unless I hear from you that I'm on the wrong track, I'll put this change on the production server tonight and see if it fixes the problem.
Copy linkTweet thisAlerts:
@NogDogJan 16.2012 — That should help. My UNIX skills are rusty, but I vaguely recall "execute" meaning something different in the context of a directory versus a (executable) file, so that might be normal. But yeah, getting your session data out of /tmp on a shared server should be a good thing. The annoying thing with having to use session_save_path() is that you have to do it in each script that calls session_start(), but if you put all that into an include file that every file which needs it can require(), then it's no big deal.
Copy linkTweet thisAlerts:
@jhsachsauthorJan 19.2012 — I have bad news. Moving the session files to a private directory didn't help at all.

I'm testing another theory tonight: that the newly loaded page is sometimes trying to read the session variables before the previous page has stored them. If that's the case, I hope I can fix the problem by calling session_write_close near the end of every script (again, through a .inc file).

The documentation for session_write_close doesn't discuss this problem at all, but I've found references to it that suggest (don't clearly state) that it might be what is happening.

If not, I don't know what else to try. So far the only thing I can think of is to pull out the session code completely and write my own equivalent service using a database. That would be really ugly, and I hope I'm not forced to do it.
Copy linkTweet thisAlerts:
@criterion9Jan 19.2012 — I have bad news. Moving the session files to a private directory didn't help at all.

I'm testing another theory tonight: that the newly loaded page is sometimes trying to read the session variables before the previous page has stored them. If that's the case, I hope I can fix the problem by calling session_write_close near the end of every script (again, through a .inc file).

The documentation for session_write_close doesn't discuss this problem at all, but I've found references to it that suggest (don't clearly state) that it might be what is happening.

If not, I don't know what else to try. So far the only thing I can think of is to pull out the session code completely and write my own equivalent service using a database. That would be really ugly, and I hope I'm not forced to do it.[/QUOTE]

Are you using some fancy client-side asynchronous calls to multiple files that depend on each other or something? I've never had a problem with synchronous calls in the way you are describing.
Copy linkTweet thisAlerts:
@jhsachsauthorJan 19.2012 — No, everything is utterly basic. At the top of each script I load a .inc file, which executes some setup code, then the following. (The first line is new, for dealing with the "shared temp directory" theory.)

[CODE]session_save_path( $_SERVER['DOCUMENT_ROOT'] . '/tmp' );
define( 'SESSION_LIFE', 24*60*60 );
session_start();

if ( isset($_SESSION['LAST_ACTIVITY']) &&
( time() - $_SESSION['LAST_ACTIVITY'] > SESSION_LIFE ) ) {
// Last request was too long ago.
if ( ini_get('session.use_cookies') ) {
// Make the cookie already expired.
$params = session_get_cookie_params();
setcookie( session_name(), '', time()-42000,
$params['path'], $params['domain'], $params['secure'], $params['httponly']
);
}
// Destroy session data in storage & the session variables.
session_destroy();
session_unset();
}
// Update the last activity time stamp.
$_SESSION['LAST_ACTIVITY'] = time();[/CODE]


I've deleted some logging code which I added to diagnose the problem, and which hasn't affected its symptoms.

BTW, I've established that the cookie destroying code is not involved by logging the value of $_SESSIONS before it is executed. In cases where the problem occurs, $_SESSIONS is already empty.
Copy linkTweet thisAlerts:
@criterion9Jan 19.2012 — No, everything is utterly basic. At the top of each script I load a .inc file, which executes some setup code, then the following. (The first line is new, for dealing with the "shared temp directory" theory.)

[CODE]session_save_path( $_SERVER['DOCUMENT_ROOT'] . '/tmp' );
define( 'SESSION_LIFE', 24*60*60 );
session_start();

if ( isset($_SESSION['LAST_ACTIVITY']) &&
( time() - $_SESSION['LAST_ACTIVITY'] > SESSION_LIFE ) ) {
// Last request was too long ago.
if ( ini_get('session.use_cookies') ) {
// Make the cookie already expired.
$params = session_get_cookie_params();
setcookie( session_name(), '', time()-42000,
$params['path'], $params['domain'], $params['secure'], $params['httponly']
);
}
// Destroy session data in storage & the session variables.
session_destroy();
session_unset();
}
// Update the last activity time stamp.
$_SESSION['LAST_ACTIVITY'] = time();[/CODE]


I've deleted some logging code which I added to diagnose the problem, and which hasn't affected its symptoms.

BTW, I've established that the cookie destroying code is not involved by logging the value of $_SESSIONS before it is executed. In cases where the problem occurs, $_SESSIONS is already empty.[/QUOTE]


Have you checked what the setting for the session expiration is in the php.ini file? It might be set lower than your expiration you are using in your code...
Copy linkTweet thisAlerts:
@jhsachsauthorJan 19.2012 — Have you checked what the setting for the session expiration is in the php.ini file? It might be set lower than your expiration you are using in your code... [/QUOTE]

I don't think that fits the evidence. I added may log entries in my effort to locate the problem, and they usually show that session data disappears when the previous page has been loaded only a few minutes, a typical length of time for a user to look at a page.

Just to be sure, I checked the host's session.gc_maxlifetime value and found that it is 1440.
Copy linkTweet thisAlerts:
@criterion9Jan 20.2012 — I don't think that fits the evidence. I added may log entries in my effort to locate the problem, and they usually show that session data disappears when the previous page has been loaded only a few minutes, a typical length of time for a user to look at a page.

Just to be sure, I checked the host's session.gc_maxlifetime value and found that it is 1440.[/QUOTE]


Did you also check session.cookie_lifetime?
Copy linkTweet thisAlerts:
@jhsachsauthorJan 21.2012 — I'll try that.

The documentation is unclear about whether to call session_set_cookie_params or ini_set('session.cookie_lifetime'). As far as I can tell by experimenting, they do the same thing. To be safe, I'm calling both.

The default value was 0, so I'm not too hopeful that changing it will help.
×

Success!

Help @jhsachs spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.10,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...