StrikerNL
StrikerNL

Reputation: 91

PHP: set_time_limit() seems to have no effect whatsoever

I'm trying to use PHP's set_time_limit() in a normal PHP apache script (not CLI) to limit its max running time. It's not doing anything with system calls or anything similar, we're not using safe mode (we're the owners but not the admins of the server). This is PHP 5.2.4 on Linux (Ubuntu).

Simple test case:

ini_set('max_execution_time', 1);
set_time_limit(1);
$i=0;
while ($i++<100000000000) {} // or anything other arbitrary that takes time
die('Done');

Expected result: something relating to the script execution time being exceeded

Actual result: Done is printed.

Am I doing something obvious wrong? (By the way, yes, the script really needs to and can run for too long and need to be aborted, it's unfortunately not something that can be avoided. That is not the point here.)

Upvotes: 9

Views: 5756

Answers (6)

diyism
diyism

Reputation: 12935

Both max_execution_time(...) and ini_set('max_execution_time',...) won't count the time cost of sleep(),file_get_contents(),shell_exec(),mysql_query() etc, so i build the following function my_background_exec() to run a static method on the background/detached process and when time is out, kill it automatically:

my_exec.php:

<?php
function my_background_exec($function_name, $params, $str_requires, $timeout=600)
         {$map=array('"'=>'\"', '$'=>'\$', '`'=>'\`', '\\'=>'\\\\', '!'=>'\!');
          $str_requires=strtr($str_requires, $map);
          $path_run=dirname($_SERVER['SCRIPT_FILENAME']);
          $my_target_exec="/usr/bin/php -r \"chdir('{$path_run}');{$str_requires}\\\$params=json_decode(file_get_contents('php://stdin'),true);call_user_func_array('{$function_name}', \\\$params);\"";
          $my_target_exec=strtr(strtr($my_target_exec, $map), $map);
          $my_background_exec="(/usr/bin/php -r \"chdir('{$path_run}');{$str_requires}my_timeout_exec(\\\"{$my_target_exec}\\\", file_get_contents('php://stdin'), {$timeout});\" <&3 &) 3<&0";//php by default use "sh", and "sh" don't support "<&0"
          my_timeout_exec($my_background_exec, json_encode($params), 2);
         }

function my_timeout_exec($cmd, $stdin='', $timeout)
         {$start=time();
          $stdout='';
          $stderr='';
          //file_put_contents('debug.txt', time().':cmd:'.$cmd."\n", FILE_APPEND);
          //file_put_contents('debug.txt', time().':stdin:'.$stdin."\n", FILE_APPEND);

          $process=proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes);
          if (!is_resource($process))
             {return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
             }
          $status=proc_get_status($process);
          posix_setpgid($status['pid'], $status['pid']);    //seperate pgid(process group id) from parent's pgid

          stream_set_blocking($pipes[0], 0);
          stream_set_blocking($pipes[1], 0);
          stream_set_blocking($pipes[2], 0);
          fwrite($pipes[0], $stdin);
          fclose($pipes[0]);

          while (1)
                {$stdout.=stream_get_contents($pipes[1]);
                 $stderr.=stream_get_contents($pipes[2]);

                 if (time()-$start>$timeout)
                    {//proc_terminate($process, 9);    //only terminate subprocess, won't terminate sub-subprocess
                     posix_kill(-$status['pid'], 9);    //sends SIGKILL to all processes inside group(negative means GPID, all subprocesses share the top process group, except nested my_timeout_exec)
                     //file_put_contents('debug.txt', time().":kill group {$status['pid']}\n", FILE_APPEND);
                     return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
                    }

                 $status=proc_get_status($process);
                 //file_put_contents('debug.txt', time().':status:'.var_export($status, true)."\n";
                 if (!$status['running'])
                    {fclose($pipes[1]);
                     fclose($pipes[2]);
                     proc_close($process);
                     return $status['exitcode'];
                    }

                 usleep(100000); 
                }
         }
?>

test.php:

<?php
my_background_exec('A::jack', array('hello ', 'jack'), 'require "my_exec.php";require "a_class.php";', 8);
?>

a_class.php:

<?php
class A
{
    static function jack($a, $b)
           {sleep(4);
            file_put_contents('debug.txt', time().":A::jack:".$a.' '.$b."\n", FILE_APPEND);
            sleep(15);
           }
}
?>

Upvotes: 2

Cogicero
Cogicero

Reputation: 1524

Check if your ini_set works by checking the disable_functions flag in php.ini. If it is not disabled, echo phpinfo() immediately after your ini_set('max_execution_time', 1); above. If the value is changed under the local column, then you know ini_set works. Else, your script has not set any maximum execution time, then.

Upvotes: 0

klennepette
klennepette

Reputation: 3196

Try having your script actually do something, sleep time isn't counted towards execution time since while the script is sleeping it's not executing.

EDIT : Check out this bugreport http://bugs.php.net/37306 The last comment is that it was fixed in "CVS HEAD, PHP_5_2 and PHP_5_1." so maybe your PHP version has this bug. Maybe you can you try changing the max_input_time.

Upvotes: 1

Kristoffer la Cour
Kristoffer la Cour

Reputation: 2576

That is because the function sleep is ignored by the time limit, it will stop counting time when sleep is activated and start counting again when it's deactivated.

Upvotes: 1

code_burgar
code_burgar

Reputation: 12323

On some systems ini_set and similar functions are disabled for security and resource sharing reasons. Check with your administrator to see if such restrictions are in place.

Upvotes: 2

Mark Baker
Mark Baker

Reputation: 212412

Quoting from the manual:

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.

sleep() is among those functions that do not affect the running time of the script

see MPHH's comment on the sleep() page of the manual

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(), the sleep() function, database queries, etc. is not included when determining the maximum time that the script has been running.

Upvotes: 5

Related Questions