Passed
Push — master ( 857842...9f3929 )
by Christian
03:54
created

CloudFlare::doAnyApiQuery()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 3
nop 1
dl 0
loc 22
rs 9.8666
c 0
b 0
f 0
1
<?php
2
namespace RemotelyLiving\PHPDNS\Resolvers;
3
4
use GuzzleHttp\Client;
5
use GuzzleHttp\ClientInterface;
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Promise\PromiseInterface;
8
use function GuzzleHttp\Promise\unwrap;
9
use GuzzleHttp\Psr7\Response;
10
use RemotelyLiving\PHPDNS\Entities\DNSRecordCollection;
11
use RemotelyLiving\PHPDNS\Entities\DNSRecordType;
12
use RemotelyLiving\PHPDNS\Entities\Hostname;
13
use RemotelyLiving\PHPDNS\Mappers\CloudFlare as CloudFlareMapper;
14
use RemotelyLiving\PHPDNS\Resolvers\Exceptions\QueryFailure;
15
16
class CloudFlare extends ResolverAbstract
17
{
18
    protected const BASE_URI = 'https://cloudflare-dns.com';
19
    protected const DEFAULT_TIMEOUT = 5.0;
20
    protected const DEFAULT_OPTIONS = [
21
        'base_uri' => self::BASE_URI,
22
        'connect_timeout' => self::DEFAULT_TIMEOUT,
23
        'strict' => true,
24
        'allow_redirects' => false,
25
        'protocols' => ['https'],
26
        'headers' => [
27
            'Accept' => 'application/dns-json',
28
        ],
29
    ];
30
31
    /**
32
     * @var \GuzzleHttp\Client|\GuzzleHttp\ClientInterface
33
     */
34
    private $http;
35
36
    /**
37
     * @var \RemotelyLiving\PHPDNS\Mappers\CloudFlare
38
     */
39
    private $mapper;
40
41
    public function __construct(ClientInterface $http = null, CloudFlareMapper $mapper = null)
42
    {
43
        $this->http = $http ?? new Client(self::DEFAULT_OPTIONS);
44
        $this->mapper = $mapper ?? new CloudFlareMapper();
45
    }
46
47
    protected function doQuery(Hostname $hostname, DNSRecordType $type): DNSRecordCollection
48
    {
49
        try {
50
            return (!$type->isA(DNSRecordType::TYPE_ANY))
51
                ? $this->doApiQuery(['name' => (string)$hostname, 'type' => (string)$type])
52
                : $this->doAnyApiQuery($hostname);
53
        } catch (RequestException $e) {
54
            throw new QueryFailure("Unable to query CloudFlare API", 0, $e);
55
        }
56
    }
57
58
    /**
59
     * Cloudflare does not support ANY queries, so we must ask for all record types individually
60
     */
61
    private function doAnyApiQuery(Hostname $hostname) : DNSRecordCollection
62
    {
63
        $collection = new DNSRecordCollection();
64
        $promises = [];
65
66
        foreach (DNSRecordType::VALID_TYPES as $type) {
67
            if ($type === DNSRecordType::TYPE_ANY) {
68
                continue;
69
            }
70
71
            $promises[] = $this->doAsyncApiQuery(['name' => (string)$hostname, 'type' => $type])
72
                ->then(function (Response $response) use (&$collection) {
73
                    $decoded = json_decode((string)$response->getBody(), true);
74
                    foreach ($this->parseResult($decoded) as $fields) {
75
                        $collection[] = $this->mapper->mapFields($fields)->toDNSRecord();
76
                    }
77
                });
78
        }
79
80
        unwrap($promises);
81
82
        return $collection;
83
    }
84
85
    private function doAsyncApiQuery(array $query) : PromiseInterface
86
    {
87
        return $this->http->requestAsync('GET', '/dns-query?' . http_build_query($query));
88
    }
89
90
    private function doApiQuery(array $query = []) : DNSRecordCollection
91
    {
92
        $decoded = json_decode((string)$this->doAsyncApiQuery($query)->wait()->getBody(), true);
93
        $collection = new DNSRecordCollection();
94
95
        foreach ($this->parseResult($decoded) as $fields) {
96
            $collection[] = $this->mapper->mapFields($fields)->toDNSRecord();
97
        }
98
99
        return $collection;
100
    }
101
102
    private function parseResult(array $result) : array
103
    {
104
        if (isset($result['Answer'])) {
105
            return $result['Answer'];
106
        }
107
108
        if (isset($result['Authority'])) {
109
            return $result['Authority'];
110
        }
111
112
        return [];
113
    }
114
}
115