Link to home
Start Free TrialLog in
Avatar of Vipin Kumar
Vipin KumarFlag for India

asked on

How to display result of a PHP script one by one?

Hi,

I have a created a script which pings the devices and show the result. But there are around 100+ devices in the list and once I start the script it pings all devices and then show the result, which approximately takes 15 to 20 min.

I would be thankful if someone could tell me a way to display the output of the script one by one i.e. ping a device then show its result and then move to the next device and so on. But the result of all devices should be there on the page.

I have already tried ob_flush(); ob_start(); and flush(); but I could not get the expected result.

Thanks in advance.
Avatar of Gregory Miller
Gregory Miller
Flag of United States of America image

You are on the right track... Output buffering is the way to get it done. Can you share your code that was not working?
Avatar of kaufmed
You might want to read Ray_Paseur's article:  Understanding Client/Server Protocols and Web Applications. This will give you an understanding of why what you want to do isn't necessarily straight-forward.

That said, you can achieve what you want with web sockets, but as I understand they are not yet supported by all browsers and webservers. The only other option would be to set up some AJAX code in your pages that polls the server for updates on the status of the long-running task. You could store the results of your pings in the user's session, and then return what's in session on each AJAX request. I'll have to defer to the others for an example as I don't have immediate access to a PHP machine at the moment.
Avatar of Vipin Kumar

ASKER

I have attached two files which contains the script to ping the devices. Would be thankful if you could let me know where to put the output buffering.
ping.inc.php
ping.php
Ping.php
<?php 
ob_start();
require_once('header.inc.php');
ini_set('max_execution_time', 300);
$time_start = microtime(true)."<br>";
function pingDomain($domain){
	exec("ping -n 4 -a $domain", $output, $status);
	/*if (strpos($output[2],'Reply from') !== false) {
    	$status='Alive';
	} else {
		$status='Dead';
	}*/
	$searchword = 'Reply from';
	$matches = array_filter($output, function($var) use ($searchword) { return preg_match("/\b$searchword\b/i", $var); });
	//print_r($matches);
	if(!empty($matches)){
		$status='Alive';
	} else {
		$status='Dead';
	}
	return $status;
}
$link = mysql_connect('localhost','root','P@ssw0rd');
$select = mysql_select_db('deviceping');
$result = mysql_query("SELECT * FROM devicelist ORDER BY name");
require_once('ping.inc.php');
require_once('footer.inc.php');
ob_end_flush();
?>

Open in new window


ping.inc.php
<h1>Device Status</h1>
<br>
<a class="refresh" href="<?php echo $_SERVER['REQUEST_URI']; ?>"><img src="refresh.png">Refresh</a>
<table id="ticketTable" width="800" border="0" cellspacing="0" cellpadding="0">
    <caption>Showing All Devices</caption>
    <thead>
        <tr>
            <th class="centered"width="70" nowrap>Sl No.</th>
            <th width="250" nowrap>Device Name</th>
            <th width="100" nowrap>IP Address</th>
            <th width="100" nowrap>Type</th>
            <th width="150" nowrap>Location</th>
            <th width="100" nowrap>Status</th>
        </tr>
    </thead>
    <tbody>
            <?php
			$i=1;
			while($row = mysql_fetch_array($result))
			{
				echo "<tr>";
				echo "<td class='centered'>" . $i . "</td>";
				echo "<td>" . $row['name'] . "</td>";
				echo "<td>" . $row['ip_address'] . "</td>";
				echo "<td>" . $row['type'] . "</td>";
				echo "<td>" . $row['location'] . "</td>";
				$status = pingDomain($row['ip_address']);
        		/*if ($status != "Dead"){
        			echo "<p style='color:green;'>Alive</p>";
        		} else {
        			echo "<p style='color:red;'>Dead</p>";
        		}*/
        		if ($status != "Dead"){
        			echo "<td><input size='4' style='background-color:green;'></td>";
        		} else {
        			echo "<td><input size='4' style='background-color:red;'></td>";
        		}
				echo "</tr>";
				$i++;
			}
			$time_end = microtime(true);

			//dividing with 60 will give the execution time in minutes other wise seconds
			$execution_time = ($time_end - $time_start)/60;
			//execution time of the script
			echo '<b>Total Execution Time:</b> '.$execution_time.' Mins <br>';
			echo "<b>The Ping Test was last run at:</b>  ". date("j F l, Y G:i:s");
ob_flush();
			?>
    </tbody>
</table>
<p style="padding-left:400px;"><input type="button" value="Back" onClick='window.location.href="index.php"'></p>

Open in new window

Output buffering as suggested is not working. If there is any other way using AJAX for ex. can you guide me how it can be done or a link which has the code and requires little bit of modification.
Output buffering is intended to cause the server to buffer (collect and store) the script output from the time it is started until (usually) the end of the script, so it may not be your friend here.  What you really want is output flush() but that by itself may not be enough to accomplish the task of getting output before the script ends.  You want to read the man page about flush() carefully to understand what else may be involved in trying to get a script to send output from intermediate operations.

This may also be somewhat useful:
http://www.php.net/manual/en/function.ob-implicit-flush.php

See also this script.  By padding the output string to a length that is long enough to overrun the server and browser buffers, I am able (on my system) to get the intermediate outputs.
http://www.laprbass.com/RAY_force_flush.php

<?php // RAY_force_flush.php
error_reporting(E_ALL);
date_default_timezone_set('America/New_York');


// SIMULATE A LONG-RUNNING JOB WITH INTERMITTENT BROWSER OUTPUT
// FORCE FLUSH THE BROWSER OUTPUT BEFORE THE END OF THE PAGE
// PAD THE OUTPUT STRING TO MAKE IT LONG SO THE SERVER SENDS IT


function my_echo($str, $len=512, $pad=' ')
{
    // PAD THE STRING WITH INVISIBLE WHITESPACE OR SIMILAR
    $str = str_pad($str, strlen($str) + $len, $pad);
    echo $str;
    echo PHP_EOL;
    flush();
}


// TEST THE URL ARGUMENT FOR "b=y" AND OPTIONALLY START THE OUTPUT BUFFER
$b = (!empty($_GET["b"])) ? $_GET["b"] : NULL;
if ($b == 'y') ob_start();


// RUN THE TEST MESSAGES, WAITING BETWEEN THE MESSAGES
my_echo("<br/>Hello #1 " . date('c'));
sleep(1);
my_echo("<br/>Hello #2 " . date('c'));
sleep(2);
my_echo("<br/>Hello #3 " . date('c'));
sleep(3);
my_echo("<br/>Hello #4 " . date('c'));
sleep(4);
my_echo("<br/>Hello #5 " . date('c'));
sleep(1);
my_echo("<br/>Hello #6 " . date('c'));

Open in new window

HTH, ~Ray
Afterthought... This may or may not be helpful; it shows how a site monitor works.
http://www.laprbass.com/RAY_monitor_websites.php

<?php // RAY_monitor_websites.php
error_reporting(E_ALL);


// DEMONSTRATE HOW WEB SITE MONITORS WORK


// COMMONLY USED PORT NUMBERS
// SEE: http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml (SLOW TO LOAD)
// SEE: http://browntips.com/cpanel-and-whm-port-numbers/
$ports["HTTP"]     =    80;
$ports["FTP"]      =    21;
$ports["SSH"]      =    22;
$ports["DNS"]      =    53;
$ports["MYSQL"]    =  3306;
$ports["CPANEL"]   =  2082;
$ports["CPANEL-S"] =  2083;
$ports["WHM"]      =  2086;
$ports["WHM-S"]    =  2087;
$ports["POP3"]     =   110;
$ports["SMTP"]     =    25;
$ports["BOGUS"]    = 11111; // THIS IS EXPECTED TO FAIL


// AN ARRAY OF WEB SITES TO MONITOR (SELF-MONITORING OR CHOOSE OTHER URLS)
$urls[] = $_SERVER["HTTP_HOST"];
$urls[] = 'www.twitter.com';
$urls[] = 'www.example.com';
$urls[] = 'www.knownBogusUrlNumber427.org';


// GET THE RESULTS AS SOON AS POSSIBLE
ob_implicit_flush();


// ITERATE OVER THE LIST OF URLs
foreach ($urls as $url)
{
    // THE RESULTS SET
    $errno = $errstr = array();

    // THE TIME TO ALLOW FOR CONNECTION
    $timex = 1;

    // TEST EACH OF THE PORTS - SEE http://php.net/manual/en/function.fsockopen.php
    foreach ($ports as $port_name => $port_number)
    {
        $fp
        = @fsockopen // @MAKE ERRORS SILENT
        ( $url
        , $port_number
        , $errno[$port_name]
        , $errstr[$port_name]
        , $timex
        )
        ;
        // COMPLETE FAILURE SIGNAL IS FALSE + errno=0
        if ( (!$fp) && ($errno[$port_name] == 0) )
        {
            $errno[$port_name] = 'FAIL';
        }
    }

    // REPORT WHAT HAPPENED
    echo "<pre>"  . PHP_EOL;
    echo "URL: $url TIME: $timex";
    foreach ($errno as $port_name => $error_number)
    {
        if (!$error_number)
        {
            echo PHP_EOL . "OK: $port_name $ports[$port_name]";
        }
        else
        {
            echo PHP_EOL . "ERROR $error_number: $port_name $errstr[$port_name] ON PORT $ports[$port_name]";
        }
    }
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Gregory Miller
Gregory Miller
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Technoweed,

Your solution works, but there is small issue. All the formatting and everything is gone. The output is being displayed but with out formatting. If you could help me how to get the formatting as well.

Thanks in advance.
can that be a separate question?
i misplaced the function call. the issue has been solved now. thanks for your support.
For my own edification, using the selected solution if I were to call this function from another script--one that generates a page--then the whole of my page will not be rendered until this function finishes, correct? I mean, I'll get back everything before the function call, then my function results, one at a time, and then the remainder of my page, yes?
Yes, that is exactly how it seems to work. Alternatively, you could use the output buffering ob_start(), etc. to do much of the same. I wound up going this direction quite a ways before it became a reality that the issue I was chasing was the exec() function not liking to be run within the buffering. I switched to the system() function and left the other work as is.
@kaufmed®:

I've plowed this field before.  If you want to see how it works with direct script access, click here.
http://www.laprbass.com/RAY_force_flush.php

The example in this response is what you'll be seeing.
https://www.experts-exchange.com/questions/28328557/How-to-display-result-of-a-PHP-script-one-by-one.html?anchorAnswerId=39749777#a39749777

If you call the "force flush" script from another PHP script, your method of access may interact with server-side buffering to determine whether you will see the responses in a timely (piecemeal) manner.  PHP file_get_contents() will wait until the script ends, then return the responses all at once.  Using fopen() and fread() I am able to get the responses as each one is created.
http://www.laprbass.com/RAY_temp_kaufmed.php

<?php // RAY_temp_kaufmed.php


// SEE http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28328557.html#a39750038


// A SCRIPT THAT WILL EMIT DATA IN TIME-DELAY BURSTS
$url = 'http://www.laprbass.com/RAY_force_flush.php';
$fpr = fopen($url, 'r');

// READ AND ECHO THE DATA AS IT IS RETURNED
while (!feof($fpr))
{
    $dat = fread($fpr, 8192);
    echo $dat;
}

Open in new window

It's not clear to me that the system() command has anything to do with this.  The behavior I observe when I use Firefox to visit the link in the accepted solution is a very long pause, followed by a large burst of data, suggesting that there is some buffering going on.  The behavior I see in Chrome is more piecemeal and I think more in line with what the Author wanted.  So it may be as simple as changing browsers ;-)
OK, what I was thinking of was along the lines of:

Main Page
<?php
    session_start();            // Start the session so...
    $unique_id = session_id();  // ...we can get a unique id for this user
    session_write_close();      // Close the session file because we're not using it
    $status_filename = 'user/' . $unique_id . '_status';  // Create a distinct filename for this user

    exec('php pinger.php ' . $status_filename . ' > /dev/null &');  // Redirect to prevent hang
?>
<!DOCTYPE html>
<html>
    <head>
        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    </head>
    <div>This is some content before</div>
    <ul id="list-status"></ul>
    <div>This is some content before</div>
    <script>
        var interval = setInterval(function() {
            $.get("status.php", function (data) {
                var items = data.split('\n');
                var bound = items.length;
                
                if (items[bound - 1] == 'done') {
                    clearInterval(interval);
                }
                
                $('#list-status').empty();
                
                for (var i = 0; i < bound; i++) {
                    $('<li>' + items[i] + '</li>').appendTo('#list-status');
                }
            });
        }, 1000 * 2);  // Every 2 seconds
    </script>
</html>

Open in new window


Ping Script
<?php
    if (count($argv) > 1) {
        $status_filename = $argv[1];
        
        if (file_exists($status_filename)) {
            unlink($status_filename);
        }
        
        $fh = fopen($status_filename, 'a+');

        // Simulate pings
        for ($i = 0; $i < 5; $i++) {
            fwrite($fh, 'ping' . $i . ' completed' . "\n");
            fflush($fh);
            sleep(3);
        }
        
        fwrite($fh, 'done');
        fclose($fh);
    }

Open in new window


Status Script
<?php

    session_start();
    $unique_id = session_id();
    session_write_close();
    $status_filename = 'user/' . $unique_id . '_status';
    
    if (file_exists($status_filename)) {
        $response = file_get_contents($status_filename);
    }
    else {
        $response = 'done';
    }
    
    echo $response;

Open in new window


So once the page loads you see:

User generated image
...while it's running you see:

User generated image
...and when it's done you see:

User generated image
All of the regular page content is visible during the entire process. It seems to me that doesn't happen in the other suggestions. But PHP is not my forte, so I'll gladly bow out if I am just missing the point  = )