I'm currently learning PHP so I'm doing some training, and I've come to a problem that I don't understand.
I wrote a few lines of code on Repl.it :
$msg = "Once upon a time...";
for ($i=0; $i < strlen($msg); $i++) {
echo '<span>'.$msg[$i].'</span>';
usleep(100000);
}
Here the code is running as intended (at least as I wanted it to), which is, one character is displayed after the other with a few milliseconds of delay between each. Like in RPG dialog boxes, for instance.
Now I imported my code to Visual Studio and ran it with Xampp on localhost, and everything is displayed only after the whole loop is done working.
So, in this case, with "Once upon a time...", which is 19 character long, it loads for 100.000 ms * 19, and then I have the whole message displayed at once.
Any hint for me? :)
PHP runs on the server. By default, the server waits until the page is totally generated before sending the result to the client.
You can use a combination of ob_flush() (flush the PHP output buffer) and flush() (flush the system output buffer) to achieve what you want :
$msg = "Once upon a time...";
for ($i=0; $i < strlen($msg); $i++) {
echo '<span>'.$msg[$i].'</span>';
usleep(100000);
ob_flush();
flush();
}
However, I think it would be better to be as quick as possible on the server side, and let javascript handles the fancy rendering on the client side.
Related
I'm trying to run a loop every second for 25 seconds basically.
for($i = 0; $i <= 25; $i += 1){
echo $i;
sleep(1)
}
The thing is it doesn't output until it's fully done, so after the loop continues 25 times. Is there a way to do this so it will output before each sleep? and not wait until the full loop is complete?
Thanks!
I just hashed through this same problem from a beginner perspective and came up with this bare-bones script which will do what you want.
<?PHP
ob_start();
$buffer = str_repeat(" ", 4096)."\r\n<span></span>\r\n";
for ($i=0; $i<25; $i++) {
echo $buffer.$i;
ob_flush();
flush();
sleep(1);
}
ob_end_flush();
?>
Questions that you may ask could be here (about \r\n) and here (about ob_flush()). Hope that helps you out.
What you're trying to achieve is incremental output to the browser from PHP.
Whether this is achievable can depend on your server and how you're invoking PHP.
PHP under FastCGI
You're probably a bit more likely to run into this kind of problem when PHP is running under FastCGI rather than as an Apache module, because the coupling between the server and the PHP processes is not as tightly coupled. FastCGI communication uses output buffering once the data has left the PHP processes, with the output sent to the browser only once the request is fully complete, or this buffer has filled up. On top of this, the PHP processes tend to be terminated after a certain amount of time, to avoid letting any one run for too long.
That said, a combination of ob_end_flush() (or ob_flush()) and flush() should still cause PHP to request that the downstream buffers are cleared, so this may still work. You may need to also investigate whether you need to length the time limit for PHP scripts.
PHP under mod_php
If you're using mod_php, you can write incrementally out to the browser. Use the flush() command to ensure that the PHP module will flush it instantly. If you don't have output buffering, or some Apache module such as mod_gzip, then it should go out instantly to the user's browser. What's more, you can keep your PHP script running as long as you like (with set_time_limit() in PHP), under the default configurations, though of course it will consume some memory.
You may run into trouble with some browsers which don't start rendering the page until a certain amount of a page is downloaded. Some versions of IE may wait for 1KB. I've found that Chrome can wait for more. A lot of people get around this by adding padding, such as a long comment 1 or 2 KB long at the top of the document.
Call flush will force PHP to push all of the output buffer to the client before proceeding.
for($i = 0; $i <= 25; $i += 1){
echo $i;
flush();
sleep(1);
}
EDIT:
After testing this on my lighttpd server I noticed that it buffered my outputs in blocks of 4096 characters, and I assume other browser might have similar buffering schemes. Also GZIP can prevent flush completely. Unfortunately there is no way to test that it's working due to the nature of HTTP.
Also another issue with this strategy is that it leaves that PHP proc blocked to other requests. This can cause requests to pile up.
How can I execute an external binary from within my php code, without the page waiting for a return value before it is sent?
Let me clarify. I have a web application that needs to do some fairly cpu intensive tasks, like a lot of file IO amongst other things. I want my user to be able to initiate their task from the GUI in their browser, but I then want my program to hand the task over to a separate file on my server so my user does not have to sit and wait for all the work to be done and risking timing out his/her connection etc.
Let me show you a simple example of what I have tried:
index.php
<?php
echo "This is a test run for an eternal program: <br><br>";
echo shell_exec("hello");
//ok so we can run executables from php script YAY .. but what about if they are long programs?
//If my script is waiting for a return value surely the connection will time out?
echo "<br><br>";
echo shell_exec("long_run"); //Waits for return value as expected. Further more it
//just does not do the file IO
?>
hello
#!/usr/bin/php5
<?php
echo "hello";
?>
long_run
#!/usr/bin/php5
<?php
$f = fopen("time.txt","a");
$i = 1;
while ($i < 10) {
sleep(2);
echo $i . "<br>";
fwrite($f, $i . " ");
$i++;
}
fclose($f);
?>
Note: I wrote the long_run example in PHP for consistency in reality my program is a binary and needs to interface with the PHP that my actual website is written with.
I have contemplated solving my problem by simply having the PHP save instructions to a file which my binary can scan on a regular basis and act upon. I think this kind of interface would be easy enough to implement it, but as someone with limited experience in PHP and indeed in web projects of this nature generally I would really like to get a better idea of the 'correct' approach to this problem. I'm sure there must be a standard way?
You can achieve it by running your command in background just append & to the command you wich to run.
It will run the binary in a seperate process without a return value
<?php
shell_exec("yourcommand &"); // It will run the 'yourcommand' in backgournd
.... // All further operations will be executed right after the shell exec without waiting for it return value
I am currently implementing a long polling function in Codeigniter and have come up a problem.
Lets say I have a normal PHP Controller:
function longpolling()
{
//PHP Timelimit infinite
set_time_limit(0);
while(true){
echo "test";
//Sleep 3 Seconds
sleep(3);
}
}
The page is just saying loading when called and does not return "test" instead you get 404 Error after a while.
What am I doing wrong?
Thank you!
You aren't doing anything 'wrong' it's just that php doesn't work the way you're expecting it to.
If you did it like this:
$i = 0;
while ($i < 10)
{
echo "Hi There!";
sleep(2);
$i++;
}
It will eventually output lots of Hi There, but not one at a time, rather it will all display at the end of the while loop.
You could even throw a flush() in there
$i = 0;
while ($i < 10)
{
echo "Hi There!";
flush();
sleep(2);
$i++;
}
And you still wont get anything until the very end.
Because your while(true) never ends you will never see any output, and I assume the browser timeout kicks in? Or the max_execution_time setting is reached?
Just popped into my head now: It might work if you wrote some data to a file in an infinite loop I have never tried it myself.
I've ran into issues like this myself. You'll have to look into flushing the output out as php and/or the webserver might be buffering the data until a certain threshold is met. I had a horrible time struggling with IIS over this, I think Apache is a lot easier to manage. Plus there's telling the webserver what to do as well. For apache, here's a snippet found on php.net:
I just had some problems with flush() and ob_flush(). What I did to
resolve this problem took me some time to figure out so I'd like to
share what I came up with.
The main problem is the php setting "output_buffering" which can be
set too large and will prevent your text from outputting. To change
this value you can either set it in php.ini or you can add the line
php_value output_buffering "0"
to your .htaccess file. It will not work with ini_set() since it is
PHP_INI_PERDIR.
This is combined with the flush() function used before sleep(). I also had to output over a number of characters before it started flushing properly:
public function longpolling()
{
echo str_repeat(" ", 1024); flush();
for( $i = 0; $i < 10; $i++) {
echo $i."<br/>";
flush();
sleep(1);
}
}
Also. I just tried this on my server and it wouldn't work until I added the php_value line to my htaccess file. Once I did, it worked as expected.
The page will keep loading until the PHP file execution has reached the end of the file. PHP doesn't work like C or C++. You make a request and when everything is done you get the output. Once the page is loaded no PHP is executing anymore.
And sleep() is just used to slow PHP down in some cases. In this case:
echo "Something";
sleep(30);
echo " else";
"Something" and " else" will be printed at the same moment while the total execution will take 30 seconds more.
I have some PHP code that is receiving and processing large images. I'd like to echo out some JavaScript at certain points while the image is being processed to update the DOM with jQuery. Here is some sample code, but it isn't working. It just waits the entire 5 seconds and then makes the alerts happen back to back. I want it to do the first alert immediately and then next alert after 5 seconds.
ob_start();
echo '<script type="text/javascript">alert(\'1...\');</script>';
ob_flush();
sleep(5);
ob_start();
echo '<script type="text/javascript">alert(\'2...\');</script>';
ob_flush();
Can anyone help?
Most browsers buffer content until a certain size is reached. Try making your script blocks longer by padding them with something.
Also: You should call flush, not just ob_flush, and make sure zlib compression is turned off.
I have some PHP code that is receiving and processing large images. I'd like to echo out some JavaScript at certain points while the image is being processed to update the DOM with jQuery.
This may be out-of-scope for what you have to get done, but I'd use AJAX for this. You can certainly get what you want to occur, but the approach isn't good in the long term.
Instead of submitting the whole page and waiting for it to come back at a crawl, use an AJAX request to upload the image and get the result. Then a timer on the client can issue separate AJAX "how far done are you?" requests. The two PHP instances would communicate via setting a "done" flag on the job entry in a database, etc.
While it makes the client-side stuff a bit more complex, it is much easier to handle user interaction (such as allowing the user to cancel a long-running job) and makes your PHP code a lot more tightly-focused.
Adding this to the top of the script will work:
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);
As far as I know, ob_implicit_flush(1) forces a flush on every output statement. So the other ob_start() and ob_flush() calls wouldn't be necessary. I don't know if that works for you.
<?php
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);
echo '<script type="text/javascript">alert(\'1...\');</script>';
sleep(5);
echo '<script type="text/javascript">alert(\'2...\');</script>';
?>
Following is working in FF4:
<?php
echo '<script type="text/javascript">alert("1");</script>';
flush();
sleep(5);
echo '<script type="text/javascript">alert("2");</script>';
?>
I implemented a chat "server" with something like that long ago. It was working.
This ob_* stuff isn't helpful for this.
I developed a facebook application in PHP. The problem is that it takes 2 minutes to display the result. This might confuse the user, who sees a blank canvas and leaves.
I just want to echo a statement that it is still processing.
I tried flush(); and ob_flush(); and ob_start(); but it is of no use.
Is there any other simpler alternative to address my specific problem?
I tried this, but it did not work as well.
ob_implicit_flush(true);
ob_end_flush();
for ($i=0; $i<5; $i++) {
echo $i.'<br>';
sleep(1);
}
EDIT:
The above code works perfectly fine with IE and other Browsers.
Only Chrome has this issue.
Convert it to an AJAX request, where you load a quick page which can have anything you want, and then loads in data from the slower page in the background.
flush() won't do what you want because it will return only part of the output and the client will tend to wait for the complete page.
Call flush(); as often as required.
Unfortunately, this might or might not make the browser feel happy to display your stuff. Even on IE, the result isn't predictable.