Completed
Push — master ( 629445...184782 )
by Vasily
9s
created

Pool::resolve()   C

Complexity

Conditions 17
Paths 6

Size

Total Lines 64
Code Lines 48

Duplication

Lines 19
Ratio 29.69 %

Importance

Changes 4
Bugs 3 Features 0
Metric Value
cc 17
eloc 48
c 4
b 3
f 0
nc 6
nop 4
dl 19
loc 64
rs 5.9734

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace PHPDaemon\Clients\DNS;
3
4
use PHPDaemon\Cache\CappedStorageHits;
5
use PHPDaemon\Core\ComplexJob;
6
use PHPDaemon\FS\FileSystem;
7
use PHPDaemon\Network\Client;
8
9
/**
10
 * @package    NetworkClients
11
 * @subpackage DNSClient
12
 * @author     Vasily Zorin <[email protected]>
13
 */
14
class Pool extends Client
15
{
16
17
    /**
18
     * @var array Record Types [code => "name", ...]
19
     */
20
    public static $type = [
21
        1     => 'A', 2 => 'NS', 3 => 'MD', 4 => 'MF', 5 => 'CNAME',
22
        6     => 'SOA', 7 => 'MB', 8 => 'MG', 9 => 'MR', 10 => 'RR',
23
        11    => 'WKS', 12 => 'PTR', 13 => 'HINFO', 14 => 'MINFO',
24
        15    => 'MX', 16 => 'TXT', 17 => 'RP', 18 => 'AFSDB',
25
        19    => 'X25', 20 => 'ISDN',
26
        21    => 'RT', 22 => 'NSAP', 23 => 'NSAP-PTR', 24 => 'SIG',
27
        25    => 'KEY', 26 => 'PX', 27 => 'GPOS', 28 => 'AAAA', 29 => 'LOC',
28
        30    => 'NXT', 31 => 'EID', 32 => 'NIMLOC', 33 => 'SRV',
29
        34    => 'ATMA', 35 => 'NAPTR', 36 => 'KX', 37 => 'CERT', 38 => 'A6',
30
        39    => 'DNAME', 40 => 'SINK', 41 => 'OPT', 42 => 'APL', 43 => 'DS',
31
        44    => 'SSHFP', 45 => 'IPSECKEY', 46 => 'RRSIG', 47 => 'NSEC',
32
        48    => 'DNSKEY', 49 => 'DHCID', 50 => 'NSEC3', 51 => 'NSEC3PARAM',
33
        55    => 'HIP', 99 => 'SPF', 100 => 'UINFO', 101 => 'UID', 102 => 'GID',
34
        103   => 'UNSPEC', 249 => 'TKEY', 250 => 'TSIG', 251 => 'IXFR',
35
        252   => 'AXFR', 253 => 'MAILB', 254 => 'MAILA', 255 => 'ALL',
36
        32768 => 'TA', 32769 => 'DLV',
37
    ];
38
39
    /**
40
     * @var array Hosts file parsed [hostname => [addr, ...], ...]
41
     */
42
    public $hosts = [];
43
44
    /**
45
     * @var \PHPDaemon\Core\ComplexJob Preloading ComplexJob
46
     */
47
    public $preloading;
48
49
    /**
50
     * @var CappedStorageHits Resolve cache
51
     */
52
    public $resolveCache;
53
54
    /**
55
     * @var array Classes [code => "class"]
56
     */
57
    public static $class = [
58
        1   => 'IN',
59
        3   => 'CH',
60
        255 => 'ANY',
61
    ];
62
    
63
    /**
64
    * @var array resolve.conf file parsed 
65
    */
66
    public $nameServers = [];
67
68
    /**
69
     * Constructor
70
     */
71
    protected function init()
72
    {
73
        $this->resolveCache = new CappedStorageHits($this->config->resolvecachesize->value);
0 ignored issues
show
Bug introduced by
The property resolvecachesize does not seem to exist in PHPDaemon\Config\Section.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
74
    }
75
76
    /**
77
     * Setting default config options
78
     * Overriden from NetworkClient::getConfigDefaults
79
     * @return array
80
     */
81
    protected function getConfigDefaults()
82
    {
83
        return [
84
            /* [integer] port */
85
            'port'             => 53,
86
            
87
            /* [integer] resolvecachesize */
88
            'resolvecachesize' => 128,
89
            
90
            /* [string] Servers */
91
            'servers'          => '',
92
            
93
            /* [string] hostsfile */
94
            'hostsfile'        => '/etc/hosts',
95
            
96
            /* [string] resolvfile */
97
            'resolvfile'       => '/etc/resolv.conf',
98
            
99
            /* [boolean] Expose? */
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
100
            'expose'           => 1,
101
        ];
102
    }
103
104
    /**
105
     * Applies config
106
     * @return void
107
     */
108
    public function applyConfig()
109
    {
110
        parent::applyConfig();
111
        $pool = $this;
112
        if (!isset($this->preloading)) {
113
            $this->preloading = new ComplexJob();
114
        }
115
        $job = $this->preloading;
116
        $job->addJob('resolvfile', function ($jobname, $job) use ($pool) {
117
            FileSystem::readfile($pool->config->resolvfile->value, function ($file, $data) use ($pool, $job, $jobname) {
0 ignored issues
show
Bug introduced by
The property resolvfile does not seem to exist in PHPDaemon\Config\Section.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
118
                if ($file) {
119
                    preg_match_all('~nameserver ([^\r\n;]+)~', $data, $m);
120
                    foreach ($m[1] as $s) {
121
                        $pool->nameServers[] = $s;
122
                    }
123
                }
124
                $job->setResult($jobname);
125
            });
126
        });
127
        $job->addJob('hostsfile', function ($jobname, $job) use ($pool) {
128
            FileSystem::readfile($pool->config->hostsfile->value, function ($file, $data) use ($pool, $job, $jobname) {
0 ignored issues
show
Bug introduced by
The property hostsfile does not seem to exist in PHPDaemon\Config\Section.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
129
                if ($file) {
130
                    preg_match_all('~^(\S+)\s+([^\r\n]+)\s*~m', $data, $m, PREG_SET_ORDER);
131
                    $pool->hosts = [];
132
                    foreach ($m as $h) {
0 ignored issues
show
Bug introduced by
The expression $m of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
133
                        $hosts = preg_split('~\s+~', $h[2]);
134
                        $ip    = $h[1];
135
                        foreach ($hosts as $host) {
136
                            $host               = rtrim($host, '.') . '.';
137
                            $pool->hosts[$host][] = $ip;
138
                        }
139
                    }
140
                }
141
                $job->setResult($jobname);
142
            });
143
        });
144
        $job();
145
    }
146
147
    /**
148
     * Resolves the host
149
     * @param  string   $hostname Hostname
150
     * @param  callable $cb       Callback
151
     * @param  boolean  $noncache Noncache?
152
     * @param  array    $nameServers
153
     * @callback $cb ( array|string $addrs )
154
     * @return void
155
     */
156
    public function resolve($hostname, $cb, $noncache = false, $nameServers = [])
157
    {
158 View Code Duplication
        if (!$this->preloading->hasCompleted()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
            $pool = $this;
160
            $this->preloading->addListener(function ($job) use ($hostname, $cb, $noncache, $pool, $nameServers) {
0 ignored issues
show
Unused Code introduced by
The parameter $job is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
161
                $pool->resolve($hostname, $cb, $noncache, $nameServers);
162
            });
163
            return;
164
        }
165
        $hostname = rtrim($hostname, '.') . '.';
166
        if (isset($this->hosts[$hostname])) {
167
            $cb($this->hosts[$hostname]);
168
            return;
169
        }
170
        if (!$noncache && ($item = $this->resolveCache->get($hostname))) { // cache hit
171
            $ip = $item->getValue();
172
            if ($ip === null) { // operation in progress
173
                $item->addListener($cb);
174
            } else { // hit
175
                $cb($ip);
176
            }
177
            return;
178
        } elseif (!$noncache) {
179
            $item = $this->resolveCache->put($hostname, null);
180
            $item->addListener($cb);
181
        }
182
        $pool = $this;
183
        $this->get($hostname, function ($response) use ($cb, $noncache, $hostname, $pool) {
184
            if (!isset($response['A'])) {
185
                if ($noncache) {
186
                    $cb(false);
187
                } else {
188
                    $pool->resolveCache->put($hostname, false, 5); // 5 - TTL of unsuccessful request
189
                }
190
                return;
191
            }
192
            if (!isset($response['A']) && !isset($response['AAAA'])) {
193
                $cb(false);
194
                return;
195
            }
196
            $addrs = [];
197
            $ttl   = 0;
198 View Code Duplication
            if (isset($response['A'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199
                foreach ($response['A'] as $r) {
200
                    $addrs[] = $r['ip'];
201
                    $ttl = $r['ttl'];
202
                }
203
            }
204 View Code Duplication
            if (isset($response['AAAA'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
205
                foreach ($response['AAAA'] as $r) {
206
                    $addrs[] = $r['ip'];
207
                    $ttl = $r['ttl'];
208
                }
209
            }
210
            if (sizeof($addrs) === 1) {
211
                $addrs = $addrs[0];
212
            }
213
            if ($noncache) {
214
                $cb($addrs);
215
            } else {
216
                $pool->resolveCache->put($hostname, $addrs, $ttl);
217
            }
218
        }, $noncache, $nameServers);
219
    }
220
221
    /**
222
     * Gets the host information
223
     * @param  string   $hostname Hostname
224
     * @param  callable $cb       Callback
225
     * @param  boolean  $noncache Noncache?
226
     * @param  array    $nameServers
227
     * @param  string   $proto
228
     * @callback $cb ( )
229
     * @return void
230
     */
231
    public function get($hostname, $cb, $noncache = false, $nameServers = [], $proto = 'udp') 
232
    {
233
        $pool = $this;
234
        if(!$nameServers){
0 ignored issues
show
Bug Best Practice introduced by
The expression $nameServers 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...
235
            $nameServers = $this->nameServers;
236
        }
237 View Code Duplication
        if (!$this->preloading->hasCompleted()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
            $this->preloading->addListener(function ($job) use ($hostname, $cb, $noncache, $pool, $nameServers, $proto) {
0 ignored issues
show
Unused Code introduced by
The parameter $job is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 121 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
239
                $pool->get($hostname, $cb, $noncache, $nameServers, $proto);
240
            });
241
            return;
242
        }
243
        $nameServer = reset ($nameServers);
244
        $isIpv6 = filter_var($nameServer, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
245
        if($isIpv6){
246
            $nameServer = '[' . $nameServer . ']';
247
        }
248
        $onGetConnection = function ($conn) use ($cb, $hostname, $nameServers, $noncache, $pool, $proto) {
249
            if (!$conn || !$conn->isConnected()) {
250
                if($proto === 'udp'){
251
                    //Fail to  connect via udp, trying by tcp
252
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
253
                    return;
254
                }
255
                array_shift ($nameServers);
256
                if(!$nameServers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nameServers 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...
257
                    //Totally fail to resolve name
258
                    $cb(false);
259
                }else{
260
                    //Fail connect to curr Ns, but we can try another ns
261
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'udp');
262
                }
263
            } else {
264
                $conn->get($hostname, function($response) use ($hostname, $cb, $proto, $noncache, $nameServers, $pool){
265
                    if($response === false && $proto === 'udp'){
266
                        //Fail to  connect via udp, trying by tcp
267
                        $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
268
                    }else {
269
                        call_user_func($cb, $response);
270
                    }
271
                });
272
            }
273
		};
274
        list($host, $type, $class) = explode(':', $hostname . '::', 3);
0 ignored issues
show
Unused Code introduced by
The assignment to $host is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $class is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
275
        if($type === 'AXFR'){
276
            $proto = 'tcp';
277
        }
278
        $this->getConnection($proto . '://' . $nameServer , $onGetConnection);
279
		return;
280
	}
281
}
282