Domain registrar-expiry reader for Laravel — RDAP first with a port-43 WHOIS fallback. Ask it for a domain and it returns the registrar expiry date as a Carbon instance; the package stores nothing and keeps no state between lookups.
composer require labrodev/laravel-domain-keeperPublish the config file:
php artisan vendor:publish --tag=domain-keeper-configAll environment variables are optional — the defaults work out of the box:
DOMAIN_KEEPER_RDAP_URL=https://rdap.org/domain/
DOMAIN_KEEPER_WHOIS_SERVER=whois.iana.org
DOMAIN_KEEPER_TIMEOUT=10Type-hint the RegistrarExpiryReader contract — the service provider binds it to the RDAP-first implementation:
use Labrodev\DomainKeeper\Contracts\RegistrarExpiryReader;
use Labrodev\DomainKeeper\Exceptions\RegistrarExpiryNotFound;
class DomainExpiryCheckController
{
public function __invoke(RegistrarExpiryReader $registrarExpiryReader)
{
try {
$expiry = $registrarExpiryReader->expiryDate('example.com');
} catch (RegistrarExpiryNotFound $registrarExpiryNotFound) {
// Neither RDAP nor WHOIS produced a date; the message names the domain.
}
return $expiry->toDateString();
}
}- RDAP — an HTTPS GET to
{rdap_url}{domain}(defaulthttps://rdap.org/domain/, which redirects to the authoritative registry RDAP server). The reader picks theexpirationevent out of the RDAPeventsarray. Plain HTTPS, so it works inside containers with no raw-socket access. - IANA WHOIS referral — when RDAP yields nothing, the domain is queried against the bootstrap WHOIS server (
whois.iana.orgby default) and thewhois:referral line names the TLD's authoritative WHOIS server. - Registrar WHOIS — the referral server is queried for the domain and the expiry line is parsed. The label must open a line and be one of
Registry Expiry Date,Registrar Registration Expiration Date,Expiration Date,Expiration Time,Expiry Date,Renewal date,paid-till,expiresorexpire(case-insensitive); the date is then located within the rest of the line by shape, which covers ISO timestamps (2027-04-14T04:00:00Z), alphabetic months in either order (13-Dec-2034,April 14, 2027), dotted and slashed dates year-first or day-first (2027.04.14,14.4.2027,2027/04/14) and the compact form (20270414). Anything around the date — a weekday prefix, a trailing(yyyy-mm-dd)annotation — is ignored; where a line carries more than one date the earliest one wins, and a line carrying no recognisable date yields nothing rather than a guess.
Every failure along the way falls through silently to the next step; only when all three sources come up empty does the reader throw RegistrarExpiryNotFound.
The raw port-43 socket lives in Labrodev\DomainKeeper\Services\WhoisClient, which the reader receives via constructor injection — swap it through the container to keep your suite off the network:
use Labrodev\DomainKeeper\Services\WhoisClient;
app()->instance(WhoisClient::class, new readonly class extends WhoisClient
{
public function query(string $server, string $domain): ?string
{
return "Registry Expiry Date: 2030-01-01T00:00:00Z\n";
}
});composer test # pest
composer phpstan # larastan, level 7
composer pint # laravel preset
composer check # all of the aboveMIT. See LICENSE.md.