MediaWiki
REL1_22
|
00001 <?php 00032 class ScopedPHPTimeout { 00033 protected $startTime; // float; seconds 00034 protected $oldTimeout; // integer; seconds 00035 protected $oldIgnoreAbort; // boolean 00036 00037 protected static $stackDepth = 0; // integer 00038 protected static $totalCalls = 0; // integer 00039 protected static $totalElapsed = 0; // float; seconds 00040 00041 /* Prevent callers in infinite loops from running forever */ 00042 const MAX_TOTAL_CALLS = 1000000; 00043 const MAX_TOTAL_TIME = 300; // seconds 00044 00048 public function __construct( $seconds ) { 00049 if ( ini_get( 'max_execution_time' ) > 0 ) { // CLI uses 0 00050 if ( self::$totalCalls >= self::MAX_TOTAL_CALLS ) { 00051 trigger_error( "Maximum invocations of " . __CLASS__ . " exceeded." ); 00052 } elseif ( self::$totalElapsed >= self::MAX_TOTAL_TIME ) { 00053 trigger_error( "Time limit within invocations of " . __CLASS__ . " exceeded." ); 00054 } elseif ( self::$stackDepth > 0 ) { // recursion guard 00055 trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." ); 00056 } else { 00057 $this->oldIgnoreAbort = ignore_user_abort( true ); 00058 $this->oldTimeout = ini_set( 'max_execution_time', $seconds ); 00059 $this->startTime = microtime( true ); 00060 ++self::$stackDepth; 00061 ++self::$totalCalls; // proof against < 1us scopes 00062 } 00063 } 00064 } 00065 00070 public function __destruct() { 00071 if ( $this->oldTimeout ) { 00072 $elapsed = microtime( true ) - $this->startTime; 00073 // Note: a limit of 0 is treated as "forever" 00074 set_time_limit( max( 1, $this->oldTimeout - (int)$elapsed ) ); 00075 // If each scoped timeout is for less than one second, we end up 00076 // restoring the original timeout without any decrease in value. 00077 // Thus web scripts in an infinite loop can run forever unless we 00078 // take some measures to prevent this. Track total time and calls. 00079 self::$totalElapsed += $elapsed; 00080 --self::$stackDepth; 00081 ignore_user_abort( $this->oldIgnoreAbort ); 00082 } 00083 } 00084 }