PHPackages                             chieftools/dns-resolver - 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. chieftools/dns-resolver

ActiveLibrary

chieftools/dns-resolver
=======================

Recursive DNS resolver with DNSSEC validation

v1.0.6(1mo ago)148↑2400%Apache-2.0PHPPHP ^8.4CI passing

Since Apr 5Pushed 1mo agoCompare

[ Source](https://github.com/chieftools/dns-resolver)[ Packagist](https://packagist.org/packages/chieftools/dns-resolver)[ GitHub Sponsors](https://github.com/stayallive)[ RSS](/packages/chieftools-dns-resolver/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (8)Used By (0)

DNS Resolver
============

[](#dns-resolver)

A recursive DNS resolver for PHP with full DNSSEC validation. Performs iterative resolution from the root servers down, just like a real resolver — no reliance on the system stub resolver.

The intention of this package is to be a full non-caching resolver implementation. This allows stable and consistent results. It is not designed for latency critical applications or high query volumes. For those use cases, consider using a (local) caching resolver instead.

If you want to see the resolver in action, check out [dns.chief.tools](https://dns.chief.tools?ref=gh-package) — a free online DNS lookup tool built with this package.

Requirements
------------

[](#requirements)

- PHP 8.4+
- ext-openssl

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

[](#installation)

```
composer require chieftools/dns-resolver
```

Quick start
-----------

[](#quick-start)

```
use ChiefTools\DNS\Resolver\Resolver;
use ChiefTools\DNS\Resolver\Enums\LookupStatus;

$result = new Resolver()->resolve('example.com', 'A');

if ($result->status === LookupStatus::SUCCESS) {
    foreach ($result->records as $record) {
        echo "{$record->name} {$record->ttl} {$record->type->value} {$record->data}\n";
    }
}
```

Interpreting results
--------------------

[](#interpreting-results)

`LookupResult::status` is the machine-readable outcome of the lookup. `info` is only a human-readable message for non-success cases.

```
use ChiefTools\DNS\Resolver\Resolver;
use ChiefTools\DNS\Resolver\Enums\LookupStatus;

$result = new Resolver()->resolve('example.com', 'A');

if ($result->status === LookupStatus::NXDOMAIN) {
    echo "The domain does not exist.\n";
} elseif ($result->status === LookupStatus::QUERY_FAILED) {
    echo "The lookup failed and may succeed on retry.\n";
} elseif ($result->status === LookupStatus::NO_RECORDS) {
    echo "The domain exists, but no records were found for that type.\n";
}

if ($result->isNxdomain()) {
    // Convenience helper for NXDOMAIN checks
}

if ($result->isLookupFailed()) {
    // Convenience helper for transport / nameserver failure checks
}

foreach ($result->records as $record) {
    echo $record->validation->name . "\n"; // SIGNED, FAILED, or UNKNOWN
}
```

Querying multiple types
-----------------------

[](#querying-multiple-types)

Pass an array of types to resolve them in a single call. The resolver queries the authoritative server for each type once it reaches it, avoiding redundant delegation walks.

```
use ChiefTools\DNS\Resolver\Resolver;

$result = new Resolver()->resolve('example.com', ['A', 'AAAA', 'MX']);

$aRecords = $result->ofType('A');
$mxRecords = $result->ofType(\ChiefTools\DNS\Resolver\Enums\RecordType::MX);
```

DNSSEC validation
-----------------

[](#dnssec-validation)

DNSSEC is enabled by default (`DnssecMode::ON`). The resolver validates the full chain of trust from the root zone trust anchor through every delegation.

```
use ChiefTools\DNS\Resolver\Resolver;
use ChiefTools\DNS\Resolver\Enums\DnssecMode;

// Validate and report — always returns results (default)
$result = new Resolver()->resolve('example.com', 'A', dnssec: DnssecMode::ON);

echo $result->dnssec->status->value; // "signed", "unsigned", "invalid", or "indeterminate"

// Per-record validation status
foreach ($result->records as $record) {
    echo $record->validation->name; // SIGNED, FAILED, or UNKNOWN
}

// Strict mode — clears records when validation fails
$result = new Resolver()->resolve('example.com', 'A', dnssec: DnssecMode::STRICT);

// Disable DNSSEC entirely
$result = new Resolver()->resolve('example.com', 'A', dnssec: DnssecMode::OFF);
```

Configuration
-------------

[](#configuration)

```
use ChiefTools\DNS\Resolver\ResolverConfig;

$resolver = new Resolver(
    config: new ResolverConfig(
        ipv6: false,     // Disable IPv6 for nameserver resolution (default: true)
        timeout: 5,      // Per-query timeout in seconds (default: 2)
        maxDepth: 15,    // Maximum recursive lookups (default: 10)
    ),
);
```

Custom executor
---------------

[](#custom-executor)

The resolver ships with `NetDns2QueryExecutor` (default) and `DigQueryExecutor`. You can provide your own by implementing the `DnsQueryExecutor` interface.

`DigQueryExecutor` requires external `dig` and `jc` binaries. If you do not need that integration, the default `NetDns2QueryExecutor` is the simpler and faster choice.

```
use ChiefTools\DNS\Resolver\Executors\DigQueryExecutor;

$resolver = new Resolver(
    executor: new DigQueryExecutor(
        digPath: '/usr/local/bin/dig',
        jcPath: '/usr/local/bin/jc',
    ),
);
```

Event callback
--------------

[](#event-callback)

The `onEvent` callback fires synchronously during resolution, giving real-time visibility into every step. This is useful for streaming UIs, CLI progress output, or debug logging.

```
use ChiefTools\DNS\Resolver\Resolver;
use ChiefTools\DNS\Resolver\Events\ResolverEvent;

$result = new Resolver()->resolve('example.com', 'A',
    onEvent: function (ResolverEvent $event) {
        // Pre-formatted message for simple output
        echo $event->message . "\n";

        // Or use structured data
        // $event->type      — EventType enum (LOOKUP, QUERY, DELEGATION, CNAME, QUERY_FAILURE, NAMESERVER_FALLBACK, RESOLVE_NAMESERVER, RESOLVE_FAILURE)
        // $event->depth     — nesting level for visual indentation
        // $event->domain    — domain being queried
        // $event->nameserver, $event->address, $event->timeMs, etc.
        // $event->status    — per-step status such as "signed", "unsigned", "invalid", or null on non-query events
    },
);
```

Result objects
--------------

[](#result-objects)

### `LookupResult`

[](#lookupresult)

PropertyTypeDescription`records``list`Resolved records`timeMs``int`Total resolution time in milliseconds`status``LookupStatus`Final lookup outcome: `SUCCESS`, `NO_RECORDS`, `NXDOMAIN`, or `QUERY_FAILED``info``?string`Human-readable message for non-success outcomes or strict DNSSEC failures`dnssec``?DnssecResult`DNSSEC validation result (null when disabled)Methods: `isEmpty()`, `isNxdomain()`, `isLookupFailed()`, `ofType(RecordType|string)`

### `Record`

[](#record)

PropertyTypeDescription`name``string`Owner name (e.g. `example.com.`)`type``RecordType`Record type enum`ttl``int`Time to live`data``string`Formatted record data`rawData``string`Original data as received from the nameserver`validation``RecordValidation``SIGNED`, `FAILED`, or `UNKNOWN`### `DnssecResult`

[](#dnssecresult)

PropertyTypeDescription`status``DnssecStatus``SIGNED`, `UNSIGNED`, `INVALID`, or `INDETERMINATE``errors``list`Validation error messagesMethods: `isSigned()`, `isInvalid()`

Supported record types
----------------------

[](#supported-record-types)

A, AAAA, CNAME, MX, TXT, NS, SOA, PTR, SRV, CAA, DS, DNSKEY, CDS, CDNSKEY, CSYNC, HTTPS, SVCB, DNAME, NAPTR, TLSA, SSHFP, SMIMEA, OPENPGPKEY, CERT, URI, LOC, SPF

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

If you discover a security vulnerability within this project, please report it privately via GitHub: . All security vulnerabilities will be swiftly addressed. There is no bug bounty program at this time.

License
-------

[](#license)

This package is open-source software licensed under the Apache License 2.0. This means you are free to use, modify, and distribute the software for both commercial and non-commercial purposes. See the [LICENSE](LICENSE) file for details.

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance91

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

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 ~0 days

Total

7

Last Release

45d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1090754?v=4)[Alex Bouma](/maintainers/stayallive)[@stayallive](https://github.com/stayallive)

---

Top Contributors

[![stayallive](https://avatars.githubusercontent.com/u/1090754?v=4)](https://github.com/stayallive "stayallive (34 commits)")

---

Tags

dnsdns-resolverphp

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/chieftools-dns-resolver/health.svg)

```
[![Health](https://phpackages.com/badges/chieftools-dns-resolver/health.svg)](https://phpackages.com/packages/chieftools-dns-resolver)
```

PHPackages © 2026

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