set_time_limit不影响PHP-CLI

时间:2010-04-14 04:59:37

标签: php

如何解决不影响PHP-CLI的set_time_limit?

#!/usr/bin/php -q 
<?php
set_time_limit(2);
sleep(5); // actually, exec() call that takes > 2 seconds
echo "it didn't work again";

6 个答案:

答案 0 :(得分:17)

max_execution_time限制,即set_time_limit设置的限制,计算(至少在Linux上) PHP进程在工作时花费的时间。< / p>

引用set_time_limit()的手册页:

  

注意: set_time_limit()功能和配置   仅限指令max_execution_time   影响的执行时间   脚本本身
任何时间花在   在外面发生的活动   执行脚本,如系统   使用system()来调用流   操作,数据库查询等   确定时不包括在内   脚本的最长时间   运行。
这不是真的   测量时间为的Windows   实。

当您使用sleep()时,您的PHP脚本无法正常工作:它只是在等待......所以您等待的5秒钟不会被max_execution_time限制考虑在内。< / p>

答案 1 :(得分:9)

此处的解决方案是使用pcntl_fork()。我担心它只是POSIX。 PHP需要使用--enable-pcntl而不是--disable-posix进行编译。您的代码应如下所示:

<?php

function done($signo)
{
    // You can do any on-success clean-up here.
    die('Success!');
}

$pid = pcntl_fork();
if (-1 == $pid) {
    die('Failed! Unable to fork.');
} elseif ($pid > 0) {
    pcntl_signal(SIGALRM, 'done');
    sleep($timeout);
    posix_kill($pid, SIGKILL);
    // You can do any on-failure clean-up here.
    die('Failed! Process timed out and was killed.');
} else {
    // You can perform whatever time-limited operation you want here.
    exec($cmd);
    posix_kill(posix_getppid(), SIGALRM);
}

?>

基本上它的作用是派一个运行else { ... }块的新进程。在(成功)结束时,我们将警报发送回正在运行elseif ($pid > 0) { ... }块的父进程。该块具有警报信号(done()回调)的注册信号处理程序,该处理程序成功终止。在收到之前,父进程会休眠。当超时sleep()完成后,它会向(可能是挂起的)子进程发送SIGKILL

答案 2 :(得分:5)

你能尝试通过exec运行你的php并使用“timeout”命令吗? (http://packages.ubuntu.com/lucid/timeout)

用法:超时[-signal]时间命令。

答案 3 :(得分:4)

非常hackish,但由于我经常使用cli脚本,我承认可耻地过去打了一个类似的hack。需要2个脚本。

您的第一个cron脚本(挂起的脚本)将其starttime和processid记录在一个平面文件中 - &gt;函数getmypid将是你的朋友。每隔(x)分钟从cron运行的第二个脚本检查flatfile,杀死已经通过指定执行时间的任何内容,清除flatfile,甚至可以根据需要登录另一个文件。

祝你好运;)

<强>更新

记住了这个问题并回来发布了这个问题。在通过Sebastian Bergmann的github帐户搜索时,我偶然发现php-invoker。用作者的话来说:

  
    

用于在超时时调用PHP callables的实用程序类。

  

虽然目前的答案非常适合基于Linux的非便携式使用,但如果需要跨平台解决方案,这个解决方案应该是Windows兼容的,适用于在windoze上运行的那些可怜的灵魂。 Bergmann先生是一名PHP神,我完全保证了剧本的质量。

答案 4 :(得分:4)

我觉得脚本在一个循环中挂起。为什么不简单地执行脚本开始$STARTUP_TIME=time(),然后在脚本需要退出时继续检查循环?

答案 5 :(得分:2)

编辑1

我在PHP手册中注意到了这条评论:

  

请注意,在Linux下,   睡眠时间被忽略,但在   Windows,它算作执行时间。

据我所知,sleep是作为系统调用实现的,因此被PHP as described in the manual忽略。

编辑2

  

运行exec()代替   睡觉()那里。和一些exec()的东西   无限期挂起。我也是   寻找从内部杀死它   exec(在linux shell上),就像   exec(kill_after15secs myscript.sh),   但我似乎无法找到那个

我现在看到了实际的问题。假设您在Lunix / Unix环境中工作,您可以围绕这些方面设计解决方案:

<?php $pid = system( 'sh test.sh >test-out.txt 2>&1 & echo $!' ); ?>

猜猜是什么,这捕获了您开始的流程的进程ID。您可以将其与时间戳一起记录到数据库或文本文件中。在另一个运行的cron脚本中,例如,每5分钟运行一次,检索5分钟前创建的所有进程ID并检查它们是否仍在运行:

<?php $status = system( 'ps ' . $pid ); ?>

如果进程仍在运行,则会得到两行输出:

 PID TTY STAT TIME COMMAND
2044 ?   S    0:29 sh test.sh

如果是这样,请按以下方式终止:

<?php system( 'kill ' . $pid ); ?>

我写了一篇关于creating background processes in PHP的文章(然后追捕他们)。所有的例子都是从那里复制过来的。

编辑3

所有点连接在一起:

<?php
    $pid = system( 'sh test.sh >test-out.txt 2>&1 & echo $!' );
    $timer = 300;
    while( --$timer ) {
        sleep(1);
        $status = system( 'ps ' . $pid );
        $status = explode( "\n", $status, 2 ); // I am not sure, please experiment a bit here
        if ( count( $status ) == 1 || trim( $status[ 1 ] ) == '' ) {
            die( 'spawned process ended successfully' );
        }
    }
    system( 'kill ' . $pid );
    die( 'spawned process was terminated' );
?>