Php – Do not rely on set_time_limit too much

By | April 23, 2019

Php set_time_limit

Php has a function called set_time_limit which can be used to dynamically adjust the maximum execution time permitted to a script. It allows specifying the time in seconds and limits the script execution time to that many seconds.

The set_time_limit function whenever called, effectively extends the script execution time by that many seconds. So if the script has already run for 15 seconds and set_time_limit(30) is called, then it would run for a total of 30+15 = 45 seconds. That's how its designed to work.

The default time limit is generally 30 seconds and defined by the 'max_execution_time' option of php.ini
Use the ini_get function to get it.

ini_get('max_execution_time');

To find out the time already consumed by php use the getrusage function

$dat = getrusage();
echo $dat["ru_nswap"];         // number of swaps
echo $dat["ru_majflt"];        // number of page faults
echo $dat["ru_utime.tv_sec"];  // user time used (seconds)
echo $dat["ru_utime.tv_usec"]; // user time used (microseconds)

However the function itself can show varying behaviour across different platforms and environments and conditions. The documentation mentions

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.

This means that any kind of activity that happens outside the script will not be controllable by set_time_limit. For example if you connect to a remote server using the socket_connect function or any other function that uses socket, then the time spend in the system call (function provided by underlying operating system) to connect will not be controllable by set_time_limit. Although the moment the control returns to the php script, the time limit would take effect.

So lets say if you are trying to ensure that scripts do not run out of bounds using set_time_limit, then make sure that you handle outside script calls separately and do no rely on set_time_limit for them. Otherwise the entire purpose would be defeated since the script still has a scope to consume more time.

Unlimiting with set_time_limit

A different scenario is, when you try to remove time restrictions on the php script to keep it running for as long as it wants or till an error occurs. To set unlimited time call the set_time_limit function with 0 as the parameter

set_time_limit(0);

But this will have different effects at different time. It would depend highly on how exactly the php script is being run.

If the script is being run from command line,

# /usr/bin/php /path/to/script.php

Then the time limit would be by default be 0 , means no time restriction. When running from apache, the max_execution_time is by default set to 30 seconds.

However when the script is run from a browser (that is inside an external environment like apache), and we need the php to keep outputting data to the browser, then php's set_time_limit function is not the only one controlling how long the php script can run and output the data to the browser. The server setup can also impose time restrictions on the php script.

Php handlers

If you are running php as using apache mod_php then you can use set_time_limit(0) to remove execution time limits on the php script. This will work as expected in most situations.

However when php is being run via mod_fcgid or mod_fastcgi or a fastcgi process manager like php-fpm then the running environment will have its own time restrictions on the script. You would notice that even after setting set_time_limit to 0, the script would end after a specific amount of time. This will be the time restriction placed by the execution environment.

For example mod_fastcgi has an option called "-idle-timeout" which controls the idle time of the script. So if the script does not output anything to the fastcgi handler for that many seconds then fastcgi would terminate it. The setup is somewhat like this

Apache <-> mod_fastcgi <-> php processes

Same thing happens with mod_fcgid. It too has an inbuilt time restriction. Php FPM has the option called request_terminate_timeout that will terminate the process after that many seconds.

So next time, don't be surprised when set_time_limit fails and check all php things properly.

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

2 Comments

Php – Do not rely on set_time_limit too much
  1. David Spector / Springtime Software

    I sometimes have bugs in my PHP code that causes recursions involving exception handling and possibly other operations. Unfortunately, I haven’t been able to create a tiny example.

    In these cases, which have happened for me several times, set_time_limit fails, and the browser keeps trying to load the PHP output, either with an infinite loop or with the fatal error message which is the topic of this question.

    By reducing the allowed allocation size by adding

    ini_set(‘memory_limit’,’1M’);

    near the beginning of your code you should be able to prevent the fatal error. Then you may be left with a program that terminates, but is still difficult to debug.

    At this point insert BreakLoop() calls inside your program to gain control and find out what loop or recursion in your program is causing the problem.

    The definition of BreakLoop is as follows:

    function BreakLoop($MaxRepetitions=500,$LoopSite=”unspecified”)
    {
    static $Sites=[];
    if (!@$Sites[$LoopSite] || !$MaxRepetitions)
    $Sites[$LoopSite]=[‘n’=>0, ‘if’=>0];
    if (!$MaxRepetitions)
    return;
    if (++$Sites[$LoopSite][‘n’] >= $MaxRepetitions)
    {
    $S=debug_backtrace(); // array_reverse
    $info=$S[0];
    $File=$info[‘file’];
    $Line=$info[‘line’];
    exit(“*** Loop for site $LoopSite was interrupted after $MaxRepetitions repetitions. In file $File at line $Line.”);
    }
    } // BreakLoop

    The $LoopSite argument can be the name of a function in your code. It isn’t really necessary, since the error message you will get will point you to the line containing the BreakLoop() call.

  2. Chris Harrison

    Hopefully with the next version of PHP, multithreading will reduce the amount of long extended process scripts clogging up PHP developers workflows.
    Heres hoping anyway :)

Leave a Reply

Your email address will not be published. Required fields are marked *