9


I tried to output a simple ping command on a web page in a similar way( and same time) as it is displaying in terminal, using shell_exec; But it is displaying only after the complete execution, while I needed it to display whenever it is displaying on terminal,

My code is

<?php
$i= shell_exec("ping -c 4 google.com");
echo "<pre> $i <pre>";
?>

It is waiting for a while and the dumping the whole thing on a single shot.. can PHP recognize the outputting of each line and display it on the web page

EDIT
I tried this also

    <?php
    $proc = popen("ping -c 4 google.com", 'r');
    echo '<pre>';
    while (!feof($proc)) {
        echo fread($proc, 4096);
    }
    echo '</pre>';
    ?>

But still I gets the same result..

EDIT
When I tried to execute this PHP code in terminal , ( php test.php) it is working properly in the same way it gives when we directly do ping on server. but in web page it is still the same.

luchaninov
  • 6,792
  • 6
  • 60
  • 75
user2723949
  • 289
  • 2
  • 4
  • 10
  • 1
    shell_exec doesn't return until the command completes. it would not be possible for it to fire off the command and then let execution continue on to the echo command. you probably popen() and then fread in a loop to grab lines as they come available. – Marc B Nov 27 '13 at 17:00
  • @MarcBSo that should be the way to do it ! Could you please elaborate? – user2723949 Nov 27 '13 at 17:08
  • @MarcB I have edited my question, I tried the above piece of code with popen and fread, but it is also giving me the same output? Anything I have done wrong? – user2723949 Nov 28 '13 at 10:55
  • That might not (only) be a problem of getting the info step-by-step, but also of outputting it to the browser – look into flushing the output buffer. This is quite a complex subject matter, as multiple factors play a role here – output buffering by PHP, possible also by the web server … and last but not least whether or not the browser wants to display partial info as soon as he receives it. – CBroe Nov 28 '13 at 11:10
  • @CBroe As I mentioned in my edit, I was able to execute this php program through command line with the desired output, But as you mentioned , it is causing problems when coming to web pages. is there any way to solve this?? – user2723949 Nov 28 '13 at 11:21
  • I mentioned a few places where issues might occur already, so look into them. – CBroe Nov 28 '13 at 11:23
  • @CBroe I added the line flush(); after the line echo fread($proc,4096);.. but result is the same. Shall I do something on php.ini file? – user2723949 Nov 28 '13 at 12:08
  • See http://stackoverflow.com/questions/1281140/run-process-with-realtime-output-in-php – Chris Wheeler Nov 28 '13 at 13:31
  • Where are you trying to use `popen()`? I hope not via web-server? If yes, then remember - __HTTP is synchronous__ and while web-server will not form response entirely, it will not send it to client. So case with `popen()` will work __only in CLI mode__ – Alma Do Nov 28 '13 at 14:23

7 Answers7

7

Uhm, strange behavior from the web browser. I'm using this code:

<?php
ob_end_flush();
ini_set("output_buffering", "0");
ob_implicit_flush(true);

function pingtest()
{
    $proc = popen("ping -c 5 google.com", 'r');
    while (!feof($proc))
    {
        echo "[".date("i:s")."] ".fread($proc, 4096);
    }
}

?>
<!DOCTYPE html>
<html>
<body>
  <pre>
Immediate output: 
<?php
pingtest();
?>
  </pre>
</body>
</html>

In the browser the content appears after all bytes has been received. But, the content is actually delivered on time, do this test:

wget -O - -q "http://localhost/ping.php"

You will see that the response is delivered by php & apache2 on time.

I'm using this kind of execution on long task for a while, but using a more complex solution:

  • an html file for interface
  • a php file that run the long task
  • Connect html interface with php long execution using EventSource object (available on html5)

interface (test.html)

<!DOCTYPE html>
<html>
<head>
    <title>Simple EventSource example</title>
</head>
<body>
    <script type="text/javascript">
    function eventsourcetest() {
        var ta = document.getElementById('output');
        var source = new EventSource('test.php');
        source.addEventListener('message', function(e) {
            if (e.data !== '') {
               ta.value += e.data + '\n';
            }
        }, false);
        source.addEventListener('error', function(e) {
            source.close();
        }, false);
    }
    </script>
    <p>Output:<br/><textarea id="output" style="width: 80%; height: 25em;"></textarea></p>
    <p><button type="button" onclick="eventsourcetest();">ping google.com</button>
</html>

Server Side Component (test.php)

<?php
ob_end_flush();
ini_set("output_buffering", "0");
ob_implicit_flush(true);
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

function echoEvent($datatext) {
    echo "data: ".implode("\ndata: ", explode("\n", $datatext))."\n\n";
}

echoEvent("Start!");
$proc = popen("ping -c 5 google.com", 'r');
while (!feof($proc)) {
    echoEvent(fread($proc, 4096));
}
echoEvent("Finish!");

Put both files in one place on a webserver and enter test.html, I think this is what you are looking for from the beginning.

Carlos C Soto
  • 1,025
  • 9
  • 11
1

Use output buffering and flush. You might also want to look into the Symfony 2 process component.

max
  • 96,212
  • 14
  • 104
  • 165
1

Its not a PHP matter, or rather its a shared matter between php and the browser.

In PHP: Make sure output buffering is off, you can do this by running ob_end_clean() before outputting anything.

As this SO post suggests you have to either pad the very first string outputted to 512 bytes OR specify a charset encoding via http header. The padding solution may very well be the easiest way around this, its basically this: echo(str_pad("Live Ping Test!",512)); and then start echoing the result of your fread.

Community
  • 1
  • 1
dorphalsig
  • 689
  • 1
  • 10
  • 27
1

You might want to try using flush() to flush the output as and when its ready, and use passthru() to execute the command.

Gaurav Phapale
  • 979
  • 1
  • 8
  • 21
  • flush() is key -- but depending on your webserver you may need to add bogus characters to make the flush() actually flush. – virmaior Dec 15 '13 at 14:34
1

Carlos C Soto is right, you have to use javascript. EventSource is the way to go. Basically, it's javascript code that will constantly call a url

You can write the output of ping in a file, and write a php script that will read the last line, then call this script with eventsource.

Search "Server Sent Events" on the web to find more examples

freezeeedos
  • 103
  • 4
0

if can resolve using apache execution user. if your root user is diffrent and server user different then it will not allow to execute command line command.

Nitin Rana
  • 21
  • 1
0

I tested Carlos's answer on my side...

and I HAD to add flush();ob_flush(); for it to work properly (both needed flush AND ob_flush)

like this

<?php

$proc = popen("ping -c 5 google.com", 'r');
while (!feof($proc))
{
    echo "[".date("i:s")."] ".fread($proc, 4096).'<br>';flush();ob_flush();
}

?>