Scenario:
All pages of xyz.com use sessions to check if I am logged in and display customized content. I am logged into xyz.com using Firefox on my iMac. Now, when I try to visit, xyz.com/page1.php and xyz.com/page2.php at the same time, until page1 loads page2 is not processed.
Code Samples
xyz.com/contains (links):
/* session check code here */
Page1
Page2
xyz.com/page1.php contains:
/* session check code here */
echo "Hello";
flush();
sleep(5);
echo "done";
xyz.com/page2.php contains:
/* session check code here */
echo "Second Page";
/* {insert code} to log time() to log.txt */
Testing
Here's what I do. I login to shell and keep this command running:
user#xyz.com [~/public_html]# tail -f log.txt
From my browser (Firefox), (on xyz.com), I open multiple instances of page1 and page2 in tabs. If you can see, page1 will at least take 5 seconds to load. Now, until page 1 loads, request to page2 is pending. It's not at all processed. How do I know that? The code at the end of page2 logs access time to a file which I am monitoring. Let's consider the following case:
Requests fired to (in order)
1. page1.php
2. page2.php
3. page2.php
4. page1.php
5. page1.php
6. page2.php
Result (approx)
1237961919 <-- #2
1237961920 <-- #3
1237961931 <-- #6
What was expected (approx)
1237961915 <-- #2
1237961915 <-- #3
1237961915 <-- #6
^Assuming that all calls were fired at the same time
Observation
While the calls are being processed, I am obviously not able to visit xyz.com as well (being another request). However, if I open it in another browser (Safari), it works well. That's because it's another session?
Possible Causes
The server has some security in place?
I am more of a coder but if someone can give pointers, I can check the server (VPS) to see if apache/php is misconfigured.
I am not handling sessions correctly?
Does it matter? Shouldn't the second request be processed as a different thread?
Aliens are controlling my VPS.
Ok, kidding. I am just tired of tracing this issue. Would really appreciate if anyone has any clue what could be going wrong here.
Your server is using file based sessions. When one request is pending, the session file is locked and the server will queue any later requests trying to use the same session.
Switch to database-stored sessions and you'll be fine.
Try to call session_write_close() in page1.php after you finished session check code.
/* session check code here */
session_write_close();
echo "Hello";
flush();
This should close the session file (remove the lock) and page2.php can open it.
http://php.net/manual/en/function.session-write-close.php
check your network.http.max-persistent-connections-per-server and network.http.max-connections-per-server.
Never encountered anything like that. Are you sure you're not using CGI instead of FastCGI? Also check how many php processes your webserver spawns and how many requests they can handle each.
To help with testing you might want to put sleep function in test-page.
Dont know for php, but with asp.net, the session object cannot be used in read/write by more than one thread at a time. So it produced something like you describe. The second request wait for the session state to be "unlocked" to start processing. It does makes sense, that way the session object is consistent for all the processing of the request.
Related
This is most likely a PHP noobie question.
To test some server caching configuration, I added the following code to my test suite:
<?php
if (array_key_exists('visited', $GLOBALS))
{
print_r("We have already met");
} else {
print_r("Hello ShimmerCat");
}
$GLOBALS['visited']=1;
?>
I'm expecting this code to take differents path of the branch during a first and second request, but it is returning the second message always. How can I achieve what I want?
PHP itself is stateless, meaning every time a user visits a PHP page, the whole operation is done from scratch with each variable being defined as in the script.
If you want to store data between views, the basic way is with cookies. If you want the details of what's remembered to be secure, use session cookies.
I am working with three PHP files. Two serve as web pages and the other in an external server side script. The server side script is included in both web pages files and what I want to do is have some buttons on the first page and depending on which one is clicked, populate the second page with data and then redirect to it.
With the code below, the idea was to pick up the button click, figure out which button was clicked, and then call the function to run the proper query and set the needed variables. I don't understand why the variable is not getting set.
Thanks to anyone looking at this!
First page's button (index.php):
<input type="submit" id='details' name='details' value='Submit'/>
Second page where variable is undefined when page is loaded:
<h4><?php echo $selected_button; ?></h4>
External script:
function detailBuilder(){
$selected_button = "Option One";
//header('Location: details.php'); if this is here, the page still redirects but the variable doe not get set
//More will happen here once it works
}
if(isset($_POST['details'])){
detailBuilder();
header('Location: details.php');
}
As several people have pointed out previously, you redirect to another file. At that point, all locally defined variables are gone - you no longer have access to them.
Look into PHP sessions in PHP's documentation. Sessions will allow you to transfer these variables from request to request. However, sessions will only work if you are running some form of webserver.
UPDATE: Also to note, as other people (once again) have pointed out, $selected_button = "Option One" will ONLY apply inside the "scope" of the function detailBuilder. So calling detailBuilder() creates a variable called $selected_button inside the function, and then immediately discards it.
UPDATE 2.0: Sorry for so many updates. Here's an example of setting a session:
Update 3.0: updated code slightly
First things first. Make sure you start the session.
session_start();
You're going to have to call session_start() at the start of any php script! That means that the first file that executes every time should have session_start() at the top.
External Script:
$_SESSION["selected_option"] = "Option One";
Script where originally it was undefined:
$selected_details = $_SESSION["selected_option"];
?>
<h4><?=$selected_details?></h4>
I have two PHP scripts which both have an "include_once('authentication.inc');" script near the top. Both scripts reference the same authentication file. That authentication file currently performs a header redirect (like "header('Location: index.php');") if the user is not signed in.
In one file (A.php) the immediate next line of code after the include of the authentication file is:
if(isset($_GET['delete']))
mysql_query("DELETE FROM table WHERE index=".$_GET['delete']);
In the other file (B.php) there are several other includes which occur before the same "delete code" listed above.
So the authenticate.inc file looks like:
if(!valid_credentials($username,$password))
header('Location: index.php');
And file A.php looks like:
include_once('authenticate.inc');
if(isset($_GET['delete']))
mysql_query("DELETE FROM table WHERE index=".$_GET['delete']);
And file B.php looks like:
include_once('authenticate.inc');
include_once('other.php');
include_once('file2.php');
include_once('onemore.php');
if(isset($_GET['delete']))
mysql_query("DELETE FROM table WHERE index=".$_GET['delete']);
Yet when I call A.php?delete=5, that record is deleted from the database while when I call B.php?delete=8 that record is not.
I have checked the 3 intermediary includes and do not see any die() statements, nor any other header redirects.
So while it's clear that A.php is continuing to execute after the header is sent, why isn't B.php doing the same thing? Is the header being sent before the next set of imports?
**
Also: I know to add the die() or exit command after the headers are sent. I'm working on someone else's code and trying to explain behavior, not writing this myself.
**
No way to tell. If the starts are aligned properly, the header coud be sent to the client browser immediately and the bowser will start closing the current connection and request the new URL immediately. This'll cause the current PHP script to start shutting down.
On the other hand, if the caches are slow and the network glitchy, the client browser may not get the redirect header for seconds/minutes/hours, and the script could continue executing indefinitely.
In general you should assume that the moment you've issued a header redirect that the script is basically "walking dead" and should not do any further work.
The sole exception to this rule is that you CAN use ignore_user_abort(TRUE), which tells PHP to NOT shut down when the remote user disconnects. That'd allow you to continue on working even though the browser has shut down the connection and moved on to the new page.
Update your authenticate.inc file to die() after the redirect. This will prevent any other code from executing.
if(!valid_credentials($username,$password)) {
header('Location: index.php');
die();
}
Without it, and depending upon your server configuration, the rest of the PHP code will be executed on the server even after the headers are transmitted back to the client. Until the client closes the connection, the code will run.
Just put an exit() after the header redirect. It will stop all execution after the redirect.
There is probably some output in either of the included files, with echo or other outputting functions. If the browser by then has followed the redirect and aborted the connection, the PHP script will by default exit. You can change this behaviour with ignore_user_abort(true);. You should however use die(); after the Location header. If the query execution is wanted, just put that query before the Location header. Don't forget to use proper escaping for the input, otherwise the script could be a target for a mysql injection attack.
To answer your question, it seems that the browser will wait until your script finished execution and only then will request another location.
Please note that you shouldn't use GET method to delete records.
As for the not deleting id=8 - just debug it. Not a big deal.
A good var_dump() is always better than some vague ideas about headers.
I think I forgetting something in my code but can't find what.
On my server I have simple logging.php file.
If I pass user/password parameters then a new session is created.
If I pass loggout the session is destroyed.
If I pass report the list of current session variables are reported on screen.
If I test the code writing urls in my browser all works fine. First invoke .../logging.php?user=xxx&password=xxx. The session is started and the session variables are reported to me. And finally I destroy the session passing the logout parameter.
If user request a report or a logout and no session exists a HTTP-401 error code is returned to client.
On the other hand I have a piece of JavaScript code that I can inject on web page using a bookmarklet. Once code is injected I show a toolbar where user can write user/password and send to server.
The logging actions seems to works fine, and server returns me a 200 status code, but later if I make a request to logout the server returns me a 401 error, which mean no session exists.
I was using chrome and looking at HTTP request and responses can see that when I logging the server returns in the response different values for PHPSESSIONID.
That means two different AJAX request are considered different sessions. The server seems to not recognize the second request from AJAX as if it was started by the same client.
Repeat, the PHP code works fine if I execute using browser directly but not with AJAX request, so I think I forgetting something in AJAX.
Any ideas?
Thanks in advance.
Update
To be more concise, my problem is calling php from JavaScript. It seems there are no sessions started.
Imagine a very simple PHP code:
logging.php: given a user/password starts a new session and also stores 'user' names as a session variable.
request.php: which returns the user name stored as session variable.
logout.php: which destroys the session.
My first AJAX request start a PHP session. That seems fine because a PHPSESSIONID cookie is returned from server. Also I store the user name as session variable.
The second AJAX request tries to get the user name (stored in the session) but it gets nothing and in addition a new PHPSESSIONID cookie is returned from server.
I know it seems impossible and more when I'm testing using browser url request and works fine, but it's the truth.
I'm forgetting something on AJAX, expiration times or something similar?
Update again
I made some tests and I found the problem but not the solution.
My JS code is injected through a bookmarklet.
When I inject the code in a HTML page from my server, the AJAX requests works fine. The first (logging) request gets a PHPSESSID which is passed in subsequent request to the server.
On the other hand If I load google.com and inject the code, the first (logging) request gets the PHPSESSID too but later it is not sent with next requests.
Anyone has experienced the same issue? which is the problem?
Thanks in advance.
Update again, again
Ok finally I found my problem. Because my JS is injected from a different domain (current page is from domainA and my JS code comes from domainB) cookies are not cross domain, so PHPSESSID can be shared.
A possible soulution is when I logging I will return the PHP session ID in pice of JSON data and use it for subsequent calls.
If I'm correct, you're trying to log in a user by making an AJAX request to a URL, with the username and password provided in the URL? That's not really a safe construction, the password is very vulnerable this way?!
I would advice you to implement jQuery, and transer the login details using the $.POST command:
http://api.jquery.com/jQuery.post/
Make sure all your files (also those requested by AJAX) contain session_start(); on top of the file.
When every file contains session_start(); and you're using the same $_SESSION variables to check if a user is loggedin, it should work!
Are both of your AJAX requests coming from the same page? The requests are Asynchronous, so it may be that the "logged in?" request is returning its result before the "log in" request goes through.
From what you have asked, I hope your code is (at its beginning more or less) something like:
A file logging.php like this:
<?php # file : loggging.php
if(!ini_set('session.auto_start'))
// more stuff
if(!empty($_REQUEST['user']) && !empty($_REQUEST['passwd'])) {
session_regenerate_sid(); // This is important (1)
$_SESSION['user'] = $_REQUEST['user'];
// Whatever
}
A file request.php like this..
<?php # file : request.php
if(!ini_set('session.auto_start'))
// Whatever stuff to process data
var_dump($_SESSION);
// Or a nice foreach($v as $i => $x) {
// echo("[$i] => $x\n<br />");
// } instead :)
And your logout.php should read something like..
<?php # file : logout.php
if(!ini_set('session.auto_start')) session_start();
session_destroy();
You are probably not calling either session_start() or you are calling it twice.
To check this out try this: change all your session_start() lines for:
session_name('MYCoolNewName');
session_start();
Now your session should not read PHPSESSID, instead it should be MYCoolNewName.
If it is not, then your problem is the aforementioned.
(1) I put as important session_regenerate_sid() because opened authenticated sessions are a threat out there. I'll demonstrate it with an example.
Alice visits coolwebsite.com/login.php, which gives her a SID which I'll call AliceSID.
Alice tells Bob to visit coolwebsite.com/login.php?PHPSESSID=AliceSID, and when Bob does Alice could log in his account unless Bob's session was regenerated.
I have a very similar setup to the person here:
PHP Background Processes
i.e a very long script that takes up to 10 minutes. However, I need the person who calls the script redirected back to the homepage while the script works. In other words, I need the user experience to be something like this:
click the update button
script begins to execute, can take up to 10 minutes
user is redirected back to the home page immediately
Is this possible using only PHP? I gather I will need
ignore_user_abort(true);
set_time_limit(0);
But how do I redirect the user? I can't use javascript because output only gets written to the page at long increments, and I want the redirection to be immediate. Can I use headers? Or will that mess with things?
Alternatively, I could use the cron job approach, but I have zero experience in making a cron job or having it run php code (is that even possible?)
Thanks,
Mala
Update:
Using headers to redirect does not work - the page will not load until the script is done. However, eventually the webserver times out and says "Zero-Sized Reply: The requested URL could not be retrieved" (although the script continues running). I guess my only option is to go with the cron job idea. Ick!
The most obvious solution to me would be splitting the redirect and the background calculation in two separate files and let the redirect script execute the 10-minute script:
job.php:
<?php
// do the nasty calculation here
redirect.php:
<?php
// start the script and redirect output of the script to nirvana, so that it
// runs in the background
exec ('php /path/to/your/script/job.php >> /dev/null 2>&1 &');
// now the redirect
header ('Location /index.php');
Assumptions for this to work: You should be on a Linux host with either safe_mode disabled or having set the safe_mode_exec_dir appropriately. When you're running under windows, the exec string needs to be adapted, while the rest about safe_mode remains true.
Notice: When you need to pass arguments to the script, use escapeshellarg() before passing it on, see also the PHP manual on exec
I've tried several methods and none seems to work, I've even tried to use register_shutdown_function() but that also failed. I guess you're stuck with making a cron job.
I just remembered something (but I haven't tested it), you can try to do something like this:
set_time_limit(0);
ignore_user_abort(true);
ob_start(); // not sure if this is needed
// meta refresh or javascript redirect
ob_flush(); // not sure if this is needed
flush();
// code to process here
exit();
Not sure if it'll work but you can try it out.
I have a similar situation with processing logins.
To keep it short...
I get a PDT, IPN and each sends me a logging email.
An email is sent to client on IPN VERIFIED to give serial number and password to client.
As PDT and IPN I use goto to send me a logging email instead of a bunch of sequential ifs.
On reading many answers I studied each to figure what would suit my isssue.
I finally used...
<?php
ignore_user_abort(TRUE); // at very top
As I worked through the progressive checks (no ifs), if they failed I use for example...
$mcalmsg .= "Check [serialnbr]\r\n";
if (empty($_POST['serialnbr']))
{ header('Location: '.$returnurl.'?error=1');
$mcalmsg .= "Missing [serialnbr]\r\n";
goto mcal_email; // Last process at end of script
}
else
{$serialnbr=strtoupper(htmlspecialchars(trim($_POST['serialnbr'])));
$mcalmsg .= "[serialnbr]=$serialnbr\r\n";
}
This (so far) is working just as needed.
Of course there is more in the script but follows the same concept.
Where this says location, there are also 3 information pages that can each be displyed using the same concept.
mcal_email: //last process before ending, always gets here after all else from goto or clearing all checks.
// compose email and send
?> // end of script
Why not try the header approach and see what happens? You could also try a call to php header method and see if this does the trick. I would work on trial and error to see what will solve your problem.