MediaWiki  REL1_22
ScopedPHPTimeout.php
Go to the documentation of this file.
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 }