*/ private $last = array(); /** * Constructor: loads the lane's state from the previous batch. * * @param int $lane Lane index. */ public function __construct( int $lane ) { $this->lane = $lane; $stored = get_option( $this->option(), array() ); if ( is_array( $stored ) ) { foreach ( $stored as $host => $time ) { $this->last[ (string) $host ] = (float) $time; } } } /** * Seconds to wait before the host may be requested again. * * @param string $host Host. */ public function wait_time( string $host ): float { if ( ! isset( $this->last[ $host ] ) ) { return 0.0; } return max( 0.0, $this->last[ $host ] + self::MIN_GAP - $this->now() ); } /** * Record a request to a host. * * @param string $host Host. */ public function record( string $host ): void { $this->last[ $host ] = $this->now(); } /** * Sleep until the host may be requested. * * @param string $host Host. */ public function wait( string $host ): void { $wait = $this->wait_time( $host ); if ( $wait > 0 ) { $this->sleep( $wait ); } } /** * Persist entries that still matter for the next batch. */ public function save(): void { $now = $this->now(); $keep = array(); foreach ( $this->last as $host => $time ) { if ( $now - $time < self::MIN_GAP + 1 ) { $keep[ $host ] = $time; } } update_option( $this->option(), $keep, false ); } /** * Current time (overridable in subclasses). */ protected function now(): float { return microtime( true ); } /** * Sleep (overridable in subclasses). * * @param float $seconds Seconds. */ protected function sleep( float $seconds ): void { usleep( (int) ( $seconds * 1000000 ) ); } /** * Option name for this lane's state. */ private function option(): string { return 'unbreak_throttle_lane_' . $this->lane; } }