PHP Session won't update with AJAX

I’m not sure if the problem can be solved by PHP or Javascript.

I have an ajax call on my index.php page that is supposed to update a PHP session.
Specifically the session stores the quantity of each product added to the session.

This is the php file that is called by the AJAX script:
ajax-api.php


// AJAX passes variables:  q_code, q_qty, sess_id

// make sure using same session id
session_id($_GET['sess_id']);

// the correct number is displayed, which means I am able to access the right session
echo "alert('original quantity: ".$_SESSION['quote'][$_GET['q_code']]."')";

$_SESSION['quote'][$_GET['q_code']]=$_GET['q_qty'];

// this also shows the correct number
echo "alert('final quantity: ".$_SESSION['quote'][$_GET['q_code']]."')";


The problem is that it does not actually save to the PHP Session!

As soon as I refresh the page or go to another page, that session variable is back to the original quantity.

The session information is saved in a MySQL database. I do not know why the ajax script would return the correct quantity, but then subsequent pages show the wrong quantity.

Any suggestions on whether PHP or JavaScript would fix this problem?

Thanks!

you need to call session_start() before you can use a session. manually setting the session id via session_id() must come before starting the session.

Still doesn’t work!
(Note I actually had called session_id and session_start in the above code. I actually called session_id twice accidentally, but even with the calls in the order below and no second call, it did not work)


// AJAX passes variables:  q_code, q_qty, sess_id

// make sure using same session id
session_id($_GET['sess_id']);
session_start();

// the correct number is displayed! The correct session is accessed
echo "alert('original quantity: ".$_SESSION['quote'][$_GET['q_code']]."')";

$_SESSION['quote'][$_GET['q_code']]=$_GET['q_qty'];

// this also shows the correct number after the session variable is set!
echo "alert('final quantity: ".$_SESSION['quote'][$_GET['q_code']]."')"; 

but it does not save!

What happens when you directly goto that url in your browser, complete with query string args?

clear your cookies.

After clearing cookies, I added two products to the quote. It showed 1 of each.
Then I went to another page and back just to make sure (not using the back button but clicking a link)

Then I pasted the ajax URL which showed the success message embedded in javascript for example: alert(‘quantities updated’)

I think it has to do with the http headers, since these are not sent or received in the ajax call (from what I understand).

What drives me mad is that Live HTTP Headers in Firefox only shows the session ID, so the information should be stored in MySQL when the ajax is called, and still be there when I visit the next page!

$_SESSION becomes locked and unchangeable once headers are sent to the browser. Headers will be sent when the first line of output is sent to the browser. So your problem here is your debug code that is echoing output before you apply your change :slight_smile:

On a related note, if you are using prototype.js framework you can make debugging AJAX much easier in firefox using the firebug console tool Using your script as an example


// AJAX passes variables:  q_code, q_qty, sess_id

/* Set our response header to javascript. Prototype.js will
 * eval our response out to the console (though any other
 * function can be called straight out of PHP with this
 * technique
 */
header ('Content-Type: application/javascript');

/* Now we will start an output buffer to insure that we do
 * not output anything before we are ready to end our
 * script
 */
@ob_start();

// make sure using same session id
session_id($_GET['sess_id']);
session_start();

// the correct number is displayed! The correct session is accessed
echo "console.info('original quantity: ".$_SESSION['quote'][$_GET['q_code']]."')";

$_SESSION['quote'][$_GET['q_code']]=$_GET['q_qty'];

// this also shows the correct number after the session variable is set!
echo "console.info('final quantity: ".$_SESSION['quote'][$_GET['q_code']]."')";

/* Now that we're done we close the session formally */
session_commit();

/* And return our results as a javascript
 * prototype will auto eval the response
 * and populate the firebug console.
 */
$output = @ob_get_clean();
echo $output;

This is not true of native php sessions, as well as most custom session handlers.

This has been my experience with them though. Tell you what, if he applies the fix and it works chances are good that you’re wrong. If it still fails to work then I’m wrong.

The way I’ve written PHP for some time now all output is echoed on the last line of the program to avoid problems of this nature.

Thanks for the suggestion, but I tried it and did not change anything.

I also output everything on the last line, but I do not use ob_start and ob_get_clean. I store each part of the web page in variables thenconcatenate them in a template that is echoed right at the end of the script.

What is interesting is that the “last active” column in my session table in MySQL is updated when I do the ajax call, but the session data column is unchanged.
Look at the following three lines for session_id, last_updated, and session_data.

The first is after adding two products
The second is after the ajax call
The third is after hitting an “update” which posts the data.

last_updated
1243624388
1243624659
1243624706

session_id
bc22b97b080c1bf8c26422f7e08ef603
bc22b97b080c1bf8c26422f7e08ef603
bc22b97b080c1bf8c26422f7e08ef603

session_data
quote|a:2:{s:26:“13342-200905141315-DS1052D”;s:1:“1”;s:27:“13345-200905141315-DS1102CA”;s:1:“1”;}
quote|a:2:{s:26:“13342-200905141315-DS1052D”;s:1:“1”;s:27:“13345-200905141315-DS1102CA”;s:1:“1”;}
quote|a:2:{s:26:“13342-200905141315-DS1052D”;s:1:“5”;s:27:“13345-200905141315-DS1102CA”;s:1:“2”;}

Keep in mind everyone here needs to make a whole lot of assumptions about how your custom session handler is programmed. An assumption of the native files based behavior is out the window.

Consider doing some logging, to a file. Put debug statements throughout your session handling code so you can see which functions get called with which values for which request data. You could group debug statements per http request by using uniqid() once per request.

did you check that session variable is not updated anywhere else but on the ajax page.?

The problem is, the php script doesn’t receive a cookie, letting it know that session data on the server is associated with the ajax call. You are going to have to pass your session cookie id to the php script within the headers of your ajax call:

<?php echo "http.setRequestHeader('Cookie','yourcookiename=" . session_id() . "');''; ?>

This assumes you are generating the ajax within php, but you will need to get the cookie value to the php script, or it will never work.

I am a little confused why you pass along the session id? And also why some people recommend you need to pass along a cookie manually?

By default neither of those are needed, as the cookie (which usally contain the session id, unless disabled on the server) is passed along on every web request if its set on that location on the server.

The issue is caused by the session handler/system. You mentioned its stored in the database, how does your session handler look and how is it called?

If you have not tried it yet, revert back to the filebased session system just to test if it works as expected then.

If it works then, its possible you might need to register a shutdown method for the session handler or there is a issue/bug in the session handler.

Note, you also might want to validate the data to assure its something you expect. I.e. is it a valid product, is the product available etc etc. As the way it is now, I can easily order products that are for example no longer available. This could again get you into a pickle if you display a confirmation page and charge the CC at once, as you have entered a legal agreement to deliver said product.

Michael Morris:
You can change the Session data even after the headers has been sent. If your not able to do this, its most probably an issue with your script. Keep in mind that depending on your session handeling you sometimes need to register the shutdown method.

It looks like your AJAX call passes the sessionid just fine.
Setting the session ID is not needed. XHR sends any cookies for the given domain it sends the HTTP Request to just like a normal page load.

It looks like the problem is your handling the request made through XHR on the server side. What is the request, does it really do an update to the session data?

Do you have any way of getting the HTTP Request and response? eg: Firebug, Wireshark etc.?

Look at the following example code which updates the session variable as expected and i hope this will help you out.


<?php
session_start();
if(!isset($_SESSION['quantity']))
	$_SESSION['quantity'] = '100';
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>AJAX Autoload</title>
</head>
<script type="text/javascript">
var strLoading = '<div style="padding:2px;color:#FF0000;"><img src="ajax-loader.gif" border="0" /> Loading...</div>';
function sendGetRequest(para_url, contentDiv, divLoading){
	var url = "";
	var xmlHttp = GetXmlHttpObject();
	if(para_url.indexOf('?')) url = para_url + "&rand=" + Math.random();
	else url = para_url + "?rand=" + Math.random();
	
	xmlHttp.onreadystatechange = function(){
		afterStateChange(xmlHttp, contentDiv, divLoading);
	}
	xmlHttp.open("GET", url, true);
	xmlHttp.send(null);
}

function afterStateChange(xmlHttp, contentDiv, divLoading){ 
	if(xmlHttp.readyState < 4){
		showLoading('On', contentDiv, divLoading);
	}
	if(xmlHttp.readyState == 4 || xmlHttp.readyState == "complete"){
		showLoading('Off', contentDiv, divLoading);
		document.getElementById(contentDiv).innerHTML 	= xmlHttp.responseText
	}
}

function showLoading(flag, contentDiv, divLoading){ 
	if(divLoading != ''){
		var objLoading = document.getElementById(divLoading);
		if(!objLoading) var objLoading = document.getElementById(contentDiv);
		if(flag == 'On') objLoading.innerHTML = strLoading;
		else if(flag == 'Off') objLoading.innerHTML = '';
	}
}

function GetXmlHttpObject(){
	var xmlHttp;
	try{
		/* Firefox, Opera 8.0+, Safari */
		xmlHttp = new XMLHttpRequest();
	}
	catch (e){
		/* Internet Explorer */
		try{
			xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
		}
		catch(e){
			try{
				xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
			}
			catch (e){
				alert("Your browser does not support AJAX!");
				return false;
			}
		}
	}
	return xmlHttp;
}
</script>
<body>
<div id="showStatus"></div>
<form name="frm1" id="frm1">
Quantity: <input type="text" name="qty" id="qty" value="<?php echo $_SESSION['quantity'];?>" /> <br />
<input type="button" value="Update Quantity" name="btnQty" id="btnQty" onclick="sendGetRequest('update.php?sess=<?php echo session_id();?>', 'showStatus', 'showStatus');" />
<input type="button" value="Reload Page" name="btnReload" id="btnReload" onclick="document.location='./';" />
</form>
</body>
</html>

update.php


session_start();
session_id($_GET['sess']);
$_SESSION['quantity'] += 1;
echo '<span style="color: green;">Quantity was updated to : ' . $_SESSION['quantity'] . '</span>';

@rajug:

I don’t think your code will work unless you put more copyright info :smiley:

What do you mean? You can use the code or even modify and sell if you want…:slight_smile:

I was joking. There is a lot of copyright info in your code, so I was laughing because it is more than the code!

As a some poster said calling session_id with a parameter is not needed,
and will acutally create a new session…which could explain this

Here’s another thing that helped me recently, and I don’t know if it applies to you. If the php script is only updating a session, and isn’t sending anything back to the browser, you will still need to have the php script send a 200 - OK header, and a space character or some sort of data. In my case, it was only certain browsers, but these browsers weren’t “seeing” any response from the php script until I did what I am saying.

The script I am talking about is the theme changer script on my website.