state === BoundaryCircuitState::OPEN; } /** * Check if circuit is closed */ public function isClosed(): bool { return $this->state === BoundaryCircuitState::CLOSED; } /** * Check if circuit is half-open */ public function isHalfOpen(): bool { return $this->state === BoundaryCircuitState::HALF_OPEN; } /** * Record a failure and return new state */ public function recordFailure(): self { return new self( failureCount: $this->failureCount + 1, successCount: $this->successCount, lastFailureTime: Timestamp::now(), openedAt: $this->openedAt, state: $this->state, halfOpenAttempts: $this->halfOpenAttempts, ); } /** * Record a success and return new state */ public function recordSuccess(): self { return new self( failureCount: 0, // Reset failure count on success successCount: $this->successCount + 1, lastFailureTime: $this->lastFailureTime, openedAt: null, // Reset opened time state: $this->state, halfOpenAttempts: 0, // Reset half-open attempts ); } /** * Transition to OPEN state */ public function transitionToOpen(): self { return new self( failureCount: $this->failureCount, successCount: $this->successCount, lastFailureTime: $this->lastFailureTime, openedAt: Timestamp::now(), state: BoundaryCircuitState::OPEN, halfOpenAttempts: 0, ); } /** * Transition to HALF_OPEN state */ public function transitionToHalfOpen(): self { return new self( failureCount: $this->failureCount, successCount: $this->successCount, lastFailureTime: $this->lastFailureTime, openedAt: $this->openedAt, state: BoundaryCircuitState::HALF_OPEN, halfOpenAttempts: 0, ); } /** * Transition to CLOSED state */ public function transitionToClosed(): self { return new self( failureCount: 0, // Reset failure count successCount: $this->successCount, lastFailureTime: null, // Reset last failure time openedAt: null, // Reset opened time state: BoundaryCircuitState::CLOSED, halfOpenAttempts: 0, ); } /** * Increment half-open attempts */ public function incrementHalfOpenAttempts(): self { return new self( failureCount: $this->failureCount, successCount: $this->successCount, lastFailureTime: $this->lastFailureTime, openedAt: $this->openedAt, state: $this->state, halfOpenAttempts: $this->halfOpenAttempts + 1, ); } /** * Check if failure threshold is exceeded */ public function exceedsFailureThreshold(int $threshold): bool { return $this->failureCount >= $threshold; } /** * Check if success threshold is met for half-open state */ public function meetsSuccessThreshold(int $threshold): bool { return $this->successCount >= $threshold; } /** * Check if half-open attempts exceed maximum */ public function exceedsHalfOpenAttempts(int $maxAttempts): bool { return $this->halfOpenAttempts >= $maxAttempts; } /** * Convert to array for serialization */ public function toArray(): array { return [ 'failure_count' => $this->failureCount, 'success_count' => $this->successCount, 'last_failure_time' => $this->lastFailureTime?->toFloat(), 'opened_at' => $this->openedAt?->toFloat(), 'state' => $this->state->value, 'half_open_attempts' => $this->halfOpenAttempts, ]; } /** * Create from array (deserialization) */ public static function fromArray(array $data): static { return new self( failureCount: $data['failure_count'] ?? 0, successCount: $data['success_count'] ?? 0, lastFailureTime: isset($data['last_failure_time']) ? Timestamp::fromFloat($data['last_failure_time']) : null, openedAt: isset($data['opened_at']) ? Timestamp::fromFloat($data['opened_at']) : null, state: BoundaryCircuitState::from($data['state'] ?? 'closed'), halfOpenAttempts: $data['half_open_attempts'] ?? 0, ); } }