$ipCidrs CIDR allowlist for this token */ public function __construct( public string $name, public ?string $token = null, public array $ipCidrs = [], public ?int $createdAt = null, public ?int $lastUsed = null, public bool $enabled = true, ) { } /** * Build from `POST /admin/tokens` response (includes plaintext token). * * @param array $payload */ public static function fromCreateResponse(array $payload): self { $cidrs = $payload['ip_cidrs'] ?? []; return new self( name: (string) ($payload['name'] ?? ''), token: isset($payload['token']) ? (string) $payload['token'] : null, ipCidrs: self::normalizeCidrs($cidrs), ); } /** * Build from a `GET /admin/tokens` row (no plaintext token). * * @param array $row */ public static function fromListRow(array $row): self { return new self( name: (string) ($row['name'] ?? ''), token: null, ipCidrs: self::normalizeCidrs($row['ip_cidrs'] ?? ''), createdAt: isset($row['created_at']) ? (int) $row['created_at'] : null, lastUsed: isset($row['last_used']) && $row['last_used'] !== null ? (int) $row['last_used'] : null, enabled: (bool) ($row['enabled'] ?? true), ); } /** * Server's `list_tokens` returns ip_cidrs as a comma-joined string; the * create endpoint returns it as a list. Normalize both into list. * * @param mixed $raw * @return list */ private static function normalizeCidrs(mixed $raw): array { if (is_array($raw)) { return array_values(array_filter( array_map(static fn ($v): string => (string) $v, $raw), static fn (string $s): bool => $s !== '', )); } if (is_string($raw) && $raw !== '') { return array_values(array_filter(explode(',', $raw), static fn (string $s): bool => $s !== '')); } return []; } }