PHPackages                             klkvsk/whoeasy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. klkvsk/whoeasy

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

klkvsk/whoeasy
==============

Easily lookup domain names, IP addresses and AS numbers by WHOIS and RDAP.

v2.0.2(1mo ago)0363Apache-2.0PHPPHP &gt;=8.2

Since Oct 30Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/klkvsk/whoeasy)[ Packagist](https://packagist.org/packages/klkvsk/whoeasy)[ Docs](https://github.com/klkvsk/whoeasy)[ RSS](/packages/klkvsk-whoeasy/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (3)Versions (7)Used By (0)

Whoeasy
=======

[](#whoeasy)

[![README-logo.png](README-logo.png)](README-logo.png)[![Packagist Version](https://camo.githubusercontent.com/7850b181a5ca7c2230bbea8802ca2cef38fb7cf2a9c0a2fcf07adf81bfa5d839/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6b6c6b76736b2f77686f65617379)](https://packagist.org/packages/klkvsk/whoeasy)[![PHP Version](https://camo.githubusercontent.com/a65590f6818cbb339b660a7cab018345f03bc4705768bf7e89ed863816a86d7e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f6b6c6b76736b2f77686f656173792f706870)](https://packagist.org/packages/klkvsk/whoeasy)[![License](https://camo.githubusercontent.com/ee036fe2dfe1314e2e1dcf1fb42714f2280a7510ede37164f148b908f51477af/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6b6c6b76736b2f77686f65617379)](https://github.com/klkvsk/whoeasy/blob/master/LICENSE)

Smart WHOIS + RDAP client and parser for PHP. Lookup domain names, IP addresses and AS numbers. Get structured, typed results from both WHOIS (RFC 3912) and RDAP (RFC 7480/9083) protocols with automatic server discovery, referral following, and result merging.

Features
--------

[](#features)

- **Dual protocol** — queries both WHOIS and RDAP, merges results for maximum coverage
- **Auto-detection** — recognizes domains, IPv4/IPv6 addresses, and AS numbers from input
- **Typed results** — readonly `DomainInfo`, `IpInfo`, `AsnInfo` value objects with `toArray()` support
- **Referral following** — automatically follows WHOIS referrals and RDAP redirects
- **5 query modes** — prefer RDAP, prefer WHOIS, RDAP-only, WHOIS-only, or both
- **Proxy support** — SOCKS5 and HTTP proxies via curl
- **CLI tool** — `vendor/bin/whoeasy` for quick lookups with JSON output
- **No runtime network I/O for server discovery** — pre-generated registry from IANA/RIR sources

Installation
------------

[](#installation)

```
composer require klkvsk/whoeasy
```

**Requirements:** PHP 8.2+, ext-mbstring

**Recommended:** ext-curl (required for RDAP; WHOIS falls back to sockets without it)

Quick Start
-----------

[](#quick-start)

```
use Klkvsk\Whoeasy\Whoeasy;

$whoeasy = Whoeasy::create();

// Domain lookup
$result = $whoeasy->domain('example.com');
echo $result->info->registrar->name;       // e.g. "RESERVED-Internet Assigned Numbers Authority"
echo $result->info->expiresDate;           // e.g. "2025-08-13 04:00:00"
echo $result->info->nameservers[0]->hostname; // e.g. "a.iana-servers.net"

// IP lookup
$result = $whoeasy->ip('8.8.8.8');
echo $result->info->networkName;  // e.g. "GOGL"
echo $result->info->country;     // e.g. "US"

// ASN lookup
$result = $whoeasy->asn('AS15169');
echo $result->info->name;         // e.g. "GOOGLE"
echo $result->info->description;  // e.g. "Google LLC"

// Auto-detect query type
$result = $whoeasy->query('example.com');
```

Error Handling
--------------

[](#error-handling)

Whoeasy never throws on empty results. Errors are attached to individual protocol hops.

```
$result = $whoeasy->query('nonexistent-domain.example');

if ($result->isNotFound()) {
    echo "Domain not found";
}

if ($result->info === null && $result->hasRetryableErrors()) {
    echo "Temporary error (rate limit, timeout) — retry later";
}

if ($result->info === null) {
    // Inspect hop-level errors for diagnostics
    foreach ($result->whois?->hops ?? [] as $hop) {
        if ($hop->error) {
            echo "WHOIS error: " . $hop->error->getMessage();
        }
    }
}
```

Query Modes
-----------

[](#query-modes)

ModeEnum valueBehavior**Prefer RDAP** (default)`QueryMode::PreferRdap`Try RDAP first, fall back to WHOIS on failure**Prefer WHOIS**`QueryMode::PreferWhois`Try WHOIS first, fall back to RDAP on failure**RDAP only**`QueryMode::RdapOnly`RDAP only, no WHOIS fallback**WHOIS only**`QueryMode::WhoisOnly`WHOIS only, no RDAP**Both**`QueryMode::Both`Query both protocols, merge results (RDAP priority)```
use Klkvsk\Whoeasy\QueryMode;
use Klkvsk\Whoeasy\QueryOptions;

$result = $whoeasy->domain('example.com', new QueryOptions(
    mode: QueryMode::Both,
));
```

Options
-------

[](#options)

`QueryOptions` is a readonly class — pass it to `Whoeasy::create()` as defaults or per-query.

FieldTypeDefaultDescription`mode``QueryMode``PreferRdap`Protocol selection strategy`proxyUri``?string``null`SOCKS5 or HTTP proxy URI`whoisTimeout``int``15`WHOIS request timeout in seconds`rdapTimeout``int``15`RDAP request timeout in seconds`recursive``bool``true`Follow referrals to authoritative servers`maxReferrals``int``3`Maximum referral hops to follow```
// Set defaults for all queries
$whoeasy = Whoeasy::create(new QueryOptions(
    proxyUri: 'socks5://127.0.0.1:1080',
    whoisTimeout: 10,
    mode: QueryMode::WhoisOnly,
));

// Override per query
$result = $whoeasy->domain('example.com', new QueryOptions(
    mode: QueryMode::Both,
));
```

Result Structure
----------------

[](#result-structure)

All info classes are `readonly` with typed properties and a `toArray()` method.

**DomainInfo** — `name`, `registrar` (Registrar), `createdDate`, `updatedDate`, `expiresDate`, `status[]`, `nameservers[]` (Nameserver), `contacts[]` (Contact), `dnssec`

**IpInfo** — `range`, `networkName`, `description`, `asNumber`, `country`, `createdDate`, `updatedDate`, `status[]`, `contacts[]` (Contact)

**AsnInfo** — `asn`, `name`, `description`, `country`, `createdDate`, `updatedDate`, `status[]`, `contacts[]` (Contact)

**Registrar** — `name`, `ianaId`, `url`, `abuseEmail`, `abusePhone`

**Nameserver** — `hostname`, `ipv4`, `ipv6`

**Contact** — `type` (ContactType enum: Registrant/Admin/Tech/Abuse), `name`, `organization`, `email`, `phone`, `fax`, `address`

All dates are formatted as `"Y-m-d H:i:s"`. Nullable fields return `null` when data is unavailable.

```
// Convert to array for serialization
$array = $result->info->toArray();
$json = json_encode($result->toArray(), JSON_PRETTY_PRINT);
```

Accessing Raw Data
------------------

[](#accessing-raw-data)

`QueryResult` contains protocol-level details through hops:

```
$result = $whoeasy->domain('example.com', new QueryOptions(
    mode: QueryMode::Both,
));

// Raw WHOIS text from each server in the referral chain
foreach ($result->whois?->hops ?? [] as $hop) {
    echo "Server: {$hop->server}\n";
    echo $hop->response . "\n\n";
}

// Raw RDAP JSON
foreach ($result->rdap?->hops ?? [] as $hop) {
    echo "URL: {$hop->url}\n";
    echo $hop->response . "\n";    // raw JSON string
    print_r($hop->json);           // decoded JSON array
}
```

CLI
---

[](#cli)

```
vendor/bin/whoeasy [options]

```

OptionDescription`-m, --mode `Query mode: `prefer-rdap` (default), `prefer-whois`, `rdap-only`, `whois-only`, `both``-p, --proxy `Proxy address (SOCKS5 or HTTP)`-r, --recursive`Follow referrals (default)`--no-recursive`Do not follow referrals`-F, --full`Output full result with hops (default: info only)`-v, --verbose`Show info-level log output`-vv, --debug`Show debug-level log output (includes raw responses)`-h, --help`Show help message```
# Domain lookup (JSON output)
vendor/bin/whoeasy example.com

# IP lookup with WHOIS only
vendor/bin/whoeasy -m whois-only 8.8.8.8

# Full result with all hops through a proxy
vendor/bin/whoeasy --full -p socks5://127.0.0.1:1080 AS15169
```

You can also run whoeasy without installing it as a dependency using [cpx](https://github.com/php-cpx/cpx):

```
cpx klkvsk/whoeasy example.com
```

What's New in v2
----------------

[](#whats-new-in-v2)

- **RDAP support** — full RFC 9083 parsing for domains, IPs, and AS numbers
- **Dual-protocol merging** — 5 query modes with intelligent result merging (RDAP priority for scalars, combined+deduplicated arrays)
- **New entry point** — `Whoeasy::create()` replaces the old `Whois` class
- **Typed result objects** — readonly `DomainInfo`, `IpInfo`, `AsnInfo` with typed properties instead of generic arrays
- **Referral chain visibility** — full hop history with raw data and per-hop errors
- **Universal WHOIS parser** — single parser handles all known TLDs via field normalization and RPSL object parsing; replaces Novutec template system
- **Error model overhaul** — never throws on empty results; `isNotFound()`, `hasRetryableErrors()` inspection methods; `RetryableException` marker interface
- **CLI rewrite** — JSON output, `--mode`, `--full`, `--proxy`, `--no-recursive` options

Data Sources
------------

[](#data-sources)

Server registry data is generated at build time from:

- [IANA bootstrap registries](https://www.iana.org/) — RDAP server assignments for domains, IPv4, IPv6, ASN
- [rfc1036/whois](https://github.com/rfc1036/whois) — WHOIS server assignments (same source as the Linux `whois` tool)
- [whoisserver.world](https://whoisserver-world.org/) — WHOIS/RDAP server assignments and sample domains per TLD
- [resolve.rs](https://resolve.rs/) — supplementary TLD coverage

The generated PHP arrays are stored in `src/Registry/Data/` and loaded via opcache with no runtime network I/O for server discovery.

Testing
-------

[](#testing)

The parser is tested against stored fixtures — raw WHOIS/RDAP responses paired with `.expected.json` files containing the expected parsed output.

```
vendor/bin/phpunit
vendor/bin/phpstan analyse
```

- 260+ WHOIS fixtures across domains, IPs, and ASNs
- 35+ RDAP fixtures for domains, IPs, and ASNs
- Expected outputs validated against raw data
- PHPStan at level max for static analysis

Contributing
------------

[](#contributing)

Issues and pull requests welcome at [github.com/klkvsk/whoeasy](https://github.com/klkvsk/whoeasy).

**Fixture workflow:**

1. Add raw WHOIS/RDAP response files to `tests/Fixture/`
2. Generate `.expected.json` sidecar files (use the `/generate-expected` Claude skill)
3. Run `vendor/bin/phpunit` to verify parsing
4. If tests fail, use the `/develop-parser` Claude skill to iteratively fix the parser

License
-------

[](#license)

Apache-2.0 — see [LICENSE](LICENSE) for details.

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance90

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~174 days

Recently: every ~218 days

Total

6

Last Release

53d ago

Major Versions

0.1.0 → v2.0.02026-03-19

PHP version history (2 changes)0.0.2PHP &gt;=8.1

v2.0.0PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/e8c39df33c0429a10cb82630098652f9761d78b94244d749b9b3aa2627865249?d=identicon)[klkvsk](/maintainers/klkvsk)

---

Top Contributors

[![klkvsk](https://avatars.githubusercontent.com/u/1466771?v=4)](https://github.com/klkvsk "klkvsk (180 commits)")

---

Tags

dnsdomainsparserphpwhoiswhois-lookupwhois-parserphpwhoiswhoisparserrdap

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/klkvsk-whoeasy/health.svg)

```
[![Health](https://phpackages.com/badges/klkvsk-whoeasy/health.svg)](https://phpackages.com/packages/klkvsk-whoeasy)
```

###  Alternatives

[goetas-webservices/xsd2php

Convert XSD (XML Schema) definitions into PHP classes and JMS metadata

2411.6M37](/packages/goetas-webservices-xsd2php)[antlr/antlr4-php-runtime

PHP 8.0+ runtime for ANTLR 4

96636.7k13](/packages/antlr-antlr4-php-runtime)[novutec/whoisparser

Lookup domain names, IP addresses and AS numbers by WHOIS.

392.0M1](/packages/novutec-whoisparser)[matecat/xliff-parser

A Xliff parser written in PHP

14127.5k](/packages/matecat-xliff-parser)[bueltge/marksimple

A simple Markdown parser for PHP.

146.9k](/packages/bueltge-marksimple)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
