PHP script on host times out before finished - php

I spoke to the one.com support who said that their servers times out after 50 seconds.
The problem is, that it the script times out before it's finished. How can I make the script loop until it is finished?
This is my script:
<?php
//$id = $_GET['id'];
//$content = file_get_contents("http://www.boligsiden.dk/salg/$id");
$con=mysqli_connect("danico.dk.mysql","danico_dk","password hidden","danico_dk");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$result = mysqli_query($con,"SELECT * FROM elements");
$int = 0;
while($row = mysqli_fetch_array($result)) {
if($int < 500 && $row['link'] != "," && $row['link'] != "") {
$link = $row['link'];
$content = file_get_contents("http://www.boligsiden.dk/salg/$link");
preg_match('#"LatLng":{"Lat":(.*?),"Lng":#', $content, $match1);
$lat = $match1[1];
echo "<tr>";
echo "<td>" . $lat . "</td>";
echo "</tr>";
$int = $int + 1;
}
}
?>
The time limit can not be overwritten. The default value on one.com host is always 50 seconds.

Like other answers said, you should use set_time_limit( int $seconds_before_timeout ) but no need to put it in the loop:
set_time_limit(0) will cut off any timing out (except in safe mode).
You should add a caching system: once you've retried your data with file_get_contents(), you could save it to a local file (like /cache/boligsiden/salg/$link.dat) so next time, if the cache is recent (you decide what "recent" means), you'll get the local file instead of doing a long-time-consumming http request.
One solution:
<?php
$con=mysqli_connect("danico.dk.mysql","danico_dk","password hidden","danico_dk");
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
exit();
// else, script will continue...
// You could use a throw new Exception(...) instead
}
$result = mysqli_query($con,"SELECT * FROM elements WHERE `link`!=',' and `link`!='' LIMIT 500");
// Way easier to put the conditions on the query itself
// as your query is not reused anywhere else
set_time_limit(0); // No timeout
while($row = mysqli_fetch_array($result))
{
// No more if there since the query itself managed it faster
$link = $row['link'];
$content = file_get_contents("http://www.boligsiden.dk/salg/$link");
preg_match('#"LatLng":{"Lat":(.*?),"Lng":#', $content, $match1);
$lat = $match1[1];
echo "<tr><td>$lat</td></tr>";
}
set_time_limit(30); // From now on, script will timeout again
// That line can be removed depending on what's coming next
// Aka if you still want the script to not timeout, remove that line
?>
set_time_limit() : http://php.net/set_time_limit

You can change the time the script is allowed to run:
set_time_limit(300); // allow a maximum execution time of 5 minutes
Warning
This function has no effect when PHP is running in safe mode. There is no workaround other than turning off safe mode or changing the time limit in the php.ini.
Warning
Be careful with this option. Wrong usage can lead to scripts that never end. Putting in a definite time limit, at the top of your script is the safest way to go.

Related

PHP Batch upload array data

I am working with a client API (master API) that does not have a bulk feature.
I have taken data from 2 different API's (client API's) and merged it into one JSON file that is properly formatted. Checked in online JSON Validator.
The JSON File is 1100 records of merged customer data. Taking one record at a time, I have built a function that submits the data successfully to the master API.
I have now built a PHP script that loops through the JSON File and takes the row data (each client record) and submits it to the master API successfully. After about 90 rows, the PHP script times out.
I have set the following code on the page
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
set_time_limit(600);
#ob_end_clean();
And am buffering each update to return a JSON status code returned from the master API.
What should I be doing to get the PHP to not time out after about 100 records and keep updating the buffer response on the page.?
Thanks in advance.
Jason
I've taken a few of different approached to this kind of problem in the past. The only quick fix for this is if you can change the php.ini settings on the server to increase the timeout enough to allow your batch to complete. This is not a great solution, but it is a solution.
The next option (in ascending order of effort) is to set up a loop between the browser and your server where your browser makes a request, the server sends a portion of the records, then returns to the browser with a cursor indicating where the process left off, the browser makes another request to the server, sending the cursor back as a parameter, and this continues until the batch finishes. This is nice because you can display a progress bar to the user.
Finally, you could have an agent running on the server that waits for batch jobs to be submitted and runs them completely outside of the HTTP request lifecycle. So your browser makes a request to kick off a batch job, which results in some sort of record in a database that can keep track of the status of the job. The agent picks up the job and sets it to a pending state while it works, then sets the completion status when it finishes. You can set set something up that allows you to poll the server from the browser periodically so you can alert the user when the process finishes. Of you can just have the agent send an email report to the user when the batch completes. This is the most solid option with the least risk of something interrupting the processing, and it can leave an audit trail without any effort at all. But it's clearly more complicated to set up.
Thanks Rob.
Your response sent me in the right direction.
I sort of used your backend idea on the frontend. I just looped through 20 records at a time and then refreshed the page via javascript and started at 21 to 40 etc. Threw in a progress bar for fun as well.
Thanks for helping me get my head around the idea. Not the right way to do it, but my Python is just as bad as my PHP.
<?php
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
set_time_limit(600);
#ob_end_clean();
require("header.php");
require_once('nav.php');
function sync_systems($sfData){
$dataPost = $sfData;
$postID = $dataPost['ID'];
$epcall = update_profile($dataPost);
$epResult = json_decode($epcall, true);
if($epResult['status'] != 404){
$sfStatus = updateSFOpportunity($epResult['client_id'], $epResult['ep_id'] );
if($sfStatus == 1){
$datamsg = " Success! The sync was a success in both Salesforce and other system. OtherSystem Record " . $epResult['ep_id'] . " was created or updated.<br/>";
} else {
$datamsg = " Success! The sync was a success in other system, but failed in Salesforce<br/>";
}
echo json_encode(['code'=>200, 'msg'=>$datamsg]);
} else {
$datamsg = " Failure! The sync did not work.<br/>";
echo json_encode(['code'=>404, 'msg'=>$datamsg]);
} // end epResult
}
function sync_ep($sfData){
$dataPost = $sfData;
$postID = $dataPost['ID'];
$epcall = update_profile($dataPost);
$epResult = json_decode($epcall, true);
if($epResult['status'] != 404){
// $sfStatus = updateSFOpportunity($epResult['client_id'], $epResult['ep_id'] );
if($sfStatus == 1){
$datamsg = " Success! The sync was a success in both Salesforce and other system. Other System Record " . $epResult['ep_id'] . " was created or updated.<br/>";
} else {
$datamsg = " Success! The sync was a success in other system, but failed in Salesforce<br/>";
}
echo json_encode(['code'=>200, 'msg'=>$datamsg]);
} else {
$datamsg = " Failure! The sync did not work.<br/>";
echo json_encode(['code'=>404, 'msg'=>$datamsg]);
} // end epResult
}
$ju = "CustomerData20Fall.json";
//read json file from url in php
$readJSONFile = file_get_contents($ju);
//convert json to array in php
$jfile = json_decode($readJSONFile);
//print_r($jfile);
//convert json to array in php
$epSync = array();
$oldValue = 0;
$total = count($jfile );
?>
<!-- Progress bar holder -->
<div id="progress" style="width:500px;border:1px solid #ccc;"></div>
<!-- Progress information -->
<div id="information" style="width"></div>
<?php
if(isset($_REQUEST["NEXTVALUE"])){
$nextValue = $_REQUEST["NEXTVALUE"];
} else {
$nextValue = 1;
}
$refreshValue = $nextValue + 20;
$displaycounter = $nextValue;
$timeRemaining = 0;
$updatedRecords = 0;
foreach ($jfile as $key => $jsons) {
$newKey = $key;
if($oldValue != $newKey){
if($newKey >= $nextValue && $newKey < $refreshValue){
// echo "Updated: " . [$oldValue]['EPID'] . "<br/>";
// echo "<hr>" . $nextValue . " >= " . $newKey . " < " . $refreshValue;
print_r($epSync[$oldValue]);
$displaycounter = $newKey;
echo sync_systems($epSync[$oldValue]);
usleep(30000);
flush();
} else {
if($key == ($refreshValue)){
$theURL = "sf-ep-sync.php?NEXTVALUE=" . $refreshValue . "&RAND=" . rand();
// echo "<hr>" . $newKey . " = " . $refreshValue . " " . $theURL ."<br/>";
echo "<script>location.href = '" . $theURL . "';</script>";
exit;
}
}
$oldValue = $newKey;
$i = $nextValue + 1;
if(($i + 1) == $total ){
$percent = intval($i/$total * 100)."%";
$timeRemaining = 0;
} else {
$percent = intval($i/$total * 100)."%";
$timeRemaining = (($total - $displaycounter)/60);
}
usleep(30000);
echo '<script language="javascript">
document.getElementById("progress").innerHTML="<div style=\"width:'.$percent.';background-color:#ddd;\"> </div>";
document.getElementById("information").innerHTML="'.$displaycounter.' row(s) of '. $total . ' processed. About ' . round($timeRemaining, 2) . ' minutes remaining.";
</script>';
// This is for the buffer achieve the minimum size in order to flush data
echo str_repeat(' ',1024*64);
}
foreach($jsons as $key => $value) {
$epSync[$newKey][$key] = $value;
}
}
Thanks,
Jason

While loops for server-sent events are causing page to freeze

I am currently working on a chat that uses Server-Sent Events to receive the messages. However, I am running into a problem. The server-sent event never connects and stays at pending because the page doesn't load.
For example:
<?php
while(true) {
echo "data: This is the message.";
sleep(3);
ob_flush();
flush();
}
?>
I expect that every 3 seconds, "data: This is the message." will be outputted. Instead, the page just doesn't load. However, I need this behavior for server-sent events. Is there a way to fix this?
Edit:
Full Code:
<?php
session_start();
require "connect.php";
require "user.php";
session_write_close();
echo $data["number"];
header("Content-Type: text/event-stream\n\n");
header('Cache-Control: no-cache');
set_time_limit(1200);
$store = new StdClass(); // STORE LATEST MESSAGES TO COMPARE TO NEW ONES
$ms = 200; // REFRESH TIMING (in ms)
$go = true; // MESSAGE CHANGED
function formateNumber ($n) {
$areaCode = substr($n, 0, 3);
$part1 = substr($n, 3, 3);
$part2 = substr($n, 6, 4);
return "($areaCode) $part1-$part2";
}
function shorten ($str, $mLen, $elp) {
if (strlen($str) <= $mLen) {
return $str;
} else {
return rtrim(substr($str, 0, $mLen)) . $elp;
}
}
do {
$number = $data["number"];
$sidebarQ = "
SELECT *
FROM (
SELECT *
FROM messages
WHERE deleted NOT LIKE '%$number%'
AND (
`from`='$number'
OR
`to`='$number'
)
ORDER BY `timestamp` DESC
) as mess
GROUP BY `id`
ORDER BY `timestamp` DESC";
$query = $mysqli->query($sidebarQ);
if ($query->num_rows == 0) {
echo 'data: null' . $number;
echo "\n\n";
} else {
$qr = array();
while($row = $query->fetch_assoc()) {
$qr[] = $row;
}
foreach ($qr as $c) {
$id = $c["id"];
if (!isset($store->{$id})) {
$store->{$id} = $c["messageId"];
$go = true;
} else {
if ($store->{$id} != $c["messageId"]) {
$go = true;
$store->{$id} = $c["messageId"];
}
}
}
if($go == true) {
$el = $n = "";
foreach ($qr as $rows) {
$to = $rows["to"];
$id = $rows["id"];
$choose = $to == $number ? $rows["from"] : $to;
$nameQuery = $mysqli->query("SELECT `savedname` FROM `contacts` WHERE `friend`='$choose' AND `number`='$number'");
$nameGet = $nameQuery->fetch_assoc();
$hasName = $nameQuery->num_rows == 0 ? formateNumber($choose) : $nameGet["savedname"];
$new = $mysqli->query("SELECT `id` FROM `messages` WHERE `to`='$number' AND `tostatus`='0' AND `id`='$id'")->num_rows;
if ($new > 0) {
$n = "<span class='new'>" . $new . "</span>";
}
$side = "<span style='color:#222'>" . ($to == $number ? "To you:" : "From you:") . "</span>";
$el .= "<div class='messageBox sBox" . ($nameQuery->num_rows == 0 ? " noname" : "") . "' onclick=\"GLOBAL.load($id, $choose)\" data-id='$id'><name>$hasName</name><div>$side " . shorten($rows["message"], 25, "...") . "</div>$n</div>";
}
echo 'data: '. $el;
echo "\n\n";
$go = false;
}
}
echo " ";
ob_flush();
flush();
sleep(2);
} while(true);
?>
I would also like to note, that this infinite loop shouldn't be causing this to happen. This is just how SSE's are set up usually and it is even done so on the MDN website.
No doubt by now you have figured this out but on the offchance you have not I used code like the following on a couple of sse scripts and it worked like a charm. The code below is generic and does not feature your sql or recordset processing but the idea is sound(!?)
<?php
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('mysql.connect_timeout','7200');
ini_set('max_execution_time', '0');
date_default_timezone_set( 'Europe/London' );
ob_end_clean();
gc_enable();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Expose-Headers: X-Events');
if( !function_exists('sse_message') ){
function sse_message( $evtname='chat', $data=null, $retry=1000 ){
if( !is_null( $data ) ){
echo "event:".$evtname."\r\n";
echo "retry:".$retry."\r\n";
echo "data:" . json_encode( $data, JSON_FORCE_OBJECT|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS );
echo "\r\n\r\n";
}
}
}
$sleep=1;
$c=1;
$pdo=new dbpdo();/* wrapper class for PDO that simplifies using PDO */
while( true ){
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
break;
}
/* Infinite loop is running - perform actions you need */
/* Query database */
/*
$sql='select * from `table`';
$res=$pdo->query($sql);
*/
/* Process recordset from db */
/*
$payload=array();
foreach( $res as $rs ){
$payload[]=array('message'=>$rs->message);
}
*/
/* prepare sse message */
sse_message( 'chat', array('field'=>'blah blah blah','id'=>'XYZ','payload'=>$payload ) );
/* Send output */
if( #ob_get_level() > 0 ) for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#flush();
/* wait */
sleep( $sleep );
$c++;
if( $c % 1000 == 0 ){/* I used this whilst streaming twitter data to try to reduce memory leaks */
gc_collect_cycles();
$c=1;
}
}
if( #ob_get_level() > 0 ) {
for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#ob_end_clean();
}
?>
While this is not a direct answer as to the problem, try using this method to find the error.. Your not getting errors, but this should help you find them maybe?
Basically you want to have a simple PHP script which includes your main script, but this page enables errors... Example below..
index.php / Simple Error Includer
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
require "other.php";
?>
other.php / You Main Script
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
weqwe qweqeq
qweqweqweqwe
?>
If you create a setup like this, if you view index.php you will see the following error Parse error: syntax error, unexpected 'qweqeq' (T_STRING) in /var/www/html/syntax_errors/other.php on line 5 because it does not have an invalid syntax on the main page and allows any includes to be error checked..
But if you where to view other.php, you would simply get a white / blank page because its unable to validate the whole page/script.
I use this method in my projects, that way regardless of what i do in other.php or any linked php pages, i will see an error report for them.
Please understand the code before commenting
to say this disables error control means you did not bother to RTM
Fill the buffer
Another issue in the past that i remember was filling the buffer before it would output to the browser. So try something like this before your loop.
echo str_repeat("\n",4096); // Exceed the required browser threshold
for($i=0;$i<70;$i++) {
echo "something as normal";
flush();
sleep(1);
}
Examples at http://www.sitepoint.com/php-streaming-output-buffering-explained/
It seems like the sleep function is interfering with the output. Putting the sleep function AFTERWARDS did work:
<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}
As other people suggest, I would encourage to use AJAX instead of an infinite loop, but that was not your question.
One thing I have noticed here is sleep() function in combination with ob_start() and - THERE IS NO - ob_start() anywhere in the full code example, yet there is flush() and ob_flush() ..
What are you flushing anyway?
And why not simply ob_end_flush() ?
The thing is that sleep() than echo(), than sleep() again, than echo() again, etc, etc.. has no effect when output buffering is turned on. Sleep function works as expected when output buffering is not in play - in between. In fact, it might *(and it will) produce quite unexpected results, and those results won't be the one we want to see.
The following code works fine here, also using Mayhem his str_repeat function to add 4k of data (that is usually the minimum for a tcp packet to be flushed by php)
echo str_repeat(' ', 4096);
while(true)
{
echo "data: This is the message.";
flush();
sleep(3);
}
Instead of using loop try this code given below which is working(tested myself) fine as per your requirement
echo "data: This is the message.";
$url1="<your-page-name>.php";
header("Refresh: 5; URL=$url1");
what this will do is it will call itself every 5 seconds (in your case set it to 3 instead of 5) and echo the output.
I am going to take a chance and state the obvious,
you could query the server every 3 seconds, and let the client do the waiting...
This could be done easily with javascript
for example, try this code and name if file.php
<?php
$action='';
if (array_key_exists('action',$_GET))
{$action=$_GET['action'];}
if ($action=='poll')
{
echo "this message will be sent every 3 sec";
}
else
{
?><HTML><HEAD>
<SCRIPT SRC="http://code.jquery.com/jquery-2.1.3.min.js"></SCRIPT>
<SCRIPT>
function doPoll()
{
$('#response').append($.get("file.php?action=poll"));
setTimeout(doPoll, 3000);
}
doPoll();
</SCRIPT>
</HEAD><BODY><DIV id="response"></DIV></BODY></HTML><?php
}
Could it be as simple as the script timing out?
Eventually PHP scripts self terminate if they run for too long. The solution for when you don't want this to happen is to keep resetting the time out.
So a simple addition might be all you need:
<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>
Of course, that might not be it but my gut instinct is that this is the problem.
http://php.net/manual/en/function.set-time-limit.php
UPDATE: I noticed in the comments that you are using some free hosting. If they are running PHP in safe mode then you cannot reset your timeout.
I had the same issue and finally found the easy and quick solution on kevin choppin's blog:
Session Locks
First and foremost, if you're using sessions for whatever reason you will need to make them read-only on the stream. If they're writable, this will lock them everywhere else, so any page loads will hang while the server waits for them to become writable again. This is easily fixed by calling; session_write_close();
I suggest using if() statement instead of using while. And in your case your condition is always true, hence it is in infinite loop.

can't get stream_set_timeout to work in foreach array loop

I'm not so familiar with PHP but have been able to get a script running that will check if a certain port is open for a number of local network computers.
the downside is that some pc's are offline and I can't get the timeout of the fsockopen function lower then 1 second (0.001 second would be enough)
I've found the *stream_set_timeout* function but can't seem to get it to work.
I'm pretty sure it's just in the wrong place, and hope someone can point out where it should go.
now I get this error:
Warning: stream_set_timeout() expects parameter 1 to be resource,
boolean given
snippet:
$timeout = 1000;
foreach ($farm as $pc){
$check = #fsockopen($pc, $port);
stream_set_timeout($check,0,$timeout);
if (is_resource($check))
{
echo $pc . " online";
fclose($check);
}
else
{
echo $pc . " offline";
}
}
current solution:
foreach ($farm as $pc){
$check = #fsockopen($pc,$port,$errCode,$errStr,0.001);
if (is_resource($check))
{
echo $pc . " online";
fclose($check);
}
else
{
echo $pc . " offline";
}
}

How to mimic a command line run with arguements inside a PHP script?

How can you mimic a command line run of a script with arguements inside a PHP script? Or is that not possible?
In other words, let's say you have the following script:
#!/usr/bin/php
<?php
require "../src/php/whatsprot.class.php";
function fgets_u($pStdn) {
$pArr = array($pStdn);
if (false === ($num_changed_streams = stream_select($pArr, $write = NULL, $except = NULL, 0))) {
print("\$ 001 Socket Error : UNABLE TO WATCH STDIN.\n");
return FALSE;
} elseif ($num_changed_streams > 0) {
return trim(fgets($pStdn, 1024));
}
}
$nickname = "WhatsAPI Test";
$sender = ""; // Mobile number with country code (but without + or 00)
$imei = ""; // MAC Address for iOS IMEI for other platform (Android/etc)
$countrycode = substr($sender, 0, 2);
$phonenumber=substr($sender, 2);
if ($argc < 2) {
echo "USAGE: ".$_SERVER['argv'][0]." [-l] [-s <phone> <message>] [-i <phone>]\n";
echo "\tphone: full number including country code, without '+' or '00'\n";
echo "\t-s: send message\n";
echo "\t-l: listen for new messages\n";
echo "\t-i: interactive conversation with <phone>\n";
exit(1);
}
$dst=$_SERVER['argv'][2];
$msg = "";
for ($i=3; $i<$argc; $i++) {
$msg .= $_SERVER['argv'][$i]." ";
}
echo "[] Logging in as '$nickname' ($sender)\n";
$wa = new WhatsProt($sender, $imei, $nickname, true);
$url = "https://r.whatsapp.net/v1/exist.php?cc=".$countrycode."&in=".$phonenumber."&udid=".$wa->encryptPassword();
$content = file_get_contents($url);
if(stristr($content,'status="ok"') === false){
echo "Wrong Password\n";
exit(0);
}
$wa->Connect();
$wa->Login();
if ($_SERVER['argv'][1] == "-i") {
echo "\n[] Interactive conversation with $dst:\n";
stream_set_timeout(STDIN,1);
while(TRUE) {
$wa->PollMessages();
$buff = $wa->GetMessages();
if(!empty($buff)){
print_r($buff);
}
$line = fgets_u(STDIN);
if ($line != "") {
if (strrchr($line, " ")) {
// needs PHP >= 5.3.0
$command = trim(strstr($line, ' ', TRUE));
} else {
$command = $line;
}
switch ($command) {
case "/query":
$dst = trim(strstr($line, ' ', FALSE));
echo "[] Interactive conversation with $dst:\n";
break;
case "/accountinfo":
echo "[] Account Info: ";
$wa->accountInfo();
break;
case "/lastseen":
echo "[] Request last seen $dst: ";
$wa->RequestLastSeen("$dst");
break;
default:
echo "[] Send message to $dst: $line\n";
$wa->Message(time()."-1", $dst , $line);
break;
}
}
}
exit(0);
}
if ($_SERVER['argv'][1] == "-l") {
echo "\n[] Listen mode:\n";
while (TRUE) {
$wa->PollMessages();
$data = $wa->GetMessages();
if(!empty($data)) print_r($data);
sleep(1);
}
exit(0);
}
echo "\n[] Request last seen $dst: ";
$wa->RequestLastSeen($dst);
echo "\n[] Send message to $dst: $msg\n";
$wa->Message(time()."-1", $dst , $msg);
echo "\n";
?>
To run this script, you are meant to go to the Command Line, down to the directory the file is in, and then type in something like php -s "whatsapp.php" "Number" "Message".
But what if I wanted to bypass the Command Line altogether and do that directly inside the script so that I can run it at any time from my Web Server, how would I do that?
First off, you should be using getopt.
In PHP it supports both short and long formats.
Usage demos are documented at the page I've linked to. In your case, I suspect you'll have difficulty detecting whether a <message> was included as your -s tag's second parameter. It will probably be easier to make the message a parameter for its own option.
$options = getopt("ls:m:i:");
if (isset($options["s"] && !isset($options["m"])) {
die("-s needs -m");
}
As for running things from a web server ... well, you pass variables to a command line PHP script using getopt() and $argv, but you pass variables from a web server using $_GET and $_POST. If you can figure out a sensible way to map $_GET variables your command line options, you should be good to go.
Note that a variety of other considerations exist when taking a command line script and running it through a web server. Permission and security go hand in hand, usually as inverse functions of each other. That is, if you open up permissions so that it's allowed to do what it needs, you may expose or even create vulnerabilities on your server. I don't recommend you do this unless you'll more experienced, or you don't mind if things break or get attacked by script kiddies out to 0wn your server.
You're looking for backticks, see
http://php.net/manual/en/language.operators.execution.php
Or you can use shell_exec()
http://www.php.net/manual/en/function.shell-exec.php

PHP Sockets, any way to speed this up

I'm writing a small script to see if certain ports on certain devices are in use or are open. My code is:
for($c=1;$c<=16;$c++){
echo "<tr><td>Pod " . $c . "</td>";
for ($d=5000;$d<=5010;$d++){
$tmp=fsockopen("10.0.0." . $c,$d,$erstr, $errno, 1);
if($tmp){
echo "<td class='o'>OPEN</td>";
fclose($tmp);
}
else{
echo "<td class='u'>IN USE</td>";
}
}
ob_flush();
flush();
echo "</tr>\n";
}
echo "</table>";
Obviously this is a lot of connections, and currently it's taking about ten seconds to run. I was wondering if there's any way I can get this to be a little bit faster? Thanks for the help!
One way to speed this up tremendously is to get asynchronous. Right now if one of the hosts is slow, it will slow down the entire pipeline because you're doing one right after another. PHP doesn't really have an event-based AIO (select), or even threading. It does, however, have fork in a linux environment. The following example hasn't been tested, but is a general idea for how to do asynchronous IO in php:
<?php
$childrenArr = array();
$childrenLeft = array();
for($c=1;$c<=16;$c++){
for ($d=5000;$d<=5010;$d++){
$pid = pcntl_fork();
if ($pid == -1) {
die("Could not fork");
} else if ($pid) {
$childrenArr[$pid] = array($c, $d);
$childrenLeft[$pid] = 1;
} else {
$tmp=fsockopen("10.0.0." . $c,$d,$erstr, $errno, 1);
exit(($tmp) ? 1 : 0);
}
}
}
$results = array();
while (count($childrenLeft) > 0) {
$oldPid = pcntl_waitpid(-1, $status, WNOHANG);
if (pcntl_wifexited($status )) {
unset($childrenLeft[$oldPid]);
list($c, $d) = $childrenArr[$oldPid];
$results[$c . "_" . $d] = pcntl_wexitstatus($status);
}
usleep(100);
}
for($c=1;$c<=16;$c++){
echo "<tr><td>Pod " . $c . "</td>";
for ($d=5000;$d<=5010;$d++){
if ($results[$c . "_" . $d]) {
echo "<td class='o'>OPEN</td>";
}
else {
echo "<td class='u'>IN USE</td>";
}
}
ob_flush();
flush();
echo "</tr>\n";
}
echo "</table>";
If a given port is not listening/accepting you will suffer all the TCP timeouts on the SYN packet re-transmissions sent out during the three-way handshake. This is the design of the TCP - we can't change that.
One thing I can recommend is switching from streams to the socket functions and trying non-blocking connect - create your 160 sockets, set them to non-blocking, initiate all connections, wait on them in select with a decreasing timeout, flag the ones that return readable. Do that in a loop until you spent, say, a whole second. Now you get a list of open TCP host/port pairs, and a list of likely closed ones, and you spent a fixed amount of time.
Disclaimer: I never do networking in PHP, so this might be totally useless.

Categories