*/ public static function featuredTlds(): array { $allTlds = self::allTlds(); $featured = array_values(array_intersect(self::PRIORITY_TLDS, $allTlds)); if ($featured !== []) { return $featured; } return array_slice($allTlds, 0, self::DEFAULT_PAGE_SIZE); } /** * Get all available TLDs sorted by priority. * * @return list */ public static function allTlds(): array { $current = self::normalizeTlds(config('domain.search_tlds', [])); $legacy = self::normalizeTlds(config('mailinfra.domain_search_tlds', [])); $tlds = $legacy !== [] ? $legacy : $current; $livePricedTlds = self::livePricedTlds(); if ($livePricedTlds === []) { return []; } $livePricedLookup = array_flip($livePricedTlds); $tlds = array_values(array_filter( $tlds, static fn (string $tld): bool => isset($livePricedLookup[$tld]) )); return self::sortWithPriority($tlds); } /** * Get paginated TLDs (excluding featured ones). * * @return array{tlds: list, hasMore: bool, total: int} */ public static function paginatedTlds(int $page = 1, int $perPage = self::DEFAULT_PAGE_SIZE): array { $allTlds = self::allTlds(); $featured = self::featuredTlds(); $remaining = array_values(array_filter($allTlds, fn ($tld) => !in_array($tld, $featured, true))); $total = count($remaining); $offset = ($page - 1) * $perPage; $tlds = array_slice($remaining, $offset, $perPage); return [ 'tlds' => $tlds, 'hasMore' => ($offset + $perPage) < $total, 'total' => $total, ]; } /** * @deprecated Use featuredTlds() or allTlds() instead * @return list */ public static function searchTlds(): array { return self::featuredTlds(); } /** * Build the TLD list and query term for a search page request. * * @return array{query: string, tlds: list, hasMore: bool, exact_domain: string|null} */ public static function searchPageConfig(string $query, int $page = 1, int $perPage = self::DEFAULT_PAGE_SIZE): array { $query = strtolower(trim($query)); $allTlds = self::allTlds(); if ($allTlds === []) { return [ 'query' => $query, 'tlds' => [], 'hasMore' => false, 'exact_domain' => null, ]; } $exactDomain = self::normalizeExactDomain($query); if ($exactDomain === null) { if ($page === 1) { $featured = self::featuredTlds(); $paginatedData = self::paginatedTlds(1, $perPage); return [ 'query' => $query, 'tlds' => $featured, 'hasMore' => $paginatedData['hasMore'] || count($paginatedData['tlds']) > 0, 'exact_domain' => null, ]; } $paginatedData = self::paginatedTlds($page - 1, $perPage); return [ 'query' => $query, 'tlds' => $paginatedData['tlds'], 'hasMore' => $paginatedData['hasMore'], 'exact_domain' => null, ]; } $exactTld = self::extractTld($exactDomain); $keyword = explode('.', $exactDomain, 2)[0] ?? $exactDomain; $firstPageTlds = array_values(array_unique(array_filter([ $exactTld, ...self::featuredTlds(), ]))); $remainingTlds = array_values(array_filter( $allTlds, static fn (string $tld): bool => ! in_array($tld, $firstPageTlds, true) )); if ($page === 1) { return [ 'query' => $keyword, 'tlds' => $firstPageTlds, 'hasMore' => $remainingTlds !== [], 'exact_domain' => $exactDomain, ]; } $offset = max(0, ($page - 2) * $perPage); return [ 'query' => $keyword, 'tlds' => array_slice($remainingTlds, $offset, $perPage), 'hasMore' => ($offset + $perPage) < count($remainingTlds), 'exact_domain' => $exactDomain, ]; } /** * Sort domain search results by TLD priority. * * @param array $results * @return array */ public static function sortResultsByTldPriority(array $results, ?string $query = null): array { $priorityMap = array_flip(self::PRIORITY_TLDS); $exactDomain = self::normalizeExactDomain((string) $query); usort($results, static function (array $a, array $b) use ($priorityMap, $exactDomain): int { $aDomain = strtolower((string) ($a['domain'] ?? '')); $bDomain = strtolower((string) ($b['domain'] ?? '')); $aExact = $exactDomain !== null && $aDomain === $exactDomain ? 0 : 1; $bExact = $exactDomain !== null && $bDomain === $exactDomain ? 0 : 1; if ($aExact !== $bExact) { return $aExact <=> $bExact; } $aPremium = self::isPremiumResult($a) ? 0 : 1; $bPremium = self::isPremiumResult($b) ? 0 : 1; if ($aPremium !== $bPremium) { return $aPremium <=> $bPremium; } $aAvailable = ! empty($a['available']) ? 0 : 1; $bAvailable = ! empty($b['available']) ? 0 : 1; if ($aAvailable !== $bAvailable) { return $aAvailable <=> $bAvailable; } $aTld = self::extractTld($a['domain'] ?? ''); $bTld = self::extractTld($b['domain'] ?? ''); $aPriority = $priorityMap[$aTld] ?? PHP_INT_MAX; $bPriority = $priorityMap[$bTld] ?? PHP_INT_MAX; if ($aPriority !== $bPriority) { return $aPriority <=> $bPriority; } return $aDomain <=> $bDomain; }); return $results; } /** * Extract TLD from a domain name. */ private static function extractTld(string $domain): string { $parts = explode('.', strtolower($domain), 2); return $parts[1] ?? ''; } private static function normalizeExactDomain(string $query): ?string { $query = strtolower(trim($query)); if ($query === '' || str_starts_with($query, '.') || str_ends_with($query, '.') || ! str_contains($query, '.')) { return null; } return preg_match('/^(?=.{1,253}$)(?!-)(?:[a-z0-9-]{1,63}\.)+[a-z]{2,63}$/', $query) === 1 ? $query : null; } /** * @param array $result */ private static function isPremiumResult(array $result): bool { $premium = $result['premium'] ?? false; if (is_bool($premium)) { return $premium; } return in_array(strtolower(trim((string) $premium)), ['1', 'yes', 'true', 'premium'], true); } /** * @param list $tlds * @return list */ private static function sortWithPriority(array $tlds): array { $priorityMap = array_flip(self::PRIORITY_TLDS); $pricingMap = self::pricingMapForTlds($tlds); usort($tlds, static function (string $a, string $b) use ($priorityMap, $pricingMap): int { $aPriority = $priorityMap[$a] ?? PHP_INT_MAX; $bPriority = $priorityMap[$b] ?? PHP_INT_MAX; if ($aPriority !== $bPriority) { return $aPriority <=> $bPriority; } $aPrice = (int) ($pricingMap[$a]['register'] ?? 0); $bPrice = (int) ($pricingMap[$b]['register'] ?? 0); if ($aPrice !== $bPrice) { return $bPrice <=> $aPrice; } return $a <=> $b; }); return $tlds; } /** * @return array */ public static function resellerClubTldProductKeys(): array { return array_replace( self::normalizeProductKeyMap(config('domain.resellerclub.tld_product_keys', [])), self::normalizeProductKeyMap(config('mailinfra.resellerclub_tld_product_keys', [])) ); } public static function resellerClubProductKeyCacheTtlSeconds(): int { $ttl = (int) config('domain.resellerclub.product_key_cache_ttl_seconds', 86400); return $ttl > 0 ? $ttl : 86400; } /** * @param mixed $value * @return list */ private static function normalizeTlds(mixed $value): array { if (! is_array($value)) { return []; } return array_values(array_unique(array_filter(array_map( static fn ($tld): string => ltrim(strtolower(trim((string) $tld)), '.'), $value )))); } /** * @param mixed $value * @return array */ private static function normalizeProductKeyMap(mixed $value): array { if (! is_array($value)) { return []; } $map = []; foreach ($value as $tld => $productKey) { $normalizedTld = ltrim(strtolower(trim((string) $tld)), '.'); $normalizedProductKey = trim((string) $productKey); if ($normalizedTld === '' || $normalizedProductKey === '') { continue; } $map[$normalizedTld] = $normalizedProductKey; } return $map; } /** * @return list */ private static function livePricedTlds(): array { try { return app(DomainPricingService::class)->getLivePricedTlds(); } catch (\Throwable) { return []; } } /** * @param list $tlds * @return array */ private static function pricingMapForTlds(array $tlds): array { try { return app(DomainPricingService::class)->getPricingForTlds($tlds); } catch (\Throwable) { return []; } } }