Skip to content

Commit d5f903e

Browse files
authored
Merge pull request #43 from utopia-php/fix-opensrs-issues
chore: update suggestions method to return prices
2 parents 650463d + ef93e8e commit d5f903e

File tree

6 files changed

+253
-52
lines changed

6 files changed

+253
-52
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ $domain = 'yourname.com';
118118

119119
$available = $reg->available($domain);
120120
$purchase = $reg->purchase($domain, $contact);
121-
$suggest = $reg->suggest(['yourname', 'yourname1.com'], ['com', 'net', 'org']);
121+
$suggest = $reg->suggest(['yourname', 'yourname1.com'], ['com', 'net', 'org'], 10, 10000, 100);
122122
$domainDetails = $reg->getDomain($domain);
123123
$renew = $reg->renew($domain, 1);
124124
$transfer = $reg->transfer($domain, [$contact]);
@@ -128,7 +128,7 @@ $transfer = $reg->transfer($domain, [$contact]);
128128
## Library Registrar API
129129
* **available(string $domain): bool** - Checks to see if a domain is available for registration.
130130
* **purchase(string $domain, array $contacts, array $nameservers = []): array** - Purchase a domain name.
131-
* **suggest(array $query, array $tlds = [], $minLength = 1, $maxLength = 100): array** - Suggest or search for domain names.
131+
* **suggest(array $query, array $tlds = [], int|null $limit = null, int|null $priceMax = null, int|null $priceMin = null): array** - Suggest or search for domain names.
132132
* **getDomain(string $domain): array** - Get domain details.
133133
* **renew(string $domain, int $years): array** - Renew a domain name.
134134
* **transfer(string $domain, array $contacts, array $nameservers = []): array** - Transfer a domain name.

composer.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,11 @@
3232
"laravel/pint": "1.2.*",
3333
"phpstan/phpstan": "1.9.x-dev"
3434
},
35-
"minimum-stability": "stable"
35+
"minimum-stability": "stable",
36+
"config": {
37+
"allow-plugins": {
38+
"php-http/discovery": true,
39+
"tbachert/spi": true
40+
}
41+
}
3642
}

src/Domains/Registrar.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ public function purchase(string $domain, array $contacts, array $nameservers = [
2323
return $this->adapter->purchase($domain, $contacts, $nameservers);
2424
}
2525

26-
public function suggest(array $query, array $tlds = [], $minLength = 1, $maxLength = 100): array
26+
public function suggest(array $query, array $tlds = []): array
2727
{
28-
return $this->adapter->suggest($query, $tlds, $minLength, $maxLength);
28+
return $this->adapter->suggest($query, $tlds);
2929
}
3030

3131
public function tlds(): array

src/Domains/Registrar/Adapter.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ abstract public function purchase(string $domain, array $contacts, array $namese
2323
/**
2424
* @param array $query
2525
* @param array $tlds
26-
* @param int $minLength
27-
* @param int $maxLength
26+
* @param int|null $limit
27+
* @param int|null $priceMax
28+
* @param int|null $priceMin
2829
* @return array
2930
*/
30-
abstract public function suggest(array|string $query, array $tlds = [], $minLength = 1, $maxLength = 100): array;
31+
abstract public function suggest(array|string $query, array $tlds = [], int|null $limit = null, int|null $priceMax = null, int|null $priceMin = null): array;
3132

3233
/**
3334
* @return array

src/Domains/Registrar/OpenSRS.php

Lines changed: 170 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,20 @@ class OpenSRS extends Adapter
1616
* Instantiate a new adapter.
1717
*
1818
* @param string $apiKey
19-
* @param string $apiSecret
2019
* @param string $username
2120
* @param string $password
2221
* @param array $defaultNameservers
2322
* @param bool $production
2423
* @return void
2524
*/
26-
public function __construct(string $apiKey, string $apiSecret, string $username, string $password, array $defaultNameservers, bool $production = false)
25+
public function __construct(string $apiKey, string $username, string $password, array $defaultNameservers, bool $production = false)
2726
{
2827
$this->endpoint =
2928
$production === false
3029
? 'https://horizon.opensrs.net:55443'
3130
: 'https://rr-n1-tor.opensrs.net:55443';
3231

3332
$this->apiKey = $apiKey;
34-
$this->apiSecret = $apiSecret;
3533
$this->defaultNameservers = $defaultNameservers;
3634

3735
$this->user = [
@@ -41,7 +39,7 @@ public function __construct(string $apiKey, string $apiSecret, string $username,
4139

4240
$this->headers = [
4341
'Content-Type:text/xml',
44-
'X-Username:'.$this->apiSecret,
42+
'X-Username: ' . $username,
4543
];
4644
}
4745

@@ -227,23 +225,82 @@ public function cancelPurchase(): bool
227225
return $successful;
228226
}
229227

230-
public function suggest(array|string $query, array $tlds = [], $minLength = 1, $maxLength = 100): array
228+
/**
229+
* Suggest domain names based on search query
230+
*
231+
* @param array|string $query Search terms to generate suggestions from
232+
* @param array $tlds Top-level domains to search within (e.g., ['com', 'net', 'org'])
233+
* @param int|null $limit Maximum number of results to return
234+
* @param int|null $priceMax Maximum price for premium domains
235+
* @param int|null $priceMin Minimum price for premium domains
236+
* @return array Domains with metadata: `available` (bool), `price` (float|null), `type` (string)
237+
*/
238+
public function suggest(array|string $query, array $tlds = [], int|null $limit = null, int|null $priceMax = null, int|null $priceMin = null): array
231239
{
232240
$query = is_array($query) ? $query : [$query];
233241

242+
// Determine which services to use based on parameters
243+
$hasPriceFilter = $priceMax !== null || $priceMin !== null;
244+
245+
if ($hasPriceFilter) {
246+
$services = ['premium'];
247+
} elseif ($limit) {
248+
$services = ['lookup', 'suggestion'];
249+
} else {
250+
$services = ['suggestion', 'premium', 'lookup'];
251+
}
252+
234253
$message = [
235254
'object' => 'DOMAIN',
236255
'action' => 'name_suggest',
237256
'attributes' => [
238-
'services' => [
239-
'suggestion',
240-
],
257+
'services' => $services,
241258
'searchstring' => implode(' ', $query),
242-
'tlds' => $tlds,
259+
'skip_registry_lookup' => 1,
243260
],
244261
];
245262

246-
$xpath = implode('/', [
263+
if (!empty($tlds)) {
264+
$message['attributes']['tlds'] = $tlds;
265+
}
266+
267+
if ($limit || $hasPriceFilter) {
268+
$formattedTlds = !empty($tlds) ? array_map(fn ($tld) => '.' . ltrim($tld, '.'), $tlds) : [];
269+
270+
if ($hasPriceFilter) {
271+
$message['attributes']['service_override']['premium'] = [];
272+
273+
if (!empty($formattedTlds)) {
274+
$message['attributes']['service_override']['premium']['tlds'] = $formattedTlds;
275+
}
276+
277+
if ($limit) {
278+
$message['attributes']['service_override']['premium']['maximum'] = $limit;
279+
}
280+
281+
if ($priceMin !== null) {
282+
$message['attributes']['service_override']['premium']['price_min'] = $priceMin;
283+
}
284+
285+
if ($priceMax !== null) {
286+
$message['attributes']['service_override']['premium']['price_max'] = $priceMax;
287+
}
288+
} elseif ($limit) {
289+
$message['attributes']['service_override']['suggestion']['maximum'] = $limit;
290+
291+
if (!empty($formattedTlds)) {
292+
$message['attributes']['service_override']['suggestion']['tlds'] = $formattedTlds;
293+
$message['attributes']['service_override']['lookup']['tlds'] = $formattedTlds;
294+
}
295+
}
296+
}
297+
298+
$result = $this->send($message);
299+
$result = $this->sanitizeResponse($result);
300+
301+
$items = [];
302+
303+
$suggestionXpath = implode('/', [
247304
'//body',
248305
'data_block',
249306
'dt_assoc',
@@ -255,19 +312,67 @@ public function suggest(array|string $query, array $tlds = [], $minLength = 1, $
255312
'dt_array',
256313
'item',
257314
]);
315+
$suggestionElements = $result->xpath($suggestionXpath);
316+
foreach ($suggestionElements as $element) {
317+
$domainNode = $element->xpath('dt_assoc/item[@key="domain"]');
318+
$statusNode = $element->xpath('dt_assoc/item[@key="status"] | dt_assoc/item[@key="availability"]');
319+
$domain = isset($domainNode[0]) ? (string) $domainNode[0] : null;
320+
$status = isset($statusNode[0]) ? strtolower((string) $statusNode[0]) : '';
321+
if ($domain) {
322+
$items[$domain] = [
323+
'available' => in_array($status, ['available', 'true', '1'], true),
324+
'price' => null,
325+
'type' => 'suggestion'
326+
];
327+
}
328+
}
258329

259-
$result = $this->send($message);
260-
$result = $this->sanitizeResponse($result);
261-
$elements = $result->xpath($xpath);
330+
$premiumXpath = implode('/', [
331+
'//body',
332+
'data_block',
333+
'dt_assoc',
334+
'item[@key="attributes"]',
335+
'dt_assoc',
336+
'item[@key="premium"]',
337+
'dt_assoc',
338+
'item[@key="items"]',
339+
'dt_array',
340+
'item',
341+
]);
262342

263-
$items = [];
343+
$premiumElements = $result->xpath($premiumXpath);
264344

265-
foreach ($elements as $element) {
345+
foreach ($premiumElements as $element) {
266346
$item = $element->xpath('dt_assoc/item');
267-
$domain = (string) $item[0];
268-
$available = (string) $item[1] === 'available';
269347

270-
$items[$domain] = $available;
348+
$domain = null;
349+
$available = false;
350+
$price = null;
351+
352+
foreach ($item as $field) {
353+
$key = (string) $field['key'];
354+
$value = (string) $field;
355+
356+
switch ($key) {
357+
case 'domain':
358+
$domain = $value;
359+
break;
360+
case 'status':
361+
$available = $value === 'available';
362+
break;
363+
case 'price':
364+
$price = is_numeric($value) ? (float) $value : null;
365+
break;
366+
}
367+
}
368+
369+
if ($domain) {
370+
$items[$domain] = [
371+
'available' => $available,
372+
'price' => $price,
373+
'type' => 'premium'
374+
];
375+
}
271376
}
272377

273378
return $items;
@@ -284,8 +389,8 @@ public function getDomain(string $domain): array
284389
$message = [
285390
'object' => 'domain',
286391
'action' => 'get',
392+
'domain' => $domain,
287393
'attributes' => [
288-
'domain' => $domain,
289394
'type' => 'all_info',
290395
'clean_ca_subset' => 1,
291396
],
@@ -437,6 +542,48 @@ private function createArray(string $key, array $ary): string
437542
return implode(PHP_EOL, $result);
438543
}
439544

545+
private function createAssoc(string $key, array $assoc): string
546+
{
547+
$result = [
548+
'<item key="'.$key.'">',
549+
'<dt_assoc>',
550+
];
551+
552+
foreach ($assoc as $itemKey => $itemValue) {
553+
if (is_array($itemValue)) {
554+
if (array_keys($itemValue) === range(0, count($itemValue) - 1)) {
555+
$result[] = $this->createArray($itemKey, $itemValue);
556+
} else {
557+
$result[] = $this->createAssoc($itemKey, $itemValue);
558+
}
559+
} else {
560+
$result[] = $this->createEnvelopItem($itemKey, $itemValue);
561+
}
562+
}
563+
564+
$result[] = '</dt_assoc>';
565+
$result[] = '</item>';
566+
567+
return implode(PHP_EOL, $result);
568+
}
569+
570+
private function createServiceOverride(array $overrides): string
571+
{
572+
$result = [
573+
'<item key="service_override">',
574+
'<dt_assoc>',
575+
];
576+
577+
foreach ($overrides as $serviceName => $serviceConfig) {
578+
$result[] = $this->createAssoc($serviceName, $serviceConfig);
579+
}
580+
581+
$result[] = '</dt_assoc>';
582+
$result[] = '</item>';
583+
584+
return implode(PHP_EOL, $result);
585+
}
586+
440587
private function createEnvelopItem(string $key, string|int|array $value): string
441588
{
442589
if (is_array($value)) {
@@ -567,7 +714,7 @@ private function createNamespaceAssign(array $nameservers): string
567714
return implode(PHP_EOL, $result);
568715
}
569716

570-
private function buildEnvelop(string $object, string $action, array $attributes, string $domain = null): string
717+
private function buildEnvelop(string $object, string $action, array $attributes, ?string $domain = null): string
571718
{
572719
$result = [
573720
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>',
@@ -604,6 +751,9 @@ private function buildEnvelop(string $object, string $action, array $attributes,
604751
case 'remove_ns':
605752
$result[] = $this->createNamespaceAssign($value);
606753
break;
754+
case 'service_override':
755+
$result[] = $this->createServiceOverride($value);
756+
break;
607757
default:
608758
$result[] =
609759
is_array($value)

0 commit comments

Comments
 (0)