windows PHP 使用系统调用为脚本设置超时,set_time_limit 不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2603912/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP set timeout for script with system call, set_time_limit not working
提问by reflexiv
I have a command-line PHP script that runs a wget request using each member of an array with foreach. This wget request can sometimes take a long time so I want to be able to set a timeout for killing the script if it goes past 15 seconds for example. I have PHP safemode disabled and tried set_time_limit(15) early in the script, however it continues indefinitely. Update:Thanks to Dor for pointing out this is because set_time_limit() does not respect system() calls.
我有一个命令行 PHP 脚本,它使用带有 foreach 的数组的每个成员运行 wget 请求。这个 wget 请求有时可能需要很长时间,所以我希望能够设置超时以杀死脚本,例如,如果它超过 15 秒。我在脚本的早期禁用了 PHP 安全模式并尝试了 set_time_limit(15),但是它无限期地继续。更新:感谢 Dor 指出这是因为 set_time_limit() 不尊重 system() 调用。
So I was trying to find other ways to kill the script after 15 seconds of execution. However, I'm not sure if it's possible to check the time a script has been running while it's in the middle of a wget request at the same time (a do while loop did not work). Maybe fork a process with a timer and set it to kill the parent after a set amount of time?
所以我试图找到其他方法在执行 15 秒后终止脚本。但是,我不确定是否可以检查脚本在 wget 请求中同时运行的时间(do while 循环不起作用)。也许用计时器分叉一个进程并设置它在设定的时间后杀死父进程?
Thanks for any tips!
感谢您提供任何提示!
Update:Below is my relevant code. $url is passed from the command-line and is an array of multiple URLs (sorry for not posting this initially):
更新:以下是我的相关代码。$url 是从命令行传递的,是一个包含多个 URL 的数组(抱歉最初没有发布这个):
foreach( $url as $key => $value){
$wget = "wget -r -H -nd -l 999 $value";
system($wget);
}
回答by Josh
Try using the wget command line argument --timeout
in addition to set_time_limit()
.
尝试使用wget命令行参数--timeout
除了set_time_limit()
。
Keep in mind set_time_limit(15)
restarts the timeout counter from zeroso don't call it inside a loop (for your purpose)
请记住set_time_limit(15)
从零重新启动超时计数器,因此不要在循环中调用它(出于您的目的)
from man wget
:
来自man wget
:
--timeout=seconds
Set the network timeout to seconds seconds. This is equivalent to specifying --dns-timeout, --connect-timeout, and --read-timeout, all at the same time.
When interacting with the network, Wget can check for timeout and abort the operation if it takes too long. This prevents anomalies like hanging reads and infinite connects. The only timeout enabled by default is a 900-second read timeout. Setting a timeout to 0 disables it altogether. Unless you know what you are doing, it is best not to change the default time-out settings.
All timeout-related options accept decimal values, as well as subsecond values. For example, 0.1 seconds is a legal (though unwise) choice of timeout. Subsecond timeouts are useful for checking server response times or for testing network latency.
--超时=秒
将网络超时设置为秒秒。这相当于同时指定--dns-timeout、--connect-timeout 和--read-timeout。
在与网络交互时,Wget 可以检查超时并在时间过长时中止操作。这可以防止诸如挂起读取和无限连接之类的异常情况。默认情况下唯一启用的超时是 900 秒的读取超时。将超时设置为 0 将完全禁用它。除非您知道自己在做什么,否则最好不要更改默认超时设置。
所有与超时相关的选项都接受十进制值以及亚秒值。例如,0.1 秒是合法(虽然不明智)的超时选择。亚秒级超时对于检查服务器响应时间或测试网络延迟很有用。
EDIT:OK. I see what you're doing now. What you should probably do is use proc_openinstead of system
, and use the time()
function to check the time, calling proc_terminateif wget tskes too long.
编辑:好的。我知道你现在在做什么。您可能应该做的是使用proc_open而不是system
,并使用该time()
函数检查时间,如果 wget tskes 太长,则调用proc_terminate。
回答by ddrown
You can use a combination of "--timeout" and time(). Start off by figuring out how much time you have total, and lower the --timeout as your script runs.
您可以结合使用“--timeout”和 time()。首先弄清楚您总共有多少时间,并在脚本运行时降低 --timeout 。
For example:
例如:
$endtime = time()+15;
foreach( $url as $key => $value){
$timeleft = $endtime - time();
if($timeleft > 0) {
$wget = "wget -t 1 --timeout $timeleft $otherwgetflags $value";
print "running $wget<br>";
system($wget);
} else {
print("timed out!");
exit(0);
}
}
Note: if you don't use -t, wget will try 20 times, each waiting --timeout seconds.
注意:如果不使用 -t,wget 将尝试 20 次,每次等待 --timeout 秒。
Here's some example code using proc_open/proc_terminate (@Josh's suggestion):
下面是一些使用 proc_open/proc_terminate 的示例代码(@Josh 的建议):
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$pipes = array();
$endtime = time()+15;
foreach( $url as $key => $value){
$wget = "wget $otherwgetflags $value";
print "running $wget\n";
$process = proc_open($wget, $descriptorspec, $pipes);
if (is_resource($process)) {
do {
$timeleft = $endtime - time();
$read = array($pipes[1]);
stream_select($read, $write = NULL, $exeptions = NULL, $timeleft, NULL);
if(!empty($read)) {
$stdout = fread($pipes[1], 8192);
print("wget said--$stdout--\n");
}
} while(!feof($pipes[1]) && $timeleft > 0);
if($timeleft <= 0) {
print("timed out\n");
proc_terminate($process);
exit(0);
}
} else {
print("proc_open failed\n");
}
}
回答by Dor
You can direct the output of the wget program to a file, which then returns immediately.
Also see:
您可以将 wget 程序的输出定向到一个文件,然后该文件立即返回。
另见:
Note: The set_time_limit() function and the configuration directive max_execution_time only affect the execution time of the script itself. Any time spent on activity that happens outside the execution of the script such as system calls using system(), stream operations, database queries, etc. is not included when determining the maximum time that the script has been running. This is not true on Windows where the measured time is real.
注意:set_time_limit() 函数和配置指令 max_execution_time 只影响脚本本身的执行时间。在确定脚本运行的最长时间时,不包括在脚本执行之外发生的活动(例如使用 system() 的系统调用、流操作、数据库查询等)上花费的任何时间。在测量时间是真实的 Windows 上,情况并非如此。
Source: set_time_limit()@ php.net
来源:set_time_limit()@ php.net
回答by Edvard Rejthar
I think the far best way is to handle the killing in the command itself, at least on Linux (Ubuntu 17.10). No other means worked for me... proc_terminate
from another answer worked but no output would be given.
我认为最好的方法是在命令本身中处理杀戮,至少在 Linux (Ubuntu 17.10) 上是这样。没有其他方法对我有用......proc_terminate
从另一个答案起作用但不会给出任何输出。
In the example below, I adding a magic string that checks if your process is still running. If running longer than $sec
, it kills the process immediately, leaving a note 'Timeout!'.
在下面的示例中,我添加了一个魔术字符串来检查您的进程是否仍在运行。如果运行时间超过$sec
,它会立即终止进程,并留下一个注释“超时!”。
Like this, I am sure the command won't exceed timeout and I receive all the stdout/stderr I need.
像这样,我确信该命令不会超过超时,并且我收到了我需要的所有 stdout/stderr。
$wget = "wget -r -H -nd -l 999 $value"; // your command
$sec = 5; // timeout in seconds
$cmd = $wget . " & ii=0 && while [ \"2\" -eq \"`ps -p $! | wc -l \" ]; do ii=$((ii+1)); if [ $ii -gt ".($sec)."0 ]; then echo 'Timeout!';kill $!; break;fi; sleep 0.1; done"
system($cmd); // system call