Completed
Push — master ( 14b7d6...b907e5 )
by Vasily
04:04
created

Pool   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 330
Duplicated Lines 7.58 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 8
Bugs 6 Features 0
Metric Value
c 8
b 6
f 0
dl 25
loc 330
rs 8.2608
wmc 40
lcom 1
cbo 4

5 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 4 1
A getConfigDefaults() 0 22 1
C resolve() 19 64 17
C get() 6 60 11
D applyConfig() 0 40 10

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Pool often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Pool, and based on these observations, apply Extract Interface, too.

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',
22
        2 => 'NS',
23
        3 => 'MD',
24
        4 => 'MF',
25
        5 => 'CNAME',
26
        6 => 'SOA',
27
        7 => 'MB',
28
        8 => 'MG',
29
        9 => 'MR',
30
        10 => 'RR',
31
        11 => 'WKS',
32
        12 => 'PTR',
33
        13 => 'HINFO',
34
        14 => 'MINFO',
35
        15 => 'MX',
36
        16 => 'TXT',
37
        17 => 'RP',
38
        18 => 'AFSDB',
39
        19 => 'X25',
40
        20 => 'ISDN',
41
        21 => 'RT',
42
        22 => 'NSAP',
43
        23 => 'NSAP-PTR',
44
        24 => 'SIG',
45
        25 => 'KEY',
46
        26 => 'PX',
47
        27 => 'GPOS',
48
        28 => 'AAAA',
49
        29 => 'LOC',
50
        30 => 'NXT',
51
        31 => 'EID',
52
        32 => 'NIMLOC',
53
        33 => 'SRV',
54
        34 => 'ATMA',
55
        35 => 'NAPTR',
56
        36 => 'KX',
57
        37 => 'CERT',
58
        38 => 'A6',
59
        39 => 'DNAME',
60
        40 => 'SINK',
61
        41 => 'OPT',
62
        42 => 'APL',
63
        43 => 'DS',
64
        44 => 'SSHFP',
65
        45 => 'IPSECKEY',
66
        46 => 'RRSIG',
67
        47 => 'NSEC',
68
        48 => 'DNSKEY',
69
        49 => 'DHCID',
70
        50 => 'NSEC3',
71
        51 => 'NSEC3PARAM',
72
        55 => 'HIP',
73
        99 => 'SPF',
74
        100 => 'UINFO',
75
        101 => 'UID',
76
        102 => 'GID',
77
        103 => 'UNSPEC',
78
        249 => 'TKEY',
79
        250 => 'TSIG',
80
        251 => 'IXFR',
81
        252 => 'AXFR',
82
        253 => 'MAILB',
83
        254 => 'MAILA',
84
        255 => 'ALL',
85
        32768 => 'TA',
86
        32769 => 'DLV',
87
    ];
88
89
    /**
90
     * @var array Hosts file parsed [hostname => [addr, ...], ...]
91
     */
92
    public $hosts = [];
93
94
    /**
95
     * @var \PHPDaemon\Core\ComplexJob Preloading ComplexJob
96
     */
97
    public $preloading;
98
99
    /**
100
     * @var CappedStorageHits Resolve cache
101
     */
102
    public $resolveCache;
103
104
    /**
105
     * @var array Classes [code => "class"]
106
     */
107
    public static $class = [
108
        1 => 'IN',
109
        3 => 'CH',
110
        255 => 'ANY',
111
    ];
112
113
    /**
114
     * @var array resolve.conf file parsed
115
     */
116
    public $nameServers = [];
117
118
    /**
119
     * Constructor
120
     */
121
    protected function init()
122
    {
123
        $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...
124
    }
125
126
    /**
127
     * Setting default config options
128
     * Overriden from NetworkClient::getConfigDefaults
129
     * @return array
130
     */
131
    protected function getConfigDefaults()
132
    {
133
        return [
134
            /* [integer] port */
135
            'port' => 53,
136
137
            /* [integer] resolvecachesize */
138
            'resolvecachesize' => 128,
139
140
            /* [string] Servers */
141
            'servers' => '',
142
143
            /* [string] hostsfile */
144
            'hostsfile' => '/etc/hosts',
145
146
            /* [string] resolvfile */
147
            'resolvfile' => '/etc/resolv.conf',
148
149
            /* [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...
150
            'expose' => 1,
151
        ];
152
    }
153
154
    /**
155
     * Applies config
156
     * @return void
157
     */
158
    public function applyConfig()
159
    {
160
        parent::applyConfig();
161
        $pool = $this;
162
        if (!isset($this->preloading)) {
163
            $this->preloading = new ComplexJob();
164
        }
165
        $job = $this->preloading;
166
        $job->addJob('resolvfile', function ($jobname, $job) use ($pool) {
167
            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...
168
                if ($file) {
169
                    foreach (explode("\n", $data) as $line) {
170
                        $line = trim($line);
171
                        if ($line !== '' && $line[0] !== '#' && preg_match('~nameserver ([^\r\n;]+)~i', $line, $m)) {
172
                            $pool->nameServers[] = $m[1];
173
                        }
174
                    }
175
                }
176
                $job->setResult($jobname);
177
            });
178
        });
179
        $job->addJob('hostsfile', function ($jobname, $job) use ($pool) {
180
            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...
181
                if ($file) {
182
                    preg_match_all('~^(\S+)\s+([^\r\n]+)\s*~m', $data, $m, PREG_SET_ORDER);
183
                    $pool->hosts = [];
184
                    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...
185
                        $hosts = preg_split('~\s+~', $h[2]);
186
                        $ip = $h[1];
187
                        foreach ($hosts as $host) {
188
                            $host = rtrim($host, '.') . '.';
189
                            $pool->hosts[$host][] = $ip;
190
                        }
191
                    }
192
                }
193
                $job->setResult($jobname);
194
            });
195
        });
196
        $job();
197
    }
198
199
    /**
200
     * Resolves the host
201
     * @param  string $hostname Hostname
202
     * @param  callable $cb Callback
203
     * @param  boolean $noncache Noncache?
204
     * @param  array $nameServers
205
     * @callback $cb ( array|string $addrs )
206
     * @return void
207
     */
208
    public function resolve($hostname, $cb, $noncache = false, $nameServers = [])
209
    {
210 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...
211
            $pool = $this;
212
            $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...
213
                $pool->resolve($hostname, $cb, $noncache, $nameServers);
214
            });
215
            return;
216
        }
217
        $hostname = rtrim($hostname, '.') . '.';
218
        if (isset($this->hosts[$hostname])) {
219
            $cb($this->hosts[$hostname]);
220
            return;
221
        }
222
        if (!$noncache && ($item = $this->resolveCache->get($hostname))) { // cache hit
223
            $ip = $item->getValue();
224
            if ($ip === null) { // operation in progress
225
                $item->addListener($cb);
226
            } else { // hit
227
                $cb($ip);
228
            }
229
            return;
230
        } elseif (!$noncache) {
231
            $item = $this->resolveCache->put($hostname, null);
232
            $item->addListener($cb);
233
        }
234
        $pool = $this;
235
        $this->get($hostname, function ($response) use ($cb, $noncache, $hostname, $pool) {
236
            if (!isset($response['A'])) {
237
                if ($noncache) {
238
                    $cb(false);
239
                } else {
240
                    $pool->resolveCache->put($hostname, false, 5); // 5 - TTL of unsuccessful request
241
                }
242
                return;
243
            }
244
            if (!isset($response['A']) && !isset($response['AAAA'])) {
245
                $cb(false);
246
                return;
247
            }
248
            $addrs = [];
249
            $ttl = 0;
250 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...
251
                foreach ($response['A'] as $r) {
252
                    $addrs[] = $r['ip'];
253
                    $ttl = $r['ttl'];
254
                }
255
            }
256 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...
257
                foreach ($response['AAAA'] as $r) {
258
                    $addrs[] = $r['ip'];
259
                    $ttl = $r['ttl'];
260
                }
261
            }
262
            if (sizeof($addrs) === 1) {
263
                $addrs = $addrs[0];
264
            }
265
            if ($noncache) {
266
                $cb($addrs);
267
            } else {
268
                $pool->resolveCache->put($hostname, $addrs, $ttl);
269
            }
270
        }, $noncache, $nameServers);
271
    }
272
273
    /**
274
     * Gets the host information
275
     * @param  string $hostname Hostname
276
     * @param  callable $cb Callback
277
     * @param  boolean $noncache Noncache?
278
     * @param  array $nameServers
279
     * @param  string $proto
280
     * @callback $cb ( )
281
     * @return void
282
     */
283
    public function get($hostname, $cb, $noncache = false, $nameServers = [], $proto = 'udp')
284
    {
285
        $pool = $this;
286
        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...
287
            $nameServers = $this->nameServers;
288
        }
289 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...
290
            $this->preloading->addListener(function ($job) use (
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...
291
                $hostname,
292
                $cb,
293
                $noncache,
294
                $pool,
295
                $nameServers,
296
                $proto
297
            ) {
298
                $pool->get($hostname, $cb, $noncache, $nameServers, $proto);
299
            });
300
            return;
301
        }
302
        $nameServer = reset($nameServers);
303
        $isIpv6 = filter_var($nameServer, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
304
        if ($isIpv6) {
305
            $nameServer = '[' . $nameServer . ']';
306
        }
307
        $onGetConnection = function ($conn) use ($cb, $hostname, $nameServers, $noncache, $pool, $proto) {
308
            if (!$conn || !$conn->isConnected()) {
309
                if ($proto === 'udp') {
310
                    //Fail to  connect via udp, trying by tcp
311
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
312
                    return;
313
                }
314
                array_shift($nameServers);
315
                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...
316
                    //Totally fail to resolve name
317
                    $cb(false);
318
                } else {
319
                    //Fail connect to curr Ns, but we can try another ns
320
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'udp');
321
                }
322
            } else {
323
                $conn->get(
324
                    $hostname,
325
                    function ($response) use ($hostname, $cb, $proto, $noncache, $nameServers, $pool) {
326
                        if ($response === false && $proto === 'udp') {
327
                            //Fail to  connect via udp, trying by tcp
328
                            $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
329
                        } else {
330
                            call_user_func($cb, $response);
331
                        }
332
                    }
333
                );
334
            }
335
        };
336
        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...
337
        if ($type === 'AXFR') {
338
            $proto = 'tcp';
339
        }
340
        $this->getConnection($proto . '://' . $nameServer, $onGetConnection);
341
        return;
342
    }
343
}
344