$signalKeys * @param list $supportedContexts */ public function __construct( public string $controlKey, public ?string $subjectFamilyKey, public ?string $workload, public array $signalKeys, public array $supportedContexts, public bool $primary = false, public ?string $notes = null, ) { if (trim($this->controlKey) === '') { throw new InvalidArgumentException('Microsoft subject bindings require a canonical control key.'); } if ($this->subjectFamilyKey === null && $this->workload === null && $this->signalKeys === []) { throw new InvalidArgumentException(sprintf('Microsoft subject binding for [%s] requires at least one discriminator.', $this->controlKey)); } if ($this->supportedContexts === []) { throw new InvalidArgumentException(sprintf('Microsoft subject binding for [%s] requires at least one supported context.', $this->controlKey)); } } /** * @param array $data */ public static function fromArray(string $controlKey, array $data): self { return new self( controlKey: $controlKey, subjectFamilyKey: self::optionalString($data['subject_family_key'] ?? null), workload: self::optionalString($data['workload'] ?? null), signalKeys: self::stringList($data['signal_keys'] ?? []), supportedContexts: self::stringList($data['supported_contexts'] ?? []), primary: (bool) ($data['primary'] ?? false), notes: self::optionalString($data['notes'] ?? null), ); } public function supportsContext(string $consumerContext): bool { return in_array(trim($consumerContext), $this->supportedContexts, true); } public function matches(CanonicalControlResolutionRequest $request): bool { if ($request->provider !== 'microsoft') { return false; } if (! $this->supportsContext($request->consumerContext)) { return false; } if ($request->subjectFamilyKey !== null && $this->subjectFamilyKey !== $request->subjectFamilyKey) { return false; } if ($request->workload !== null && $this->workload !== $request->workload) { return false; } if ($request->signalKey !== null && ! in_array($request->signalKey, $this->signalKeys, true)) { return false; } return true; } /** * @return array{ * control_key: string, * provider: string, * subject_family_key: ?string, * workload: ?string, * signal_keys: list, * supported_contexts: list, * primary: bool, * notes: ?string * } */ public function toArray(): array { return [ 'control_key' => $this->controlKey, 'provider' => 'microsoft', 'subject_family_key' => $this->subjectFamilyKey, 'workload' => $this->workload, 'signal_keys' => $this->signalKeys, 'supported_contexts' => $this->supportedContexts, 'primary' => $this->primary, 'notes' => $this->notes, ]; } private static function optionalString(mixed $value): ?string { if (! is_string($value)) { return null; } $trimmed = trim($value); return $trimmed === '' ? null : $trimmed; } /** * @param iterable $values * @return list */ private static function stringList(iterable $values): array { return collect($values) ->filter(static fn (mixed $value): bool => is_string($value) && trim($value) !== '') ->map(static fn (string $value): string => trim($value)) ->values() ->all(); } }