Passed
Push — master ( e0fc9e...585118 )
by Christian
40s queued 11s
created

CloudFlare::generateEachTypeQuery()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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