I don't understand PHP sleep() behavior - php

I have this form:
<form method="post" action="secret.php">
<label for="pw">Password: </label><input type="password" name="pw" id="pw" />
</form>
This is secret.php:
<?php
if(isset($_POST["pw"])) {
if($_POST["pw"] == "hello") {
echo("<strong>Good pw.</strong><br />");
} else {
echo("<strong>Bad pw.</strong><br />");
echo("Back");
sleep(5);
}
} else {
header("Location: /tut/first/form.php");
}
?>
What happens is that if the password is wrong, it sleeps before displaying Bad pw. When I submit the form, it sleeps 5 seconds on the form page, and then changes page and displays Bad pw. Why?

What is happening is that you are causing the PHP script to sleep. The script must complete before it sends the result back to the client (the browser).* So you are causing the script to take 5 seconds longer before it responds to the client that it wasn't a good password.
Since you are not trying to avoid a brute force situation here I would suggest something like this:
<?php
if(isset($_POST["pw"])) {
if($_POST["pw"] == "hello") {
echo("<strong>Good pw.</strong><br />");
} else {
echo("<strong>Bad pw.</strong><br />");
echo("<script type=\"text/javascript\">");
echo ("setTimeout(function() {");
echo ("window.location = form.php;"); //might need a more complete URL here
echo ("}, 5)"); //sleep for 5 seconds before redirecting
echo("</script>");
sleep(5);
}
} else {
header("Location: /tut/first/form.php");
}
?>
*The output is actually sent back as it's written in the PHP script but with buffering you don't see this making much of a difference except in headers and very large pages.

You need to look into output buffering, although from what I see, the logic is flawed.
This may help

If you want to echo something to the browser right away, try doing flush() when you want to flush the output buffer to the browser. Also, you may need to disable compression (like gzip) which can interfere with output buffering.
However with that said, you're going about this completely wrong. All that the user would have to do is open up another tab / refresh and the server will validate the login information again, so doing sleep() isn't going to have the effect you think it is.
I have actually designed something similar to this and this is what I did:
Create a database table called failed_logins and another called login_bans and both are based on IP address. Every time a user provides incorrect information, add an entry in the failed_logins table. What you want to do is tier it so that after the first login, the user is banned for 5 seconds, after the second, it goes up to 15 seconds and 3 or more within a certain period (like say 2 hours) the user is banned for 45 seconds. This is all done server-side so that there is nothing the user can do to circumvent the ban. So you will have to check their IP every time they access the page to see if their IP has been banned.
Then on the client-side display a countdown timer containing the number of seconds remaining in the ban and disable the submit button.

Related

PHP Session works fine under one directory, but the same code fails in a different directory

I have the below code that works fine under /t/cgi-bin/test1.php, but the same code fails under /p/cgi-bin/test1.php. Every time I run under /p/cgi-bin..., it takes me to test_log1.html right away. It is not waiting for 15 minutes before it times out to test_log1.html. Any help is appreciated.
<?php
session_start();
if(isset($_SESSION["test"]))
{
if((time() - $_SESSION['last_login_timestamp']) > 900)
{
header("location:../test_log1.html");
}
else
{
$_SESSION['last_login_timestamp'] = time();
}
}
else
{
header('location:../test_log2.html');
}
?>
Nothing wrong with the code. The file had to be recreated. It is working now. This post can be closed.
There are a few issues.
One
Perhaps because by default there is only one session per browser. It's not per tab, or per url.
So, once you have run, the session already has the last_login_timestamp. It then uses the same value for the second session.
If you want them to be different then you have to use different session ids for the two paths.
Another way is to clear/reset the session at the start of the file.
Yet another is to use a different test variable such as test1 and test2.
Two
test is never getting set, and the value of last_login_timestamp is null. I am assuming that you are setting test elsewhere. So, for testing, here is the updated code. The ?? operator ensures that the value goes to time when it is null
<?php
session_start();
if(isset($_SESSION["test"]))
{
$last = $_SESSION['last_login_timestamp'] ?? time();
if((time() - $last) > 900)
{
header("location:../test_log1.html");
}
else
{
echo 'Has not timed out';
}
}
else
{
$_SESSION["test"] = 'xx';
header('location:../test_log2.html');
}
?>
If you want three independent sessions then you have to use different session_ids or just different variables. So use test_p and last_login_timestamp_p for directory p, etc.
Three
PHP is a server side language. When run, nothing is sent to the browser, except the redirect, or echo from this code above.
Once the session variable is set, and 15 mins later you run the code, it will time out in both places. This will keep happening from then on, until the session variable is cleared.
However, it can not time out on the browser and go to a different page by itself, after 15 mins. For that you need to use javascript.
try:
header('Location: '../test_log2.html');

Sleep() Function not doing what I think it should

First, correct me if i'm wrong but i'm under the assumption that when you run the Sleep() function, it pauses running the script where it is located in the script, not at the beginning. If that is true can someone tell me why the below script waits 5 seconds and then shows both echos at the same time. NOT echo the first statement on page load and then wait 5 seconds and then fire the second echo....
echo "Your account username has been updated, you will now be redirected to the home page!";
sleep(5);
echo "REDIRECT!";
In your code PHP execution will pause for 5 seconds but it will not render itself part by part. i.e. It will not show the first statement and then the second. PHP keeps all its value in output buffer and display them when its finishes execution.
What happens is, it holds the value of first echo in output buffer and then waits for 5 seconds, then is holds another echo output in output buffer and shows all at once.
What you are trying to do is a lot easier in JS.
echo "Your account username has been updated, you will now be redirected to the home page!";
echo "<script> document.setTimeout(function() { document.location('redirect.html'); }, 5000); </script>";

How to freeze a PHP script for a few seconds?

I would like to make my PHP script freeze at a screen for at least 5 seconds before loading into the next script. I have tried "Sleep()" however that is not the function I am looking for as it only pause the script that is "going" to be loaded.
Here are my codes:
echo "Welcome ",$_SESSION['username']."<br>";
echo "Click here to Logout : ".'<br>Logout';
sleep(10);
header("Location: http://192.168.11.32/phploginserver/test.php");
echo '<script type="text/javascript">window.location="test.php"</script>';
}
I would like the echo'to another page' to be delayed for at least 5 seconds before loaded so that my users can view their user name before being automatically redirected to the next page.
$time = new DateTime();
$newtime = $time->Modify("+5 seconds");
while($newtime > (new DateTime()))
{
// Do nothing
}
You cannot freeze PHP script at a screen.
Just because there are no PHP scripts at screen. But merely HTML, rendered by browser.
Such a freezing considered bad practice and don't used nowadays.
Either show a message, if it's really important, or get user to the destination immediately (preferred).
You can use some AJAX-powered tips, as stackoverflow does.
so that my users can view their user name
Don't they know it already?
Can't they see it on the next page?
What if a user got disturbed and do not notice that message?
You can use an additional parameter with the php header to delay it. Like this:
header('Refresh: 10; url=http://192.168.11.32/phploginserver/test.php');

Ajax long polling (comet) + php on lighttpd v1.4.22 multiple instances problem

I am new to this site, so I really hope I will provide all the necessary information regarding my question.
I've been trying to create a "new message arrived notification" using long polling. Currently I am initiating the polling request by window.onLoad event of each page in my site.
On the server side I have an infinite loop:
while(1){
if(NewMessageArrived($current_user))break;
sleep(10);
}
echo $newMessageCount;
On the client side I have the following (simplified) ajax functions:
poll_new_messages(){
xmlhttp=GetXmlHttpObject();
//...
xmlhttp.onreadystatechange=got_new_message_count;
//...
xmlhttp.send();
}
got_new_message_count(){
if (xmlhttp.readyState==4){
updateMessageCount(xmlhttp.responseText);
//...
poll_new_messages();
}
}
The problem is that with each page load, the above loop starts again. The result is multiple infinite loops for each user that eventually make my server hang.
*The NewMessageArived() function queries MySQL DB for new unread messages.
*At the beginning of the php script I run start_session() in order to obtain the $current_user value.
I am currently the only user of this site so it is easy for me to debug this behavior by writing time() to a file inside this loop. What I see is that the file is being written more often than once in 10 seconds, but it starts only when I go from page to page.
Please let me know if any additional information might help.
Thank you.
I think I found a solution to my problem. I would appreciate if anyone could tell, if this is the technique that is being used in COMET and how scalable this solution.
I used a user based semaphore like this:
$sem_id = sem_get($current_user);
sem_acquire($sem_id);
while(1){
if(NewMessageArrived($current_user))break;
sleep(10);
}
sem_release($sem_id);
echo $newMessageCount;
It seems common for long-polling requests to timeout after 30 seconds. So in your while loop you could echo 'CLOSE' after 30 seconds.
while(!$new_message && $timer < 30){
$new_message = NewMessageArrived($current_user);
if(!$new_message) {
sleep(10);
$timer += 10;
}
}
if($newMessageCount) {
echo $newMessageCount;
} else {
echo 'CLOSE';
}
In the Javascript, you can listen for the CLOSE.
function poll_new_messages(){
xmlhttp=GetXmlHttpObject();
//...
xmlhttp.onreadystatechange=got_new_message_count;
//...
xmlhttp.send();
}
function got_new_message_count(){
if (xmlhttp.readyState==4){
if(xmlhttp.responseText != 'CLOSE') {
updateMessageCount(xmlhttp.responseText);
}
//...
poll_new_messages();
}
}
Now, the PHP will return a response within 30 seconds, no matter what. If you use stays on the page, and you receive a CLOSE, you just don't update the count on the page, and re-ask.
If the user moves to a new page, your PHP instance will stop the loop regardless within 30 seconds, and return a response. Being on a new page though, the XHR that cared about that connection no longer exists, so it won't start up another loop.
You might try checking connection_aborted() periodically. Note that connection_aborted() might not pick up on the fact that the connection has in fact been aborted until you've written some output and done a flush().
In fact, just producing some output periodically may be sufficient for php to notice the connection close itself, and automatically kill your script.

Being redirected to the wrong place - sometimes

I have a javascript setInterval that checks an external page every 5 seconds for mail, I am finding sometimes that if I login or click a form submit at the same time as the request goes out, I sometimes find myself looking at a Y or a N (what my JS was to intercept) instead of the real link I wanted to go to.
How does one debug this? I am using firefox with firebug, my app is using PHP with javascript.
EDIT: it's almost as if the onComplete is being missed by java, and it just dumps it as the user is signing in.... it only happens when someone is changing pages and the java is running at the same time.
EDIT 2: If you want to see this for yourself, you'll need visit my site and create an account and go through the signup process (2-3 mins to do tops), the website is http://mikesandmegs.com and the beta password is goldfish. What you want to do is login just as the check mail sends its request off. Its like I need to cancel something or tell java to throw the callback out or something. You should see the requests every 5 seconds, (well it adds 5 seconds each request) but you'll see. It may take a couple try's or some luck, but it is reproducible.
This is the javascript that is running (i think I have it all posted) If I seem to be missing anything, let me know. I also posted an htnl input html that the javascript checks...
<input id="hasMail" type="hidden" value="y">
<script type='text/javascript'>
mailTimer = setInterval("checkMail();", 10000);
function checkMail()
{
// should we check the mail now?
if ($('hasMail').value == "y")
{
// remove mail new mail alert (mail-check.php returns y or n
new Ajax.Request('mail-check.php',
{
method: 'post',
postBody: '',
onComplete: checkMailNotify
});
}
}
function checkMailNotify(req)
{
if (req.responseText.length > 5)
{
$('hasMail').value = "n";
clearInterval (mailTimer);
return;
}
if (req.responseText == "y")
{
$('hasMail').value = "n";
$('topMessage').update('You have new mail...');
$('alertBox').appear();
clearInterval (mailTimer);
}
else
{
clearInterval (mailTimer);
mailInterval = mailInterval + 5000;
mailTimer = setInterval("checkMail();", mailInterval);
}
}
</script>
I know this is nowhere near a solution, but it WILL help to increase the 5 second interval, even to something like 30 seconds. I've done work with mailservers before, and we often came across problems where people would have e.g their iphone as well as their desktop mail client ping the server at very short intervals. This would result in confusing (to them) failures because of locks.
So yeah, 5 seconds for messages is very quick (it doesn't look like chat but rather just messages, is that right?). At best if you do that then the problem will happen a lot less if it all. You will however have the horrible knowledge that it can happen.
Please don't take this as an attempt at a solution to your problem. just a suggestion.
I think what's happening is that while changing pages, the data from the mail-check.php is clashing with the new request that is coming back from the network at the same time. I think a possible solution is to disable the setInterval whenever you change a page or submit a form, then re-enable it after loading the new data.
Something like:
<input type="button" onClick="clearInterval('mailTimer'); this.submit()" />
...

Categories