david-garcia/phpwhois
PHP WHOIS lookup library for querying domain registration data across TLDs. Parse registrar, creation and expiry dates, nameservers, and status directly from WHOIS servers using a simple PHP API.
Install via Composer (the package is not on Packagist under a widely-used name, so you may need a VCS repository entry pointing at https://github.com/DavidGarciaCat/phpWhois):
composer require david-garcia/phpwhois
Instantiate the Whois class and call lookup() with a domain name. The result is an array (or false on total failure) containing two things you will spend all your time with:
use phpWhois\Whois;
$whois = new Whois();
$result = $whois->lookup('example.com');
// $result['rawdata'] – array of raw response lines from the registry
// $result['regrinfo'] – parsed, structured data (may be empty)
// $result['regyinfo'] – which registry/handler answered
Look first at rawdata, not regrinfo. The parser is best-effort: for well-known gTLDs (.com, .net, .org) and a handful of ccTLDs it produces a clean regrinfo tree; for everything else it may return an empty regrinfo while rawdata is perfectly usable. Debugging every "it returned nothing" issue starts with dumping rawdata.
The canonical first use case is a domain-availability / registration check:
$result = $whois->lookup($domain);
if ($result === false) {
// network failure, timeout, or unresolvable TLD
$errors = $whois->getErrors();
} elseif (($result['regrinfo']['registered'] ?? 'no') === 'yes') {
// taken
} else {
// likely available
}
$whois->getError() / $whois->getErrors() are the only error channel — lookup() does not throw. Wrap calls in your own guard.
Raw client for "just give me the text". If you only need the WHOIS payload (to store, diff, or parse yourself), use WhoisClient directly and skip the fragile parser:
use phpWhois\WhoisClient;
$client = new WhoisClient();
$client->deepWhois = false;
$raw = $client->query($domain, $server); // server from IANA or your own map
This is also the escape hatch when regrinfo is empty for an obscure TLD.
Reading parsed fields defensively. regrinfo has a stable-ish shape for supported TLDs but nested keys vary by registry:
$info = $result['regrinfo'] ?? [];
$registered = ($info['registered'] ?? 'no') === 'yes';
$created = $info['domain']['created'] ?? null; // often "2020-01-01"
$expires = $info['domain']['expires'] ?? null;
$registrar = $info['domain']['registrar']['name'] ?? null;
$ns = $info['domain']['nserver'] ?? []; // assoc: host => ip
$status = $info['domain']['status'] ?? null;
Always use ?? — the library predates strict typing and will happily omit keys. Contact blocks (owner, admin, tech) are increasingly redacted or absent post-GDPR, so treat registrant data as optional.
Wrapping it in a service. Do not instantiate Whois inline in controllers. Bind a small service in your Laravel container and inject it:
// AppServiceProvider
$this->app->singleton(DomainLookup::class, fn () => new DomainLookup(new Whois()));
Inside the service, set config once and reuse:
$whois->setConfig('timeout', 10);
$whois->setConfig('deep_scan', false); // true = follow registrar referrals, extra network calls
Always cache, always queue. WHOIS servers rate-limit aggressively (and will blacklist your IP). Two hard rules:
dispatch(new LookupDomain($domain))), never in a web request.Cache::remember("whois:{$domain}", now()->addHours(12), ...).Also throttle: RateLimiter::attempt("whois:{$tld}", 5, fn () => ..., 60).
Custom TLD handlers. The library selects a handler from src/phpWhois/Handler/ based on the TLD. If a registry changes its output format you can register your own handler class implementing the Handler interface and point the Whois instance at it via setHandler(). This is the intended extension point — subclass a similar handler and override parse() rather than patching vendor code.
Integration tip: expose availability as a boolean via a value object rather than leaking the ['rawdata' => ..., ...] array shape into your domain layer. The array format is legacy and will change if you ever swap libraries.
composer.json, and expect to vendor/fork it if PHP breaks it.{$str[0]}-style offset access. Run your test suite on the exact PHP version you deploy to; if you see notices, suppress them locally or fork and patch.lookup() returning false vs empty regrinfo. These are different failures. false = couldn't reach a server; empty regrinfo = reached a server but couldn't parse. Log getErrors() and rawdata respectively — the distinction matters when triaging.registered is a string, not a bool. It is 'yes'/'no' (and sometimes absent). Never do if ($info['registered']) — 'no' is truthy.deep_scan is expensive. It follows registrar and registry referrals, multiplying network calls. Leave it false unless you specifically need thick WHOIS data; it dramatically increases your chance of being rate-limited.REDACTED FOR PRIVACY or absent. Do not build features that assume you can recover owner contact details for gTLDs.2020-01-01, 01-Jan-2020, timestamps). Normalise with Carbon::parse() inside a try and store your own canonical field; don't trust the parser to hand you a consistent format.Whois instance. If you reuse one instance across multiple lookups, call getErrors() and clear/handle them per lookup, or instantiate fresh per domain.composer.json's autoload section and run composer dump-autoload. Some forks of this lineage have inconsistent namespaces (phpWhois\ vs PhpWhois\); if use phpWhois\Whois fails, inspect the actual directory casing on disk (case-sensitive filesystems will bite you).rawdata responses for the TLDs you care about and unit-test your parsing/availability logic against them, rather than hitting live WHOIS in CI (slow, flaky, and against most registries' terms).How can I help you explore Laravel packages today?