started = microtime( true ); $budget->seconds = self::derive_seconds(); $budget->memory_ceiling = self::derive_memory_ceiling(); // Room for one slow entry past the budget, and still well inside the // lock's lifetime: a step outliving its lock would run twice at once. if ( function_exists( 'set_time_limit' ) ) { set_time_limit( Step_Lock::TTL - 60 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- one long export or restore step, bounded by the step lock. } return $budget; } /** * Whether this step should stop taking new work. * * @return bool */ public function spent(): bool { return $this->expired() || $this->exhausted(); } /** * Whether the time budget is used up. * * @return bool */ public function expired(): bool { return $this->elapsed() >= $this->seconds; } /** * Whether memory is close enough to the ceiling to stop. * * @return bool */ public function exhausted(): bool { if ( 0 === $this->memory_ceiling ) { return false; } return memory_get_usage( true ) > ( $this->memory_ceiling * self::MEMORY_FRACTION ); } /** * How long this budget lasts. * * @return float */ public function seconds(): float { return $this->seconds; } /** * How long this budget has been running. * * @return float */ public function elapsed(): float { return microtime( true ) - $this->started; } /** * Seconds of work one step may attempt on this host. * * @return float */ private static function derive_seconds(): float { $ini = (int) ini_get( 'max_execution_time' ); $seconds = $ini > 0 ? max( self::FLOOR, min( self::CEILING, (int) floor( $ini * 0.6 ) ) ) : self::CEILING; /** * Filter the per-step time budget, in seconds. * * @param float $seconds Derived budget. */ $seconds = (float) apply_filters( 'transtrix_step_budget', (float) $seconds ); return max( 1.0, $seconds ); } /** * The memory ceiling in bytes, or 0 when unlimited. * * @return int */ private static function derive_memory_ceiling(): int { $limit = wp_convert_hr_to_bytes( (string) ini_get( 'memory_limit' ) ); return $limit > 0 ? (int) $limit : 0; } }