Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/41.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在PHP中检测代码块的超时_Php_Timeout - Fatal编程技术网

在PHP中检测代码块的超时

在PHP中检测代码块的超时,php,timeout,Php,Timeout,如果PHP中的代码块花费的时间太长,有没有一种方法可以中止它?也许是这样的: //Set the max time to 2 seconds $time = new TimeOut(2); $time->startTime(); sleep(3) $time->endTime(); if ($time->timeExpired()){ echo 'This function took too long to execute and was aborted.'; }

如果PHP中的代码块花费的时间太长,有没有一种方法可以中止它?也许是这样的:

//Set the max time to 2 seconds
$time = new TimeOut(2);
$time->startTime();

sleep(3)

$time->endTime();
if ($time->timeExpired()){
    echo 'This function took too long to execute and was aborted.';
} 
它不必与上面的完全相同,但是是否有任何本机PHP函数或类可以执行类似的操作

编辑:Ben Lee用
pcnt\u fork
给出的答案将是完美的解决方案,只是它不适用于Windows。对于适用于Windows和Linux但不需要外部库的PHP,还有其他方法可以实现这一点吗


编辑2:XzKto的解决方案在某些情况下有效,但并不一致,而且无论我怎么尝试,我似乎都无法捕捉到异常。用例检测单元测试的超时。如果测试超时,我想终止它,然后继续下一个测试。

如果您未处于安全模式,该怎么办。

在大约两分钟内完成了这项操作,我忘记调用
$time->startTime()


我没有真正理解您的
endTime()
调用所做的,因此我改为使用
checkTime()
,它除了更新内部值之外没有任何实际用途
timeExpired()
自动调用它,因为如果您忘记调用
checkTime()
并且它使用的是旧时间,那么它肯定会发出臭味。

您可以通过分叉进程,然后使用父进程监视子进程来完成此操作。是一种分叉进程的方法,因此内存中有两个几乎相同的程序并行运行。唯一的区别是,在一个进程中,父进程,
pcntl_fork
返回一个正整数,该整数对应于子进程的进程id。在另一个过程中,子级,
pcntl\u fork
返回0

下面是一个例子:

$pid = pcntl_fork();
if ($pid == 0) {
    // this is the child process
} else {
    // this is the parent process, and we know the child process id is in $pid
}
这是基本结构。下一步是添加进程过期。您的工作将在子进程中运行,父进程将只负责监视子进程并为其计时。但是为了让一个进程(父进程)杀死另一个进程(子进程),需要有一个信号。信号是进程通信的方式,表示“应该立即结束”的信号是SIGKILL。您可以使用发送此信号。因此,家长应该等待2秒钟,然后杀死孩子,就像这样:

$pid = pcntl_fork();
if ($pid == 0) {
    // this is the child process
    // run your potentially time-consuming method
} else {
    // this is the parent process, and we know the child process id is in $pid
    sleep(2); // wait 2 seconds
    posix_kill($pid, SIGKILL); // then kill the child
}

除了forking之外,如果在一个命令(例如sleep())上编写暂停脚本,则无法真正做到这一点,但在特殊情况下,有很多解决方法:例如,如果在DB query上编程暂停,则可以使用异步查询,如果您的程序在某些外部执行时暂停,程序将打开。不幸的是,它们都是不同的,因此没有通用的解决方案

如果脚本等待一个长循环/多行代码,则可以执行如下脏技巧:

declare(ticks=1);

class Timouter {

    private static $start_time = false,
    $timeout;

    public static function start($timeout) {
        self::$start_time = microtime(true);
        self::$timeout = (float) $timeout;
        register_tick_function(array('Timouter', 'tick'));
    }

    public static function end() {
        unregister_tick_function(array('Timouter', 'tick'));
    }

    public static function tick() {
        if ((microtime(true) - self::$start_time) > self::$timeout)
            throw new Exception;
    }

}

//Main code
try {
    //Start timeout
    Timouter::start(3);

    //Some long code to execute that you want to set timeout for.
    while (1);
} catch (Exception $e) {
    Timouter::end();
    echo "Timeouted!";
}
  <?php
  try {
     //Start timeout
     Timeouter::limit(2, 'You code is heavy. Sorry.');

     //Some long code to execute that you want to set timeout for.
     declare(ticks=1) {
        foreach (range(1, 100000) as $x) {
           Timeouter::step(); // Not always necessary
           echo $x . "-";
        }
     }

     Timeouter::end();
  } catch (Exception $e) {
     Timeouter::end();
     echo $e->getMessage(); // 'You code is heavy. Sorry.'
  }

但我觉得它不是很好。如果您指定确切的情况,我认为我们可以更好地帮助您。

这是一个老问题,到目前为止可能已经解决了很多次,但是对于寻求解决此问题的简单方法的人来说,现在有一个库:。

如果执行时间超过限制,您可以使用declare函数

下面是一个如何使用的代码示例

define("MAX_EXECUTION_TIME", 2); # seconds

$timeline = time() + MAX_EXECUTION_TIME;

function check_timeout()
{
    if( time() < $GLOBALS['timeline'] ) return;
    # timeout reached:
    print "Timeout!".PHP_EOL;
    exit;
}

register_tick_function("check_timeout");
$data = "";

declare( ticks=1 ){
    # here the process that might require long execution time
    sleep(5); // Comment this line to see this data text
    $data = "Long process result".PHP_EOL;
}

# Ok, process completed, output the result:
print $data;
define(“最大执行时间”,2);#秒
$timeline=time()+最大执行时间;
函数检查超时()
{
如果(time()<$GLOBALS['timeline'])返回;
#已达到超时:
打印“超时!”.PHP\u EOL;
出口
}
寄存器勾选功能(“检查超时”);
$data=“”;
声明(ticks=1){
#这里介绍了可能需要较长执行时间的流程
sleep(5);//注释此行以查看此数据文本
$data=“长过程结果”。PHP\u EOL;
}
#好,流程完成,输出结果:
打印$数据;
使用此代码,您将看到超时消息。
如果您想在declare块内获得长流程结果,您可以删除sleep(5)行或增加脚本开头声明的最大执行时间,也可以使用第二个脚本,其中包含暂停代码,该代码通过设置超时的curl调用执行。另一个显而易见的解决方案是修复暂停的原因。

以下是我的方法。感谢他人的回答:

<?php
class Timeouter
{
   private static $start_time = FALSE, $timeout;

   /**
    * @param   integer $seconds Time in seconds
    * @param null      $error_msg
    */
   public static function limit($seconds, $error_msg = NULL)
   : void
   {
      self::$start_time = microtime(TRUE);
      self::$timeout    = (float) $seconds;
      register_tick_function([ self::class, 'tick' ], $error_msg);
   }

   public static function end()
   : void
   {
      unregister_tick_function([ self::class, 'tick' ]);
   }

   public static function tick($error)
   : void
   {
      if ((microtime(TRUE) - self::$start_time) > self::$timeout) {
         throw new \RuntimeException($error ?? 'You code took too much time.');
      }
   }

   public static function step()
   : void
   {
      usleep(1);
   }
}

我用php编写了一个脚本,使用pcntl_fork和lockfile控制超时后执行kill的外部调用的执行


#/usr/bin/env-php

你误解了海报的要求。他想限制方法的执行时间。他的例子只是伪代码。你把它变成一个类是没有帮助的。“不必完全像上面那样”-意味着它可以。它的功能与OP要求的一样,他可以在方法块中运行
timeExpired
检查。使用我的代码,他确实可以对方法的执行进行时间限制,甚至可以很好地控制执行应该停止的位置。也许我误解了他,但听起来他是在特别要求一个不需要调用过期检查的解决方案(他可能无法完全访问他需要限制时间的方法的内部).这就是他的
endTime
所暗示的…也许我们应该让OP澄清一下?我把他的问题解释为要求能够执行该检查的东西,因此他的伪代码的检查被执行。如果他不想要,他为什么会要求它?谢谢你的帮助,Cyclone。为了澄清,它只是伪代码。@Ben Lee拥有它正是我想要的。+1-这将是一个很好的解决方案,因为它非常简单,但不幸的是,我需要一个在Windows和Linux上都能工作的解决方案(请参阅注释:)。知道其他选项吗?有没有办法在特定代码块运行完成后取消时间限制?我只需要一段代码的超时,而不是整个脚本,我希望能够检测到超时,而不会出现任何其他故障。+1-这看起来可能行得通。但是,我有
  <?php
  try {
     //Start timeout
     Timeouter::limit(2, 'You code is heavy. Sorry.');

     //Some long code to execute that you want to set timeout for.
     declare(ticks=1) {
        foreach (range(1, 100000) as $x) {
           Timeouter::step(); // Not always necessary
           echo $x . "-";
        }
     }

     Timeouter::end();
  } catch (Exception $e) {
     Timeouter::end();
     echo $e->getMessage(); // 'You code is heavy. Sorry.'
  }
#!/usr/bin/env php
<?php

if(count($argv)<4){
    print "\n\n\n";
    print "./fork.php PATH \"COMMAND\" TIMEOUT\n"; // TIMEOUT IN SECS
    print "Example:\n";
    print "./fork.php /root/ \"php run.php\" 20";
    print "\n\n\n";
    die;
}

$PATH = $argv[1];
$LOCKFILE = $argv[1].$argv[2].".lock";
$TIMEOUT = (int)$argv[3];
$RUN = $argv[2];

chdir($PATH);


$fp = fopen($LOCKFILE,"w"); 
    if (!flock($fp, LOCK_EX | LOCK_NB)) {
            print "Already Running\n";
            exit();
    }

$tasks = [
  "kill",
  "run",
];

function killChilds($pid,$signal) { 
    exec("ps -ef| awk '\$3 == '$pid' { print  \$2 }'", $output, $ret); 
    if($ret) return 'you need ps, grep, and awk'; 
    while(list(,$t) = each($output)) { 
            if ( $t != $pid && $t != posix_getpid()) { 
                    posix_kill($t, $signal);
            } 
    }    
} 

$pidmaster = getmypid();
print "Add PID: ".(string)$pidmaster." MASTER\n";

foreach ($tasks as $task) {
    $pid = pcntl_fork();

    $pidslave = posix_getpid();
    if($pidslave != $pidmaster){
        print "Add PID: ".(string)$pidslave." ".strtoupper($task)."\n";
    }

  if ($pid == -1) {
    exit("Error forking...\n");
  }
  else if ($pid == 0) {
    execute_task($task);        
        exit();
  }
}

while(pcntl_waitpid(0, $status) != -1);
echo "Do stuff after all parallel execution is complete.\n";
unlink($LOCKFILE);


function execute_task($task_id) {
    global $pidmaster;
    global $TIMEOUT;
    global $RUN;

    if($task_id=='kill'){
        print("SET TIMEOUT = ". (string)$TIMEOUT."\n");
        sleep($TIMEOUT);
        print("FINISHED BY TIMEOUT: ". (string)$TIMEOUT."\n");
        killChilds($pidmaster,SIGTERM);

        die;
  }elseif($task_id=='run'){
        ###############################################
        ### START EXECUTION CODE OR EXTERNAL SCRIPT ###
        ###############################################

            system($RUN);

        ################################    
        ###             END          ###
        ################################
        killChilds($pidmaster,SIGTERM);
        die;
    }
}
<?php

$i=0;
while($i<25){
    print "test... $i\n";
    $i++;
    sleep(1);
}