label); $kind = trim($this->kind); $scope = trim($this->scope); if ($label === '') { throw new InvalidArgumentException('Next-step labels must not be empty.'); } if (! in_array($kind, ['link', 'instruction', 'diagnostic_only'], true)) { throw new InvalidArgumentException('Unsupported next-step kind: '.$kind); } if (! in_array($scope, ['tenant', 'workspace', 'system', 'none'], true)) { throw new InvalidArgumentException('Unsupported next-step scope: '.$scope); } if ($kind === 'link' && trim((string) $this->destination) === '') { throw new InvalidArgumentException('Link next steps require a destination.'); } } public static function link( string $label, string $destination, bool $authorizationRequired = true, string $scope = 'tenant', ): self { return new self( label: $label, kind: 'link', destination: $destination, authorizationRequired: $authorizationRequired, scope: $scope, ); } public static function instruction(string $label, string $scope = 'none'): self { return new self( label: $label, kind: 'instruction', scope: $scope, ); } public static function diagnosticOnly(string $label): self { return new self( label: $label, kind: 'diagnostic_only', scope: 'none', ); } /** * @param array $data */ public static function fromArray(array $data): ?self { $label = is_string($data['label'] ?? null) ? trim((string) $data['label']) : ''; $kind = is_string($data['kind'] ?? null) ? trim((string) $data['kind']) : ((is_string($data['url'] ?? null) || is_string($data['destination'] ?? null)) ? 'link' : 'instruction'); $destination = is_string($data['destination'] ?? null) ? trim((string) $data['destination']) : (is_string($data['url'] ?? null) ? trim((string) $data['url']) : null); $authorizationRequired = (bool) ($data['authorization_required'] ?? $data['authorizationRequired'] ?? false); $scope = is_string($data['scope'] ?? null) ? trim((string) $data['scope']) : 'none'; if ($label === '') { return null; } return new self( label: $label, kind: $kind, destination: $destination !== '' ? $destination : null, authorizationRequired: $authorizationRequired, scope: $scope, ); } /** * @param array> $items * @return array */ public static function collect(array $items): array { $options = []; foreach ($items as $item) { if (! is_array($item)) { continue; } $option = self::fromArray($item); if ($option instanceof self) { $options[] = $option; } } return $options; } /** * @return array{ * label: string, * kind: string, * destination: ?string, * authorization_required: bool, * scope: string * } */ public function toArray(): array { return [ 'label' => $this->label, 'kind' => $this->kind, 'destination' => $this->destination, 'authorization_required' => $this->authorizationRequired, 'scope' => $this->scope, ]; } /** * @return array{label: string, url: string} */ public function toLegacyArray(): array { return [ 'label' => $this->label, 'url' => (string) $this->destination, ]; } }