Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/Domains/Registrar/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ abstract public function purchase(string $domain, array $contacts, array $namese
* @param array $query
* @param array $tlds
* @param int|null $limit
* @param int|null $priceMax
* @param int|null $priceMin
* @param string|null $filterType Filter results by type: 'premium', 'suggestion', or null for both
* @param int|null $premiumPriceMax
* @param int|null $premiumPriceMin
* @return array
*/
abstract public function suggest(array|string $query, array $tlds = [], int|null $limit = null, int|null $priceMax = null, int|null $priceMin = null): array;
abstract public function suggest(array|string $query, array $tlds = [], int|null $limit = null, string|null $filterType = null, int|null $premiumPriceMax = null, int|null $premiumPriceMin = null): array;

/**
* @return array
Expand Down
281 changes: 176 additions & 105 deletions src/Domains/Registrar/OpenSRS.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,153 +231,224 @@ public function cancelPurchase(): bool
* @param array|string $query Search terms to generate suggestions from
* @param array $tlds Top-level domains to search within (e.g., ['com', 'net', 'org'])
* @param int|null $limit Maximum number of results to return
* @param int|null $priceMax Maximum price for premium domains
* @param int|null $priceMin Minimum price for premium domains
* @param string|null $filterType Filter results by type: 'premium', 'suggestion', or null for both
* @param int|null $premiumPriceMax Maximum price for premium domains
* @param int|null $premiumPriceMin Minimum price for premium domains
* @return array Domains with metadata: `available` (bool), `price` (float|null), `type` (string)
*/
public function suggest(array|string $query, array $tlds = [], int|null $limit = null, int|null $priceMax = null, int|null $priceMin = null): array
public function suggest(array|string $query, array $tlds = [], int|null $limit = null, string|null $filterType = null, int|null $premiumPriceMax = null, int|null $premiumPriceMin = null): array
{
$query = is_array($query) ? $query : [$query];
if ($premiumPriceMin !== null && $premiumPriceMax !== null && $premiumPriceMin >= $premiumPriceMax) {
throw new Exception("Invalid price range: premiumPriceMin ($premiumPriceMin) must be less than premiumPriceMax ($premiumPriceMax).");
}

// Determine which services to use based on parameters
$hasPriceFilter = $priceMax !== null || $priceMin !== null;
if ($filterType !== null && !in_array($filterType, ['premium', 'suggestion'])) {
throw new Exception("Invalid filter type: filterType ($filterType) must be 'premium' or 'suggestion'.");
}

if ($hasPriceFilter) {
$services = ['premium'];
} elseif ($limit) {
$services = ['lookup', 'suggestion'];
} else {
$services = ['suggestion', 'premium', 'lookup'];
if ($filterType !== null && $filterType === 'suggestion' && ($premiumPriceMin !== null || $premiumPriceMax !== null)) {
throw new Exception("Invalid price range: premiumPriceMin ($premiumPriceMin) and premiumPriceMax ($premiumPriceMax) cannot be set when filterType is 'suggestion'.");
}

$query = is_array($query) ? $query : [$query];
$message = [
'object' => 'DOMAIN',
'action' => 'name_suggest',
'attributes' => [
'services' => $services,
'services' => ['suggestion', 'premium', 'lookup'],
'searchstring' => implode(' ', $query),
'skip_registry_lookup' => 1,
],
];
$tlds = !empty($tlds) ? array_map(fn ($tld) => '.' . ltrim($tld, '.'), $tlds) : [];

if (!empty($tlds)) {
$message['attributes']['tlds'] = $tlds;
if ($filterType === 'premium' || $filterType === null) {
$message['attributes']['service_override']['premium']['tlds'] = $tlds;
}
if ($filterType === 'suggestion' || $filterType === null) {
$message['attributes']['service_override']['suggestion']['tlds'] = $tlds;
}
$message['attributes']['service_override']['lookup']['tlds'] = $tlds;
}
if ($limit) {
if ($filterType === 'premium' || $filterType === null) {
$message['attributes']['service_override']['premium']['maximum'] = $limit;
}
if ($filterType === 'suggestion' || $filterType === null) {
$message['attributes']['service_override']['suggestion']['maximum'] = $limit;
}
}
if ($premiumPriceMin !== null) {
$message['attributes']['service_override']['premium']['price_min'] = $premiumPriceMin;
}
if ($premiumPriceMax !== null) {
$message['attributes']['service_override']['premium']['price_max'] = $premiumPriceMax;
}

if ($limit || $hasPriceFilter) {
$formattedTlds = !empty($tlds) ? array_map(fn ($tld) => '.' . ltrim($tld, '.'), $tlds) : [];
$result = $this->send($message);
$result = $this->sanitizeResponse($result);

if ($hasPriceFilter) {
$message['attributes']['service_override']['premium'] = [];
$items = [];

if (!empty($formattedTlds)) {
$message['attributes']['service_override']['premium']['tlds'] = $formattedTlds;
// Process suggestion domains
if ($filterType === 'suggestion' || $filterType === null) {
$suggestionXpath = implode('/', [
'//body',
'data_block',
'dt_assoc',
'item[@key="attributes"]',
'dt_assoc',
'item[@key="suggestion"]',
'dt_assoc',
'item[@key="items"]',
'dt_array',
'item',
]);
$suggestionElements = $result->xpath($suggestionXpath);

$processedCount = 0;
$suggestionLimit = $limit;

foreach ($suggestionElements as $element) {
if ($suggestionLimit !== null && $processedCount >= $suggestionLimit) {
break;
}

if ($limit) {
$message['attributes']['service_override']['premium']['maximum'] = $limit;
}
$domainNode = $element->xpath('dt_assoc/item[@key="domain"]');
$statusNode = $element->xpath('dt_assoc/item[@key="status"] | dt_assoc/item[@key="availability"]');
$domain = isset($domainNode[0]) ? (string) $domainNode[0] : null;
$status = isset($statusNode[0]) ? strtolower((string) $statusNode[0]) : '';
$available = in_array($status, ['available', 'true', '1'], true);

if ($priceMin !== null) {
$message['attributes']['service_override']['premium']['price_min'] = $priceMin;
}
if ($domain) {
$items[$domain] = [
'available' => $available,
'price' => null,
'type' => 'suggestion'
];

if ($priceMax !== null) {
$message['attributes']['service_override']['premium']['price_max'] = $priceMax;
}
} elseif ($limit) {
$message['attributes']['service_override']['suggestion']['maximum'] = $limit;

if (!empty($formattedTlds)) {
$message['attributes']['service_override']['suggestion']['tlds'] = $formattedTlds;
$message['attributes']['service_override']['lookup']['tlds'] = $formattedTlds;
$processedCount++;
}
}
}

$result = $this->send($message);
$result = $this->sanitizeResponse($result);

$items = [];
if ($filterType === 'suggestion') {
return $items;
}

$suggestionXpath = implode('/', [
'//body',
'data_block',
'dt_assoc',
'item[@key="attributes"]',
'dt_assoc',
'item[@key="suggestion"]',
'dt_assoc',
'item[@key="items"]',
'dt_array',
'item',
]);
$suggestionElements = $result->xpath($suggestionXpath);
foreach ($suggestionElements as $element) {
$domainNode = $element->xpath('dt_assoc/item[@key="domain"]');
$statusNode = $element->xpath('dt_assoc/item[@key="status"] | dt_assoc/item[@key="availability"]');
$domain = isset($domainNode[0]) ? (string) $domainNode[0] : null;
$status = isset($statusNode[0]) ? strtolower((string) $statusNode[0]) : '';
if ($domain) {
$items[$domain] = [
'available' => in_array($status, ['available', 'true', '1'], true),
'price' => null,
'type' => 'suggestion'
];
if ($limit && count($items) >= $limit) {
return array_slice($items, 0, $limit, true);
}
}

$premiumXpath = implode('/', [
'//body',
'data_block',
'dt_assoc',
'item[@key="attributes"]',
'dt_assoc',
'item[@key="premium"]',
'dt_assoc',
'item[@key="items"]',
'dt_array',
'item',
]);
// Process premium domains
if (
($filterType === 'premium' || $filterType === null) &&
!($limit && count($items) >= $limit)
) {
$premiumXpath = implode('/', [
'//body',
'data_block',
'dt_assoc',
'item[@key="attributes"]',
'dt_assoc',
'item[@key="premium"]',
'dt_assoc',
'item[@key="items"]',
'dt_array',
'item',
]);
$premiumElements = $result->xpath($premiumXpath);

$remainingLimit = $limit ? ($limit - count($items)) : null;
$processedCount = 0;

foreach ($premiumElements as $element) {
if ($remainingLimit !== null && $processedCount >= $remainingLimit) {
break;
}

$premiumElements = $result->xpath($premiumXpath);

foreach ($premiumElements as $element) {
$item = $element->xpath('dt_assoc/item');

$domain = null;
$available = false;
$price = null;

foreach ($item as $field) {
$key = (string) $field['key'];
$value = (string) $field;

switch ($key) {
case 'domain':
$domain = $value;
break;
case 'status':
$available = $value === 'available';
break;
case 'price':
$price = is_numeric($value) ? (float) $value : null;
break;
$item = $element->xpath('dt_assoc/item');

$domain = null;
$available = false;
$price = null;

foreach ($item as $field) {
$key = (string) $field['key'];
$value = (string) $field;

switch ($key) {
case 'domain':
$domain = $value;
break;
case 'status':
$available = $value === 'available';
break;
case 'price':
$price = is_numeric($value) ? floatval($value) : null;
break;
}
}
}

if ($domain) {
$items[$domain] = [
'available' => $available,
'price' => $price,
'type' => 'premium'
];
if ($domain) {
$items[$domain] = [
'available' => $available,
'price' => $price,
'type' => 'premium'
];

$processedCount++;
}
}
}

return $items;
}

/**
* Get the registration price for a domain
*
* @param string $domain The domain name to get pricing for
* @param int $period Registration period in years (default 1)
* @param string $regType Type of registration: 'new', 'renewal', 'transfer', or 'trade'
* @return array Contains 'price' (float), 'is_registry_premium' (bool), and 'registry_premium_group' (string|null)
*/
public function getPrice(string $domain, int $period = 1, string $regType = 'new'): array
{
$message = [
'object' => 'DOMAIN',
'action' => 'GET_PRICE',
'attributes' => [
'domain' => $domain,
'period' => $period,
'reg_type' => $regType,
],
];

$result = $this->send($message);
$result = $this->sanitizeResponse($result);

$priceXpath = '//body/data_block/dt_assoc/item[@key="attributes"]/dt_assoc/item[@key="price"]';
$priceElements = $result->xpath($priceXpath);
$price = isset($priceElements[0]) ? floatval((string) $priceElements[0]) : null;

$isPremiumXpath = '//body/data_block/dt_assoc/item[@key="attributes"]/dt_assoc/item[@key="is_registry_premium"]';
$isPremiumElements = $result->xpath($isPremiumXpath);
$isRegistryPremium = isset($isPremiumElements[0]) ? ((string) $isPremiumElements[0] === '1') : false;

$premiumGroupXpath = '//body/data_block/dt_assoc/item[@key="attributes"]/dt_assoc/item[@key="registry_premium_group"]';
$premiumGroupElements = $result->xpath($premiumGroupXpath);
$registryPremiumGroup = isset($premiumGroupElements[0]) ? (string) $premiumGroupElements[0] : null;

return [
'price' => $price,
'is_registry_premium' => $isRegistryPremium,
'registry_premium_group' => $registryPremiumGroup,
];
}

public function tlds(): array
{
// OpenSRS offers no endpoint for this
Expand Down
Loading