Completed
Push — master ( dad210...6a0b65 )
by
unknown
06:47
created

Pool::applyConfig()   C

Complexity

Conditions 11
Paths 2

Size

Total Lines 44
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 11
eloc 29
c 3
b 0
f 0
nc 2
nop 0
dl 0
loc 44
rs 5.2653

How to fix   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',
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
                        /* skip commentened ips */
188
                        if (preg_match('~^[\s]*?#~m', $ip)) {
189
                            continue;
190
                        }
191
                        foreach ($hosts as $host) {
192
                            $host = rtrim($host, '.') . '.';
193
                            $pool->hosts[$host][] = $ip;
194
                        }
195
                    }
196
                }
197
                $job->setResult($jobname);
198
            });
199
        });
200
        $job();
201
    }
202
203
    /**
204
     * Resolves the host
205
     * @param  string $hostname Hostname
206
     * @param  callable $cb Callback
207
     * @param  boolean $noncache Noncache?
208
     * @param  array $nameServers
209
     * @callback $cb ( array|string $addrs )
210
     * @return void
211
     */
212
    public function resolve($hostname, $cb, $noncache = false, $nameServers = [])
213
    {
214 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...
215
            $pool = $this;
216
            $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...
217
                $pool->resolve($hostname, $cb, $noncache, $nameServers);
218
            });
219
            return;
220
        }
221
        $hostname = rtrim($hostname, '.') . '.';
222
        if (isset($this->hosts[$hostname])) {
223
            $cb($this->hosts[$hostname]);
224
            return;
225
        }
226
        if (!$noncache && ($item = $this->resolveCache->get($hostname))) { // cache hit
227
            $ip = $item->getValue();
228
            if ($ip === null) { // operation in progress
229
                $item->addListener($cb);
230
            } else { // hit
231
                $cb($ip);
232
            }
233
            return;
234
        } elseif (!$noncache) {
235
            $item = $this->resolveCache->put($hostname, null);
236
            $item->addListener($cb);
237
        }
238
        $pool = $this;
239
        $this->get($hostname, function ($response) use ($cb, $noncache, $hostname, $pool) {
240
            if (!isset($response['A'])) {
241
                if ($noncache) {
242
                    $cb(false);
243
                } else {
244
                    $pool->resolveCache->put($hostname, false, 5); // 5 - TTL of unsuccessful request
245
                }
246
                return;
247
            }
248
            if (!isset($response['A']) && !isset($response['AAAA'])) {
249
                $cb(false);
250
                return;
251
            }
252
            $addrs = [];
253
            $ttl = 0;
254 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...
255
                foreach ($response['A'] as $r) {
256
                    $addrs[] = $r['ip'];
257
                    $ttl = $r['ttl'];
258
                }
259
            }
260 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...
261
                foreach ($response['AAAA'] as $r) {
262
                    $addrs[] = $r['ip'];
263
                    $ttl = $r['ttl'];
264
                }
265
            }
266
            if (sizeof($addrs) === 1) {
267
                $addrs = $addrs[0];
268
            }
269
            if ($noncache) {
270
                $cb($addrs);
271
            } else {
272
                $pool->resolveCache->put($hostname, $addrs, $ttl);
273
            }
274
        }, $noncache, $nameServers);
275
    }
276
277
    /**
278
     * Gets the host information
279
     * @param  string $hostname Hostname
280
     * @param  callable $cb Callback
281
     * @param  boolean $noncache Noncache?
282
     * @param  array $nameServers
283
     * @param  string $proto
284
     * @callback $cb ( )
285
     * @return void
286
     */
287
    public function get($hostname, $cb, $noncache = false, $nameServers = [], $proto = 'udp')
288
    {
289
        $pool = $this;
290
        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...
291
            $nameServers = $this->nameServers;
292
        }
293 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...
294
            $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...
295
                $hostname,
296
                $cb,
297
                $noncache,
298
                $pool,
299
                $nameServers,
300
                $proto
301
            ) {
302
                $pool->get($hostname, $cb, $noncache, $nameServers, $proto);
303
            });
304
            return;
305
        }
306
        $nameServer = reset($nameServers);
307
        $isIpv6 = filter_var($nameServer, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
308
        if ($isIpv6) {
309
            $nameServer = '[' . $nameServer . ']';
310
        }
311
        $onGetConnection = function ($conn) use ($cb, $hostname, $nameServers, $noncache, $pool, $proto) {
312
            if (!$conn || !$conn->isConnected()) {
313
                if ($proto === 'udp') {
314
                    //Fail to  connect via udp, trying by tcp
315
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
316
                    return;
317
                }
318
                array_shift($nameServers);
319
                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...
320
                    //Totally fail to resolve name
321
                    $cb(false);
322
                } else {
323
                    //Fail connect to curr Ns, but we can try another ns
324
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'udp');
325
                }
326
            } else {
327
                $conn->get(
328
                    $hostname,
329
                    function ($response) use ($hostname, $cb, $proto, $noncache, $nameServers, $pool) {
330
                        if ($response === false && $proto === 'udp') {
331
                            //Fail to  connect via udp, trying by tcp
332
                            $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
333
                        } else {
334
                            call_user_func($cb, $response);
335
                        }
336
                    }
337
                );
338
            }
339
        };
340
        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...
341
        if ($type === 'AXFR') {
342
            $proto = 'tcp';
343
        }
344
        $this->getConnection($proto . '://' . $nameServer, $onGetConnection);
345
        return;
346
    }
347
}
348