Merge "Fix support for TestSwarm on SpecialJavaScriptTest/qunit"
[lhc/web/wiklou.git] / includes / ScopedPHPTimeout.php
1 <?php
2
3 /**
4 * Class to expand PHP execution time for a function call.
5 * On construction, set_time_limit() is called and set to $seconds.
6 * When the object goes out of scope, the timer is restarted, with
7 * the original time limit minus the time the object existed.
8 */
9 class ScopedPHPTimeout {
10 protected $startTime; // float; seconds
11 protected $oldTimeout; // integer; seconds
12
13 protected static $stackDepth = 0; // integer
14 protected static $totalCalls = 0; // integer
15 protected static $totalElapsed = 0; // float; seconds
16
17 /* Prevent callers in infinite loops from running forever */
18 const MAX_TOTAL_CALLS = 1000000;
19 const MAX_TOTAL_TIME = 300; // seconds
20
21 /**
22 * @param $seconds integer
23 */
24 public function __construct( $seconds ) {
25 if ( ini_get( 'max_execution_time' ) > 0 ) { // CLI uses 0
26 if ( self::$totalCalls >= self::MAX_TOTAL_CALLS ) {
27 trigger_error( "Maximum invocations of " . __CLASS__ . " exceeded." );
28 } elseif ( self::$totalElapsed >= self::MAX_TOTAL_TIME ) {
29 trigger_error( "Time limit within invocations of " . __CLASS__ . " exceeded." );
30 } elseif ( self::$stackDepth > 0 ) { // recursion guard
31 trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." );
32 } else {
33 $this->oldTimeout = ini_set( 'max_execution_time', $seconds );
34 $this->startTime = microtime( true );
35 ++self::$stackDepth;
36 ++self::$totalCalls; // proof against < 1us scopes
37 }
38 }
39 }
40
41 /**
42 * Restore the original timeout.
43 * This does not account for the timer value on __construct().
44 */
45 public function __destruct() {
46 if ( $this->oldTimeout ) {
47 $elapsed = microtime( true ) - $this->startTime;
48 // Note: a limit of 0 is treated as "forever"
49 set_time_limit( max( 1, $this->oldTimeout - (int)$elapsed ) );
50 // If each scoped timeout is for less than one second, we end up
51 // restoring the original timeout without any decrease in value.
52 // Thus web scripts in an infinite loop can run forever unless we
53 // take some measures to prevent this. Track total time and calls.
54 self::$totalElapsed += $elapsed;
55 --self::$stackDepth;
56 }
57 }
58 }