$canonicalNouns * @param list $allowedConsumers */ public function __construct( public string $registryKey, public string $boundaryClassification, public string $ownerLayer, public string $sourceClassOrFile, public array $canonicalNouns, public array $allowedConsumers, public ?string $compatibilityNotes = null, ) { if (trim($this->registryKey) === '' || trim($this->sourceClassOrFile) === '') { throw new InvalidArgumentException('Registry ownership descriptors require a registry key and source reference.'); } if ($this->canonicalNouns === [] || $this->allowedConsumers === []) { throw new InvalidArgumentException('Registry ownership descriptors require canonical nouns and allowed consumers.'); } } /** * @param array $data */ public static function fromArray(array $data): self { return new self( registryKey: (string) ($data['registry_key'] ?? ''), boundaryClassification: (string) ($data['boundary_classification'] ?? ''), ownerLayer: (string) ($data['owner_layer'] ?? ''), sourceClassOrFile: (string) ($data['source_class_or_file'] ?? ''), canonicalNouns: self::stringList($data['canonical_nouns'] ?? []), allowedConsumers: self::stringList($data['allowed_consumers'] ?? []), compatibilityNotes: is_string($data['compatibility_notes'] ?? null) ? trim((string) $data['compatibility_notes']) : null, ); } /** * @return array{ * registry_key: string, * boundary_classification: string, * owner_layer: string, * source_class_or_file: string, * canonical_nouns: list, * allowed_consumers: list, * compatibility_notes: ?string * } */ public function toArray(): array { return [ 'registry_key' => $this->registryKey, 'boundary_classification' => $this->boundaryClassification, 'owner_layer' => $this->ownerLayer, 'source_class_or_file' => $this->sourceClassOrFile, 'canonical_nouns' => $this->canonicalNouns, 'allowed_consumers' => $this->allowedConsumers, 'compatibility_notes' => $this->compatibilityNotes, ]; } /** * @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(); } }