Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting a timeout for a block of code in PHP

Tags:

php

timeout

Is there a way you can abort a block of code if it's taking too long in PHP? Perhaps something like:

//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.';
} 

It doesn't have to be exactly like above, but are there any native PHP functions or classes that do something like this?

Edit: Ben Lee's answer with pcnt_fork would be the perfect solution except that it's not available for Windows. Is there any other way to accomplish this with PHP that works for Windows and Linux, but doesn't require an external library?

Edit 2: XzKto's solution works in some cases, but not consistently and I can't seem to catch the exception, no matter what I try. The use case is detecting a timeout for a unit test. If the test times out, I want to terminate it and then move on to the next test.

like image 761
VirtuosiMedia Avatar asked Sep 21 '11 01:09

VirtuosiMedia


People also ask

How to catch timeout exception in PHP?

php ini_set('display_errors', '0'); ini_set("max_execution_time",15 ); //you can use this if you know your script should not take longer than 15 seconds to finish register_shutdown_function('shutdown'); function shutdown() { $error = error_get_last(); if ($error['type'] === E_ERROR) { //do your shutdown stuff here //be ...


1 Answers

You can do this by forking the process, and then using the parent process to monitor the child process. pcntl_fork is a method that forks the process, so you have two nearly identical programs in memory running in parallel. The only difference is that in one process, the parent, pcntl_fork returns a positive integer which corresponds to the process id of the child process. And in the other process, the child, pcntl_fork returns 0.

Here's an example:

$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
}

That's the basic structure. Next step is to add a process expiration. Your stuff will run in the child process, and the parent process will be responsible only for monitoring and timing the child process. But in order for one process (the parent) to kill another (the child), there needs to be a signal. Signals are how processes communicate, and the signal that means "you should end immediately" is SIGKILL. You can send this signal using posix_kill. So the parent should just wait 2 seconds then kill the child, like so:

$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
}
like image 171
Ben Lee Avatar answered Oct 12 '22 01:10

Ben Lee