> $matchedScopeEntries * @param list> $rejectedScopeEntries * @param array $diagnostics */ public function __construct( public readonly StrategySelectionState $selectionState, public readonly ?CompareStrategyKey $strategyKey, public readonly array $matchedScopeEntries, public readonly array $rejectedScopeEntries, public readonly string $operatorReason, public readonly array $diagnostics = [], ) { if ($this->selectionState === StrategySelectionState::Supported && ! $this->strategyKey instanceof CompareStrategyKey) { throw new InvalidArgumentException('Supported compare strategy selections require a strategy key.'); } if (trim($this->operatorReason) === '') { throw new InvalidArgumentException('Compare strategy selections require an operator-safe reason.'); } } /** * @param list> $matchedScopeEntries * @param array $diagnostics */ public static function supported( CompareStrategyKey|string $strategyKey, array $matchedScopeEntries, array $diagnostics = [], string $operatorReason = 'Compare strategy resolved successfully.', ): self { return new self( selectionState: StrategySelectionState::Supported, strategyKey: CompareStrategyKey::from($strategyKey), matchedScopeEntries: $matchedScopeEntries, rejectedScopeEntries: [], operatorReason: $operatorReason, diagnostics: $diagnostics, ); } /** * @param list> $matchedScopeEntries * @param list> $rejectedScopeEntries * @param array $diagnostics */ public static function unsupported( array $matchedScopeEntries, array $rejectedScopeEntries, array $diagnostics = [], string $operatorReason = 'No compare strategy supports the selected governed subjects.', ): self { return new self( selectionState: StrategySelectionState::Unsupported, strategyKey: null, matchedScopeEntries: $matchedScopeEntries, rejectedScopeEntries: $rejectedScopeEntries, operatorReason: $operatorReason, diagnostics: $diagnostics, ); } /** * @param list> $matchedScopeEntries * @param array $diagnostics */ public static function mixed( array $matchedScopeEntries, array $diagnostics = [], string $operatorReason = 'The selected governed subjects span multiple compare strategy families.', ): self { return new self( selectionState: StrategySelectionState::Mixed, strategyKey: null, matchedScopeEntries: $matchedScopeEntries, rejectedScopeEntries: [], operatorReason: $operatorReason, diagnostics: $diagnostics, ); } public function isSupported(): bool { return $this->selectionState === StrategySelectionState::Supported; } public function isUnsupported(): bool { return $this->selectionState === StrategySelectionState::Unsupported; } public function isMixed(): bool { return $this->selectionState === StrategySelectionState::Mixed; } /** * @return array{ * selection_state: string, * strategy_key: ?string, * matched_scope_entries: list>, * rejected_scope_entries: list>, * operator_reason: string, * diagnostics: array * } */ public function toArray(): array { return [ 'selection_state' => $this->selectionState->value, 'strategy_key' => $this->strategyKey?->value, 'matched_scope_entries' => $this->matchedScopeEntries, 'rejected_scope_entries' => $this->rejectedScopeEntries, 'operator_reason' => $this->operatorReason, 'diagnostics' => $this->diagnostics, ]; } }