larva/laravel-whois
A Laravel package for performing WHOIS domain lookups through a clean, expressive API. Retrieve registrar, registration and expiration details, with configurable caching and support for multiple TLDs. Easily integrate domain ownership checks into any Laravel application.
The package is a thin wrapper around a PHP WHOIS client (typically io-developer/php-whois) exposed through a Laravel service provider and facade. Install it and wire it up:
composer require larva/laravel-whois
As of 1.2.9 the package declares support for Laravel 12 (alongside the earlier versions it already covered), so you can install it on a current Laravel app without a composer.json conflict. If you're on Laravel 12 and Composer still resolves an older tag, require it explicitly:
composer require larva/laravel-whois:^1.2.9
If the service provider is not auto-discovered (older Laravel), register Larva\Whois\WhoisServiceProvider in config/app.php and alias the facade. Publish any bundled config if present:
php artisan vendor:publish --provider="Larva\Whois\WhoisServiceProvider"
Where to look first: the README on the repo, then vendor/larva/laravel-whois/src/ to confirm the actual method names — with a handful of stars and 0 dependents, the API surface is small but not widely documented. Skim the facade and the response class before assuming method names. After a Laravel major bump like this one, also glance at the package's composer.json in vendor/ to confirm the illuminate/* constraints actually match your installed framework version.
First use case — a domain availability check:
use Larva\Whois\Whois; // verify namespace in vendor/
$result = Whois::lookup('example.com');
if ($result->isAvailable()) {
// domain looks free
}
Wrap this call in try/catch immediately. WHOIS is a network operation against upstream registries; it fails regularly. Treat the first successful lookup as a smoke test that DNS, socket timeouts, and egress from your environment all work.
Availability check service. Do not call the facade from controllers or Blade. Wrap it in a small application service so you can swap out the package, mock it in tests, and normalize its response:
class DomainLookup
{
public function isAvailable(string $domain): ?bool
{
try {
$r = Whois::lookup($domain);
return $r->isAvailable() ? true : false;
} catch (\Throwable $e) {
report($e);
return null; // "unknown" — don't tell users it's available
}
}
}
The tri-state (true/false/null) matters: "unknown" is the correct answer when the WHOIS server times out or returns a rate-limit page.
Cache aggressively. WHOIS registries throttle and occasionally block IPs. Cache by domain:
return Cache::remember("whois:{$domain}", now()->addHours(6), fn () =>
$this->isAvailable($domain)
);
Six to twelve hours is reasonable for availability; use shorter TTLs (minutes) only when you're racing a user through checkout.
Queue bulk checks. For domain-search UIs ("check 20 TLDs at once") or monitoring jobs, dispatch one job per domain rather than looping in a request:
foreach ($domains as $d) {
CheckDomainJob::dispatch($d)->onQueue('whois');
}
Set a low concurrency on that queue (1–2 workers) so you don't hammer a single registrar. Remember that Laravel 12 defaults to a leaner queue setup — if you're on database queues, make sure the whois queue is actually being consumed by a worker (--queue=whois,default).
Integrate into form validation. A common pattern is a custom rule:
class DomainAvailable implements ValidationRule
{
public function validate(string $attr, mixed $value, \Closure $fail): void
{
if (app(DomainLookup::class)->isAvailable($value) === false) {
$fail('This domain is not available.');
}
}
}
Note the strict === false — unknown results should not block validation, only confirmed-taken ones.
Expiry / registration data. If the response object exposes parsed fields (registrar, created/expiry dates), normalize them into a DTO early. The upstream client parses free-text WHOIS responses, which are inconsistent across TLDs; keeping your own DTO insulates the rest of your app from format drift.
.com, .net, .org are reliable; newer gTLDs (*.app, *.dev) and ccTLDs vary. Test every TLD you actually offer before launching.No match for ... or an empty record for unregistered domains. The parser may or may not catch this. Verify against whois.iana.org output for the specific TLD..com/.net responses has been redacted since 2018; don't build a feature that assumes you can get registrant names.DomainLookup interface) so you can return fake responses in feature tests. Hitting the real network in CI is flaky and slow. If you're validating the Laravel 12 upgrade itself, run an integration test that does one real lookup plus a mocked suite — a version bump can change discovery/binding behaviour even when the wrapper "looks" unchanged.config:cache and package:discover, and confirm the service provider still registers cleanly under Laravel 12's slimmer bootstrap. If you use Laravel's new default app skeleton, there's no config/app.php providers array to fall back on, so auto-discovery must work.^1.2.9) in composer.json and be prepared to fork or replace with direct io-developer/php-whois calls if it goes stale — the wrapper is thin enough that extraction is trivial.whois example.com from your shell is the fastest way to tell whether the bug is in your code, the parser, or the registrar's response.How can I help you explore Laravel packages today?