GoogleDNS::doApiQuery()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 10
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 19
rs 9.9332
1
<?php
2
3
namespace RemotelyLiving\PHPDNS\Resolvers;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\ClientInterface;
7
use GuzzleHttp\Exception\RequestException;
8
use RemotelyLiving\PHPDNS\Entities\DNSRecord;
9
use RemotelyLiving\PHPDNS\Entities\DNSRecordCollection;
10
use RemotelyLiving\PHPDNS\Entities\Interfaces\DNSRecordInterface;
11
use RemotelyLiving\PHPDNS\Entities\DNSRecordType;
12
use RemotelyLiving\PHPDNS\Entities\Hostname;
13
use RemotelyLiving\PHPDNS\Mappers\GoogleDNS as GoogleDNSMapper;
14
use RemotelyLiving\PHPDNS\Resolvers\Exceptions\QueryFailure;
15
use Throwable;
16
17
use function http_build_query;
18
use function json_decode;
19
20
final class GoogleDNS extends ResolverAbstract
21
{
22
    protected const BASE_URI = 'https://dns.google.com';
23
    protected const DEFAULT_TIMEOUT = 5.0;
24
    public const DEFAULT_OPTIONS = [
25
        'base_uri' => self::BASE_URI,
26
        'strict' => true,
27
        'allow_redirects' => false,
28
        'connect_timeout' => self::DEFAULT_TIMEOUT,
29
        'protocols' => ['https'],
30
        'headers' => [
31
            'Accept' => 'application/json',
32
        ],
33
    ];
34
35
    private ClientInterface $http;
36
37
    private GoogleDNSMapper $mapper;
38
39
    /**
40
     * @param array<string, mixed> $options
41
     */
42
    public function __construct(
43
        ClientInterface $http = null,
44
        GoogleDNSMapper $mapper = null,
45
        private int $consensusAttempts = 3,
46
        private array $options = self::DEFAULT_OPTIONS
47
    ) {
48
        $this->http = $http ?? new Client();
49
        $this->mapper = $mapper ?? new GoogleDNSMapper();
50
    }
51
52
    /**
53
     * Google DNS has consistency issues so this tries a few times to get an answer
54
     */
55
    public function hasRecord(DNSRecordInterface $record): bool
56
    {
57
        $attempts = 0;
58
59
        do {
60
            $hasRecord = $this->getRecords((string)$record->getHostname(), (string)$record->getType())
61
                ->has($record);
62
63
            ++$attempts;
64
        } while (!$hasRecord && $attempts < $this->consensusAttempts);
65
66
        return $hasRecord;
67
    }
68
69
    protected function doQuery(Hostname $hostname, DNSRecordType $recordType): DNSRecordCollection
70
    {
71
        $results = $this->doApiQuery(['name' => (string)$hostname, 'type' => (string)$recordType]);
72
73
        return $this->mapResults($this->mapper, $results);
74
    }
75
76
    /**
77
     * @throws \RemotelyLiving\PHPDNS\Resolvers\Exceptions\QueryFailure
78
     */
79
    private function doApiQuery(array $query = []): array
80
    {
81
        try {
82
            $response = $this->http->request('GET', '/resolve?' . http_build_query($query), $this->options);
83
        } catch (Throwable $e) {
84
            throw new QueryFailure("Unable to query GoogleDNS API", 0, $e);
85
        }
86
87
        $result = (array) json_decode((string)$response->getBody(), true);
88
89
        if (isset($result['Answer'])) {
90
            return $result['Answer'];
91
        }
92
93
        if (isset($result['Authority'])) {
94
            return $result['Authority'];
95
        }
96
97
        return [];
98
    }
99
}
100