我不是PHP的专家。我有一个函数,它使用EXEC来运行WINRS,然后在远程服务器上运行命令。问题是这个函数放在一个循环中,调用getservicestatus函数几十次。有时,WINRS命令可能会卡住或花费的时间超过预期,导致PHP脚本超时并抛出500错误。
暂时我已经降低了PHP中的设置超时值并在IIS中创建了一个自定义500页面,如果引用页面等于脚本名称,则重新加载页面(否则,抛出错误)。但这很麻烦。显然它并不适用于每次调用函数时它是全局的。因此,它只会避免页面因HTTP 500错误而停止。
我真正想做的是在功能本身上设置5秒的超时时间。即使在stackoverflow上,我已经搜索了很多并且无法找到答案。是的,有类似的问题,但我找不到任何与我的功能相关的问题。也许有一种方法可以在执行命令时执行此操作,例如exec()的替代方法?我不知道。理想情况下,我希望功能在5秒后超时,并将$ servicestate返回为0。
代码被评论解释我的意大利面乱。我很抱歉你必须看到它......
function getservicestatus($servername, $servicename, $username, $password)
{
//define start so that if an invalid result is reached the function can be restarted using goto.
start:
//Define command to use to get service status.
$command = 'winrs /r:' . $servername . ' /u:' . $username . ' /p:' . $password . ' sc query ' . $servicename . ' 2>&1';
exec($command, $output);
//Defines the server status as $servicestate which is stored in the fourth part of the command array.
//Then the string "STATE" and any number is stripped from $servicestate. This will leave only the status of the service (e.g. RUNNING or STOPPED).
$servicestate = $output[3];
$strremove = array('/STATE/','/:/','/[0-9]+/','/\s+/');
$servicestate = preg_replace($strremove, '', $servicestate);
//Define an invalid output. Sometimes the array is invalid. Catch this issue and restart the function for valid output.
//Typically this can be caught when the string "SERVICE_NAME" is found in $output[3].
$badservicestate = "SERVICE_NAME" . $servicename;
if($servicestate == $badservicestate) {
goto start;
}
//Service status (e.g. Running, Stopped Disabled) is returned as $servicestate.
return $servicestate;
}
答案 0 :(得分:0)
最直接的解决方案,因为您正在调用外部进程,并且实际上需要在脚本中输出其输出,所以在exec
和非阻塞I / O方面重写proc_open
:
function exec_timeout($cmd, $timeout, &$output = '') {
$fdSpec = [
0 => ['file', '/dev/null', 'r'], //nothing to send to child process
1 => ['pipe', 'w'], //child process's stdout
2 => ['file', '/dev/null', 'a'], //don't care about child process stderr
];
$pipes = [];
$proc = proc_open($cmd, $fdSpec, $pipes);
stream_set_blocking($pipes[1], false);
$stop = time() + $timeout;
while(1) {
$in = [$pipes[1]];
$out = [];
$err = [];
stream_select($in, $out, $err, min(1, $stop - time()));
if($in) {
while(!feof($in[0])) {
$output .= stream_get_contents($in[0]);
break;
}
if(feof($in[0])) {
break;
}
} else if($stop <= time()) {
break;
}
}
fclose($pipes[1]); //close process's stdout, since we're done with it
$status = proc_get_status($proc);
if($status['running']) {
proc_terminate($proc); //terminate, since close will block until the process exits itself
return -1;
} else {
proc_close($proc);
return $status['exitcode'];
}
}
$returnValue = exec_timeout('YOUR COMMAND HERE', $timeout, $output);
此代码:
proc_open
打开子进程。我们只为子项stdout
指定管道,因为我们没有任何内容可以发送给它,也不关心它的stderr
输出。如果你这样做,你将不得不相应地调整以下代码。stream_select()
上的循环,会阻止一段时间,直到$timeout
集($stop - time()
)。var_dump()
输入缓冲区的内容。这不会阻止,因为我们在管道上有stream_set_blocking($pipe[1], false)
。您可能希望将内容保存到变量中(附加而不是覆盖它),而不是打印出来。$output
中。返回进程的退出代码,或者在超时的情况下返回-1
。