A circuit breaker has three states and people implement two

Closed and open are obvious: pass through, or fail fast. The third state is what makes it a breaker rather than a switch — half-open lets exactly one request through to find out whether the dependency has recovered.

// closed    → calls pass through, failures counted
// open      → calls fail immediately, no request made
// half-open → after a cooldown, ONE call is allowed
//             success → closed;  failure → open again

if ($this->state === 'open' && $this->openedAt + $this->cooldown < time()) {
    $this->state = 'half-open';
}

Without half-open, something has to close the breaker and the only candidates are a timer, which reopens the flood, or a human. The subtlety is that half-open must admit one request and not a burst — otherwise the recovering service receives the whole backlog and fails again, which is the thundering herd the breaker was protecting it from.