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.
Related
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.
I got big problem, becouse i tested everything to make it works, but`s not - yet :)
i got simple for loop and there is a star, end, flush inside, but still my browser load all output at the and of loop, and i took for this question simple example:
<?php
if (ob_get_level() == 0) ob_start();
for ($i = 0; $i<10; $i++){
echo "<br> Line to show.";
echo str_pad('',4096)."\n";
ob_flush();
flush();
sleep(2);
}
echo "Done.";
ob_end_flush();
?>
a i`vd setup all about outpuuting_bufforing, zlib, gzib, and other alls. Exacly in htacces, script, file, even in php.ini, apache. I got dedicaded server so can configure what i need. Can some1 tell me what more can i try?
Ofc there is no error in any log file.
Thanks for advice !
The comments in the official PHP documentation for ob_flush() mention, that most browsers have an all-or-nothing approach to loading content. Therefore the browser will not show anything until the whole page is loaded.
See http://php.net/manual/de/function.ob-flush.php#109699
This means that flushing output to the browser will not work for you.
The alternative would be to start the initial request via AJAX and then use a second request to provide information about the current progess.
I have a quite long loop running in a function , but does not finish all the eventual iterations and stops without giving any errors :
function my_function(){
foreach (range(100,999) as $art_id){
$current++;//see bottom flush functions...
outputProgress($current, $art_id );//see bottom flush functions...
// do a lot of stuff on remote URL...
// including download images ,
// scraping HTMl etc ..
}
}
I am using some output progress with flush to track the progress
function outputProgress($current, $total) {
// echo "<span style='background:red;font-size:1.5em;color:#fff;'>" . round($current / $total * 100) . "% </span>";
echo "<span style='background:red;font-size:1.5em;color:#fff;'>" . $current .'/'. $total . "% </span>";
myFlush();
sleep(1);
}
and
function myFlush() {
echo(str_repeat(' ', 256));
if (#ob_get_contents()) {
#ob_end_flush();
}
flush();
}
(never mind the percentage calculation , it is now disabled and just shows the ID of iteration)
I have noticed that most of the time I am executing the loop,
It will just STOP after 20-25 iterations . sometimes as little as 10.
My first suspects were the time limit , and max_execution time, so I added :
set_time_limit(99999);
ini_set('max_execution_time', 99999);
function my_function(){
foreach (range(410,499) as $art_id){ // done 500-600
set_time_limit(99999);
ini_set('max_execution_time', 99999);
// do a lot of stuff on remote URL...
// including download images ,
// scraping HTMl etc ..
}
}
As you can see, I have added those both INSIDE and OUTSIDE the function itself, just in case .
But it does not help much , and the loop still stops .
My next suspect was the Memory limit, so I added :
ini_set('memory_limit','128M');
and since I am working on wp, I also tried
define('WP_MEMORY_LIMIT', '128M');
but to no avail. The scipt still stops after little iterations .
What are the other possible causes for this behavior , and the
possible remedies ?
Mind you - the script does not give any errors, it just stops at a certain loop.
EDIT I
I have pasted the script HERE
. it is actually a slightly modified scrap_slashdot() function from the simplehtmldom lib included examples.
It is modified to insert wordpress posts while also downloading images and attaching them.
EDIT II
Using #Allendar comment echo ini_get('memory_limit'); seems like it works and it is set to 128M..
I've speeded up usleep(50000); to test. This code take a fragment of a second to complete on PHP 5.4 and causes no memory leakage:
ini_set('memory_limit', '32M'); // Force back to default X/W/L/M/AMP
function my_function(){
$current = 0;
foreach (range(100,999) as $art_id){
$current++;
outputProgress($current, $art_id );
}
}
function outputProgress($current, $total) {
echo "<span style='background:red;font-size:1.5em;color:#fff;'>" . $current .'/'. $total . "% </span>";
myFlush();
usleep(50000);
}
function myFlush() {
echo(str_repeat(' ', 256));
if (#ob_get_contents()) {
#ob_end_flush();
}
flush();
}
my_function();
echo memory_get_usage();
I've added $current = 0; to cancel out a warning given by Xdebug.
The memory usage outputs only 282304 bytes (about 275,69 kiloBytes).
It might be the case that the 1-second wait each cycle might cause the script to abort on execution time.
ini_set('max_execution_time', 0);
.. will fix this, but is not recommended ;)
If you still find the script to stop suddenly, it really must be in the part where you have your comments, that you write there is code. That code might be heavy enough on the payload of the PHP deamon to abort. Besides that there are also hosts (if the script is online) that prevent you from setting ini-values and maybe even kill the PHP process if it "zombies" around to long.
First of all this is not "long" script, i have been working with arrays - actually 16 array with all having more than 650 indexes (= 14 X 650 = 9100 indexes, nvm if i am wrong in calculation). And it loads in fraction of seconds so it seems having no problem. I am sure you are doing something seriously wrong. It works fine (if i know correctly) [(tested here online, on php 5)]], even without ini_set(); (disabled by website) and the memoery usage was 63072 (in bytes ~ 63kbs ~ 0.063mb > 128mb)
And wanted to tell you that from where do you set $current? Your my_function() has not parameters and i would also recommend you to turn on error reporting by
error_reporting(E_ALL); ini_set('display_errors', '1');
There should be problem with online compiler you are using, try one which i used or download apache server, you can also try some free hosts.
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 have a php script that uses cURL and takes about 10-15 minutes to execute. What it does, it parses about 1000 pages looking for specific matches and throughout the script I have diagnostic messages echo'ed out, like "Going to the next page", "Found a match", "Error loading page" ... The way it works now (and the way that it's normal) is it executes for like 10 minutes and only then spits out all my custom messages.
I would like to be able to display those messages as they happen, not when the script is done executing. I was thinking something like AJAX would do it, but am not sure how it would work. Any tips are greatly appreciated. Thanks.
So, this is a old post but I found a solution for this. As I also have to make the same thing, output when the script is still running. Not any answer from here helped.
First of all, I am using Win32 server(production) and XAMPP as local for tests. This example is just a proof of concept and can be modified as you please.
<?php
ob_implicit_flush(true);
for($i=1; $i<=10; $i++){
echo "$i ...<br>";
for($k = 0; $k < 40000; $k++) echo ' ';
sleep(1);
}
?>
So, we open output buffer as implicit. Then we make a demo loop to count from 1 to 10 and display the values as they are been processed. Second loop will fill in the browsers buffer. And finally to check if everything is working well we make a sleep for 1 second. Otherwise the script will run too fast and we could not know if we achieved the goal.
Hope this helps !
You could create a staging table.
The PHP script could, instead of echo'ing the message, store them into a database table (possibly memory table for performance).
You could then periodically poll a seperate PHP script using ajax, which would query the table, and return any new messages to the client.
Use flush to immediately send output to the browser, by flushing the output buffer.
echo "foo";
flush();
echo "bar";
flush();
Actually you're looking for something like flush and ob_flush, however bear in mind that there are a lot of factors that can prevent your output from being flush'd as it happens.
From the flush documentation you'll get:
Several servers, especially on Win32, will still buffer the output from your script until it terminates before transmitting the results to the browser.
Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.
I'm using the #ob_flush() after every echo. In this example PHP_EOL creates a new line after $string
function output($string){
echo $string.PHP_EOL;
#ob_flush();
}
Basically, have your script write HTML output to a temporary log file. Then use ajax to periodically update the end-user's browser with the temporary log file. jQuery will make quick work of this.
Ajax is the only guaranteed way to get it to work on all browsers. Here is a quote from PHP's flush page.
flush() may not be able to override
the buffering scheme of your web
server and it has no effect on any
client-side buffering in the browser.
It also doesn't affect PHP's userspace
output buffering mechanism. This means
you will have to call both ob_flush()
and flush() to flush the ob output
buffers if you are using those.
Sounds to be like you have output buffering turned on.
Calling ob_end_flush() will print what's currently in the buffer, and turn off the buffer for the rest of the script execution.
You can use the flush() function to send all the content of the buffer to the client. http://php.net/manual/fr/function.flush.php
You could use both flush and ob_flush, reminding to set the content type header:
<?php
header( 'Content-type: text/html; charset=utf-8' );
for( $i = 0 ; $i < 10 ; $i++ ){
echo $i . '<br>';
flush();
ob_flush();
sleep(1);
}
Source: dermeister note in php.net ob_flush page.
Tested on Firefox 42.0 and Chrome 46.0