7

I need to create a cycle to end when it is longer than 20 seconds. I have tried the following code but it's not working, running forever.

EDIT: For simple code it will stop fine, but using the include_once and include the external files keep running even after the 20 seconds expire

bot.php

$starttime = time();


while (time() - $starttime < 20) {    

 include_once 'onefile.php';
 include 'somefile.php';
 include 'somefile2.php';

}

EDIT 2:

With Josh Trii Johnston's answer the proccess is really stopped if not concluded within X seconds. The problem now is that there is another requisit on my case.. The sample provided above does not run alone. It is also included on another loop file like this:

master.php

<?php
       while (1) {   
            include 'bot.php'; 
            sleep(60);
        }

As you can see it is running on a infinite loop and what I want is not stop the entire loop but only "break" the bot.php loop, keeping the main while(1) loop active. With the provided solution it exits all the loops and the process is terminated.

ace
  • 313
  • 1
  • 5
  • 19
  • @Barmar yes you are absolutley right – B001ᛦ Sep 02 '15 at 15:12
  • I just tried your code and it worked fine. – Barmar Sep 02 '15 at 15:16
  • See working demo: http://ideone.com/AtZ6X3 – Barmar Sep 02 '15 at 15:19
  • Most probably, there is some memory overflow in your code. – someOne Sep 02 '15 at 15:26
  • Yeah, memory is racking up too fast, but here's another demo that seems to work with certain versions.. you may have to inclrease the memeory limit on your script though: https://3v4l.org/QtsO7 – I wrestled a bear once. Sep 02 '15 at 15:28
  • Yeah. I see that the code works with simple "/// CODE". I have edited original description. I am using - include 'file.php'; and that doesn't stop that way. It doesn't stop running the files wich are in fact a little bit big +- 10 minutes. I am putting the 20 second to hit and test if the timelimit works effectivelly – ace Sep 02 '15 at 15:30
  • 3
    Well there's your problem. The `include` is synchronous, it doesn't matter if you wrap a `while` around it. By the time it reaches the next iteration (**after** the `include` has completed), assuming your file takes longer than 20 seconds to load, it'll have satisfied the while condition by then. – sjagr Sep 02 '15 at 15:36
  • @sjagr is there any similar code that I can use here? – ace Sep 02 '15 at 15:38
  • 1
    I would try @JoshTriiJohnston's answer. – sjagr Sep 02 '15 at 15:40

2 Answers2

7

PHP is not magic and cannot stop execution mid-script like that. The while condition is only checked once all processing inside your includes are completed.

You can try calling register_tick_function() and provide a callback that can check the time elapsed and exit if needed.

Now with more example!

<?php
declare(ticks=1);

define('START_TIME', time());

// lambda uses START_TIME constant
register_tick_function(function() {
    if (time() - START_TIME > 20) {
        echo 'Script execution halted. Took more than 20 seconds to execute';
        exit(1);
    }
}, true);

include_once 'onefile.php';
include 'somefile.php';
include 'somefile2.php';
?>

This will halt on the next tick that happens after the 20 second mark, not exactly 20 seconds.

Edit #2 Working code based on your current updates

This code works on a similar principle but instead of halting script execution, it throws a TimeLimitException which is then used to jump to a point of execution using goto. It is hacky, but it accomplishes what you need.

<?php
declare(ticks=1);

$start_time = time();
class TimeLimitException extends Exception {}

// lambda uses $start_time global so it can reset
// the timer after the time limit is reached
register_tick_function(function() {
    global $start_time;
    if (time() - $start_time > 20) {
        echo 'Script execution halted. Took more than 20 seconds to execute', PHP_EOL;
        $start_time = time();
        throw new TimeLimitException();
    }
}, true);

try {
    // time limit will reset to here. Cannot jump to a label in a loop
    limit:

    while (1) {
        sleep(1);
        echo 'outer ';
        while (1) {
            echo 'inner ';
            sleep(2);
        }
    }
} catch (TimeLimitException $e) {
    echo 'time limit hit, jumping to label `limit`', PHP_EOL;
    goto limit;
}
Josh J
  • 6,813
  • 3
  • 25
  • 47
  • seems a good solution but not sure how to implement it. Will take a deeper look – ace Sep 02 '15 at 15:51
  • Worked like a charm! Just had to change "<" for ">". You mistaken there. – ace Sep 02 '15 at 20:37
  • Sorry about that. I cut n pasted from your original code and didn't verify first. – Josh J Sep 03 '15 at 00:29
  • With proper testing I could conclude that this solution does not fix my problem.. Pls check the edited description. Sorry :\ – ace Sep 03 '15 at 02:26
  • @ace I forgot all about this earlier this month. I've added a new solution that solves your problem based upon my understanding of it. – Josh J Sep 16 '15 at 14:38
1

Actually, something like set_time_limit is a better approach.

Alternatively, if you just want to limit the includetime, do Threads.

class workerThread extends Thread {
    public function __construct(){
        $this->starttime=time();
    }

    public function run(){
        include_once 'onefile.php';
        include 'somefile.php';
        include 'somefile2.php';
        $this->done=true;
    }
    public function finished(){
        return $this->done || (time() - $this->starttime < 20)
    }
}
$worker=new workerThread();
$worker->start();
while(!$worker->finished()){}
user2358582
  • 372
  • 2
  • 10