Resolver::resolveInternal()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 10
rs 9.4285
cc 1
eloc 6
nc 1
nop 2
1
<?php
2
3
namespace Thruster\Component\Dns;
4
5
use Thruster\Component\Dns\Exception\RecordNotFoundException;
6
use Thruster\Component\Promise\PromiseInterface;
7
8
class Resolver implements ResolverInterface
9
{
10
    /**
11
     * @var string
12
     */
13
    private $nameServer;
14
15
    /**
16
     * @var ExecutorInterface
17
     */
18
    private $executor;
19
20
    public function __construct(string $nameServer, ExecutorInterface $executor)
21
    {
22
        $this->nameServer = $nameServer;
23
        $this->executor = $executor;
24
    }
25
26
    public function resolve(string $domain) : PromiseInterface
27
    {
28
        $query = new Query($domain, Message::TYPE_A, Message::CLASS_IN, time());
29
30
        return $this->resolveInternal($this->nameServer, $query);
31
    }
32
33
    protected function resolveInternal(string $nameServer, Query $query)
34
    {
35
        return $this->executor
36
            ->query($nameServer, $query)
37
            ->then(
38
                function (Message $response) use ($query) {
39
                    return $this->extractAddress($query, $response);
40
                }
41
            );
42
    }
43
44
    private function extractAddress(Query $query, Message $response)
45
    {
46
        $answers = $response->answers;
47
48
        $addresses = $this->resolveAliases($answers, $query->getName());
49
50
        if (0 === count($addresses)) {
51
            throw new RecordNotFoundException('DNS Request did not return valid answer.');
52
        }
53
54
        $address = $addresses[array_rand($addresses)];
55
56
        return $address;
57
    }
58
59
    private function resolveAliases(array $answers, $name)
60
    {
61
        $named = $this->filterByName($answers, $name);
62
        $aRecords = $this->filterByType($named, Message::TYPE_A);
63
        $cnameRecords = $this->filterByType($named, Message::TYPE_CNAME);
64
65
        if ($aRecords) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $aRecords of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
66
            return $this->mapRecordData($aRecords);
67
        }
68
69
        if ($cnameRecords) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cnameRecords of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
70
            $aRecords = array();
71
72
            $cnames = $this->mapRecordData($cnameRecords);
73
            foreach ($cnames as $cname) {
74
                $targets = $this->filterByName($answers, $cname);
0 ignored issues
show
Unused Code introduced by
$targets is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
75
                $aRecords = array_merge(
76
                    $aRecords,
77
                    $this->resolveAliases($answers, $cname)
78
                );
79
            }
80
81
            return $aRecords;
82
        }
83
84
        return array();
85
    }
86
87
    private function filterByName(array $answers, $name)
88
    {
89
        return $this->filterByField($answers, 'name', $name);
90
    }
91
92
    private function filterByType(array $answers, $type)
93
    {
94
        return $this->filterByField($answers, 'type', $type);
95
    }
96
97
    private function filterByField(array $answers, $field, $value)
98
    {
99
        return array_filter($answers, function ($answer) use ($field, $value) {
100
            $getter = 'get' . ucfirst($field);
101
            return $value === $answer->$getter();
102
        });
103
    }
104
105
    private function mapRecordData(array $records)
106
    {
107
        return array_map(function ($record) {
108
            return $record->getData();
109
        }, $records);
110
    }
111
}
112