{"id":21588819,"url":"https://github.com/remotelyliving/php-dns","last_synced_at":"2025-04-04T15:11:21.218Z","repository":{"id":33240093,"uuid":"156761021","full_name":"remotelyliving/php-dns","owner":"remotelyliving","description":"A DNS abstraction for PHP","archived":false,"fork":false,"pushed_at":"2024-02-01T14:24:59.000Z","size":431,"stargazers_count":158,"open_issues_count":6,"forks_count":20,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-04T19:12:04.208Z","etag":null,"topics":["cloudflare","dig","dns","dns-over-https","dns-resolver","google-dns","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/remotelyliving.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-11-08T19:53:36.000Z","updated_at":"2024-10-22T11:05:46.000Z","dependencies_parsed_at":"2024-06-19T00:09:38.199Z","dependency_job_id":"d7d8048b-b20c-4d97-bc31-aea48296a192","html_url":"https://github.com/remotelyliving/php-dns","commit_stats":{"total_commits":79,"total_committers":8,"mean_commits":9.875,"dds":0.189873417721519,"last_synced_commit":"711697608616931c9ec94624cb8984355b73102e"},"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remotelyliving%2Fphp-dns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remotelyliving%2Fphp-dns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remotelyliving%2Fphp-dns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remotelyliving%2Fphp-dns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/remotelyliving","download_url":"https://codeload.github.com/remotelyliving/php-dns/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247198464,"owners_count":20900081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","dig","dns","dns-over-https","dns-resolver","google-dns","php"],"created_at":"2024-11-24T16:11:29.023Z","updated_at":"2025-04-04T15:11:21.194Z","avatar_url":"https://github.com/remotelyliving.png","language":"PHP","readme":"[![Build Status](https://travis-ci.org/remotelyliving/php-dns.svg?branch=master)](https://travis-ci.org/remotelyliving/php-dns)\n[![Total Downloads](https://poser.pugx.org/remotelyliving/php-dns/downloads)](https://packagist.org/packages/remotelyliving/php-dns)\n[![Coverage Status](https://coveralls.io/repos/github/remotelyliving/php-dns/badge.svg?branch=master)](https://coveralls.io/github/remotelyliving/php-dns?branch=master) \n[![License](https://poser.pugx.org/remotelyliving/php-dns/license)](https://packagist.org/packages/remotelyliving/php-dns)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/remotelyliving/php-dns/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/remotelyliving/php-dns/?branch=master)\n[![PHP CI](https://github.com/remotelyliving/php-dns/actions/workflows/php-ci.yml/badge.svg)](https://github.com/remotelyliving/php-dns/actions/workflows/php-ci.yml)\n\n# PHP-DNS: A DNS Abstraction in PHP\n\n### Use Cases\n\nThis library might be for you if:\n\n- You want to be able to query DNS records locally or over HTTPS\n- You want observability into your DNS lookups\n- You want something easy to test / mock in your implementation\n- You want to try several different sources of DNS truth\n- You want to easily extend it or contribute to get more behavior you want!\n\n### Installation\n\n```sh\ncomposer require remotelyliving/php-dns\n```\n\n### Usage\n\n**Basic Resolvers** can be found in [src/Resolvers](https://github.com/remotelyliving/php-dns/tree/master/src/Resolvers)\n\nThese resolvers at the least implement the `Resolvers\\Interfaces\\DNSQuery` interface\n\n- GoogleDNS (uses the GoogleDNS DNS over HTTPS API)\n- CloudFlare (uses the CloudFlare DNS over HTTPS API)\n- LocalSystem (uses the local PHP dns query function)\n- Dig (Can use a specific nameserver per instance but requires the host OS to have dig installed). Based on [Spatie DNS](https://github.com/spatie/dns)\n\n```php\n$resolver = new Resolvers\\GoogleDNS();\n\n// can query via convenience methods\n$records = $resolver-\u003egetARecords('google.com'); // returns a collection of DNS A Records\n\n// can also query by any RecordType.\n$moreRecords = $resolver-\u003egetRecords($hostname, DNSRecordType::TYPE_AAAA);\n\n// can query to see if any resolvers find a record or type.\n$resolver-\u003ehasRecordType($hostname, $type) // true | false\n$resolver-\u003ehasRecord($record) // true | false\n\n// This becomes very powerful when used with the Chain Resolver\n\n```\n\n**Chain Resolver**\n\nThe Chain Resolver can be used to read through DNS Resolvers until an answer is found.\nWhichever you pass in first is the first Resolver it tries in the call sequence.\nIt implements the same `DNSQuery` interface as the other resolvers but with an additional feature set found in the `Chain` interface.\n\nSo something like: \n\n```php\n$chainResolver = new Chain($cloudFlareResolver, $googleDNSResolver, $localDNSResolver);\n```\n\nThat will call the GoogleDNS Resolver first, if no answer is found it will continue on to the LocalSystem Resolver.\nThe default call through strategy is First to Find aka `Resolvers\\Interfaces\\Chain::withFirstResults(): Chain`\n\nYou can randomly select which Resolver in the chain it tries first too via `Resolvers\\Interfaces\\Chain::randomly(): Chain`\nExample:\n\n```php\n$foundRecord = $chainResolver-\u003erandomly()-\u003egetARecords('facebook.com')-\u003epickFirst();\n```\n\nThe above code calls through the resolvers randomly until it finds any non empty answer or has exhausted order the chain.\n\nLastly, and most expensively, there is `Resolvers\\Interfaces\\Chain::withAllResults(): Chain` and `Resolvers\\Interfaces\\Chain::withConsensusResults(): Chain`\nAll results will be a merge from all the different sources, useful if you want to see what all is out there.\nConsensus results will be only the results in common from source to source.\n\n[src/Resolvers/Interfaces](https://github.com/remotelyliving/php-dns/tree/master/src/Resolvers/Interfaces/Chain.php)\n\n```php\n// returns the first non empty result set\n$chainResolver-\u003ewithFirstResults()-\u003egetARecords('facebook.com'); \n\n// returns the first non empty result set from a randomly selected resolver\n$chainResolver-\u003erandomly()-\u003egetARecords('facebook.com'); \n\n// returns only common results between resolvers\n$chainResolver-\u003ewithConsensusResults()-\u003egetARecords('facebook.com'); \n\n// returns all collective responses with duplicates filtered out\n$chainResolver-\u003ewithAllResults()-\u003egetARecords('facebook.com'); \n```\n\n**Cached Resolver**\n\nIf you use a PSR6 cache implementation, feel free to wrap whatever Resolver you want to use in the Cached Resolver.\nIt will take in the the lowest TTL of the record(s) and use that as the cache TTL.\nYou may override that behavior by setting a cache TTL in the constructor.\n\n```php\n$cachedResolver = new Resolvers\\Cached($cache, $resolverOfChoice);\n$cachedResolver-\u003egetRecords('facebook.com'); // get from cache if possible or falls back to the wrapped resolver and caches the returned records\n```\n\nIf you do not wish to cache empty result answers, you may call through with this additional option:\n```php\n$cachedResolver-\u003ewithEmptyResultCachingDisabled()-\u003egetARecords('facebook.com');\n```\n\n**Entities**\n\nTake a look in the `src/Entities` to see what's available for you to query by and receive.\n\nFor records with extra type data, like SOA, TXT, MX, CNAME, and NS there is a data attribute on `Entities\\DNSRecord` that will be set with the proper type.\n\n**Reverse Lookup**\n\nThis is offered via a separate `ReverseDNSQuery` interface as it is not common or available for every type of DNS Resolver.\nOnly the `LocalSystem` Resolver implements it.\n\n### Observability\n\nAll provided resolvers have the ability to add subscribers and listeners. They are directly compatible with `symfony/event-dispatcher`\n\nAll events can be found here: [src/Observability/Events](https://github.com/remotelyliving/php-dns/tree/master/src/Observability/Events)\n\nWith a good idea of what a subscriber can do with them here: [src/Observability/Subscribers](https://github.com/remotelyliving/php-dns/tree/master/src/Observability/Subscribers)\n\nYou could decide where you want to stream the events whether its to a log or somewhere else. The events are all safe to `json_encode()` without extra parsing.\n\nIf you want to see how easy it is to wire all this up, check out [the repl bootstrap](https://github.com/remotelyliving/php-dns/tree/master/bootstrap/repl.php)\n\n### Logging\n\nAll provided resolvers implement `Psr\\Log\\LoggerAwareInterface` and have a default `NullLogger` set at runtime. \n\n### Tinkering\n\nTake a look in the [Makefile](https://github.com/remotelyliving/php-dns/blob/master/Makefile) for all the things you can do!\n\nThere is a very basic REPL implementation that wires up some Resolvers for you already and pipes events to sterr and stdout\n\n`make repl`\n\n```sh\nchristians-mbp:php-dns chthomas$ make repl\nPsy Shell v0.9.9 (PHP 7.2.8 — cli) by Justin Hileman\n\u003e\u003e\u003e ls\nVariables: $cachedResolver, $chainResolver, $cloudFlareResolver, $googleDNSResolver, $IOSubscriber, $localSystemResolver, $stdErr, $stdOut\n\u003e\u003e\u003e $records = $chainResolver-\u003egetARecords('facebook.com')\n{\n    \"dns.query.profiled\": {\n        \"elapsedSeconds\": 0.21915197372436523,\n        \"transactionName\": \"CloudFlare:facebook.com.:A\",\n        \"peakMemoryUsage\": 9517288\n    }\n}\n{\n    \"dns.queried\": {\n        \"resolver\": \"CloudFlare\",\n        \"hostname\": \"facebook.com.\",\n        \"type\": \"A\",\n        \"records\": [\n            {\n                \"hostname\": \"facebook.com.\",\n                \"type\": \"A\",\n                \"TTL\": 224,\n                \"class\": \"IN\",\n                \"IPAddress\": \"31.13.71.36\"\n            }\n        ],\n        \"empty\": false\n    }\n}\n=\u003e RemotelyLiving\\PHPDNS\\Entities\\DNSRecordCollection {#2370}\n\u003e\u003e\u003e $records-\u003epickFirst()-\u003etoArray()\n=\u003e [\n     \"hostname\" =\u003e \"facebook.com.\",\n     \"type\" =\u003e \"A\",\n     \"TTL\" =\u003e 224,\n     \"class\" =\u003e \"IN\",\n     \"IPAddress\" =\u003e \"31.13.71.36\",\n   ]\n\u003e\u003e\u003e $records = $chainResolver-\u003ewithConsensusResults()-\u003egetRecords('facebook.com', 'TXT')\n{\n    \"dns.query.profiled\": {\n        \"elapsedSeconds\": 0.023031949996948242,\n        \"transactionName\": \"CloudFlare:facebook.com.:TXT\",\n        \"peakMemoryUsage\": 9615080\n    }\n}\n{\n    \"dns.queried\": {\n        \"resolver\": \"CloudFlare\",\n        \"hostname\": \"facebook.com.\",\n        \"type\": \"TXT\",\n        \"records\": [\n            {\n                \"hostname\": \"facebook.com.\",\n                \"type\": \"TXT\",\n                \"TTL\": 9136,\n                \"class\": \"IN\",\n                \"data\": \"v=spf1 redirect=_spf.facebook.com\"\n            }\n        ],\n        \"empty\": false\n    }\n}\n{\n    \"dns.query.profiled\": {\n        \"elapsedSeconds\": 0.23299598693847656,\n        \"transactionName\": \"GoogleDNS:facebook.com.:TXT\",\n        \"peakMemoryUsage\": 9615080\n    }\n}\n{\n    \"dns.queried\": {\n        \"resolver\": \"GoogleDNS\",\n        \"hostname\": \"facebook.com.\",\n        \"type\": \"TXT\",\n        \"records\": [\n            {\n                \"hostname\": \"facebook.com.\",\n                \"type\": \"TXT\",\n                \"TTL\": 21121,\n                \"class\": \"IN\",\n                \"data\": \"v=spf1 redirect=_spf.facebook.com\"\n            }\n        ],\n        \"empty\": false\n    }\n}\n{\n    \"dns.query.profiled\": {\n        \"elapsedSeconds\": 0.0018258094787597656,\n        \"transactionName\": \"LocalSystem:facebook.com.:TXT\",\n        \"peakMemoryUsage\": 9615080\n    }\n}\n{\n    \"dns.queried\": {\n        \"resolver\": \"LocalSystem\",\n        \"hostname\": \"facebook.com.\",\n        \"type\": \"TXT\",\n        \"records\": [\n            {\n                \"hostname\": \"facebook.com.\",\n                \"type\": \"TXT\",\n                \"TTL\": 25982,\n                \"class\": \"IN\",\n                \"data\": \"v=spf1 redirect=_spf.facebook.com\"\n            }\n        ],\n        \"empty\": false\n    }\n}\n=\u003e RemotelyLiving\\PHPDNS\\Entities\\DNSRecordCollection {#2413}\n\u003e\u003e\u003e $records-\u003epickFirst()-\u003egetData()-\u003egetValue()\n=\u003e \"v=spf1 redirect=_spf.facebook.com\"\n\u003e\u003e\u003e \n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremotelyliving%2Fphp-dns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fremotelyliving%2Fphp-dns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremotelyliving%2Fphp-dns/lists"}