*/ final readonly class MatchingOutcome implements Arrayable, JsonSerializable { public const string Resolved = 'resolved'; public const string Ambiguous = 'ambiguous'; public const string MissingProviderResource = 'missing_provider_resource'; public const string MissingLocalEvidence = 'missing_local_evidence'; public const string UnresolvedIdentity = 'unresolved_identity'; public const string Unsupported = 'unsupported'; public const string Limited = 'limited'; public const string Excluded = 'excluded'; /** * @param array $proof */ public function __construct( public string $status, public string $reasonCode, public BaselineSubjectDescriptor $subject, public ?ProviderResourceDescriptor $matchedDescriptor = null, public ?string $matchedSubjectKey = null, public string $trust = 'none', public array $proof = [], ) {} /** * @param array $proof */ public static function resolved( BaselineSubjectDescriptor $subject, ProviderResourceDescriptor $matchedDescriptor, string $matchedSubjectKey, string $reasonCode, string $trust, array $proof = [], ): self { return new self( status: self::Resolved, reasonCode: $reasonCode, subject: $subject, matchedDescriptor: $matchedDescriptor, matchedSubjectKey: $matchedSubjectKey, trust: $trust, proof: $proof, ); } /** * @param array $proof */ public static function ambiguous(BaselineSubjectDescriptor $subject, array $proof = []): self { return new self( status: self::Ambiguous, reasonCode: 'ambiguous_match', subject: $subject, trust: 'none', proof: $proof, ); } /** * @param array $proof */ public static function missingLocalEvidence(BaselineSubjectDescriptor $subject, array $proof = []): self { return new self( status: self::MissingLocalEvidence, reasonCode: 'missing_local_evidence', subject: $subject, trust: 'none', proof: $proof, ); } /** * @param array $proof */ public static function missingProviderResource(BaselineSubjectDescriptor $subject, array $proof = []): self { return new self( status: self::MissingProviderResource, reasonCode: 'missing_provider_resource', subject: $subject, trust: 'none', proof: $proof, ); } /** * @param array $proof */ public static function unresolvedIdentity(BaselineSubjectDescriptor $subject, array $proof = []): self { return new self( status: self::UnresolvedIdentity, reasonCode: 'identity_required', subject: $subject, trust: 'none', proof: $proof, ); } /** * @param array $proof */ public static function unsupported(BaselineSubjectDescriptor $subject, array $proof = []): self { return new self( status: self::Unsupported, reasonCode: 'unsupported_subject', subject: $subject, trust: 'none', proof: $proof, ); } /** * @param array $proof */ public static function limited(BaselineSubjectDescriptor $subject, string $reasonCode = 'accepted_limitation', array $proof = []): self { return new self( status: self::Limited, reasonCode: $reasonCode, subject: $subject, trust: 'limited', proof: $proof, ); } /** * @param array $proof */ public static function excluded(BaselineSubjectDescriptor $subject, array $proof = []): self { return new self( status: self::Excluded, reasonCode: 'excluded_non_governed', subject: $subject, trust: 'none', proof: $proof, ); } /** * @param array $proof */ public function isComparable(): bool { return $this->status === self::Resolved; } public function isGap(): bool { return ! $this->isComparable(); } public function requiresWarning(): bool { return false; } public function toArray(): array { return [ 'status' => $this->status, 'reason_code' => $this->reasonCode, 'subject' => $this->subject->toArray(), 'matched_descriptor' => $this->matchedDescriptor?->toArray(), 'matched_subject_key' => $this->matchedSubjectKey, 'trust' => $this->trust, 'proof' => $this->safeProof($this->proof), ]; } public function jsonSerialize(): array { return $this->toArray(); } /** * @param array $proof * @return array */ private function safeProof(array $proof): array { $safe = []; foreach ($proof as $key => $value) { if (! is_string($key) || trim($key) === '' || (! is_scalar($value) && $value !== null)) { continue; } $safe[$key] = $value; } ksort($safe); return $safe; } }