Completed
Pull Request — master (#235)
by
unknown
04:22
created

Pool   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 270
Duplicated Lines 9.26 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 3
Bugs 2 Features 0
Metric Value
c 3
b 2
f 0
dl 25
loc 270
rs 8.6
wmc 37
lcom 1
cbo 4

5 Methods

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

How to fix   Duplicated Code   

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:

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
                        //$pool->addServer('udp://' . $s);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% 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...
123
                        //$pool->addServer('tcp://' . $s);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% 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...
124
                    }
125
                }
126
                $job->setResult($jobname);
127
            });
128
        });
129
        $job->addJob('hostsfile', function ($jobname, $job) use ($pool) {
130
            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...
131
                if ($file) {
132
                    preg_match_all('~^(\S+)\s+([^\r\n]+)\s*~m', $data, $m, PREG_SET_ORDER);
133
                    $pool->hosts = [];
134
                    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...
135
                        $hosts = preg_split('~\s+~', $h[2]);
136
                        $ip    = $h[1];
137
                        foreach ($hosts as $host) {
138
                            $host               = rtrim($host, '.') . '.';
139
                            $pool->hosts[$host][] = $ip;
140
                        }
141
                    }
142
                }
143
                $job->setResult($jobname);
144
            });
145
        });
146
        $job();
147
    }
148
149
    /**
150
     * Resolves the host
151
     * @param  string   $hostname Hostname
152
     * @param  callable $cb       Callback
153
     * @param  boolean  $noncache Noncache?
154
     * @param  array    $nameServers
0 ignored issues
show
Documentation introduced by
Should the type for parameter $nameServers not be false|array?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
155
     * @callback $cb ( array|string $addrs )
156
     * @return void
157
     */
158
    public function resolve($hostname, $cb, $noncache = false, $nameServers = false)
159
    {
160 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...
161
            $pool = $this;
162
            $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...
163
                $pool->resolve($hostname, $cb, $noncache, $nameServers);
164
            });
165
            return;
166
        }
167
        $hostname = rtrim($hostname, '.') . '.';
168
        if (isset($this->hosts[$hostname])) {
169
            $cb($this->hosts[$hostname]);
170
            return;
171
        }
172
        if (!$noncache && ($item = $this->resolveCache->get($hostname))) { // cache hit
173
            $ip = $item->getValue();
174
            if ($ip === null) { // operation in progress
175
                $item->addListener($cb);
176
            } else { // hit
177
                $cb($ip);
178
            }
179
            return;
180
        } elseif (!$noncache) {
181
            $item = $this->resolveCache->put($hostname, null);
182
            $item->addListener($cb);
183
        }
184
        $pool = $this;
185
        $this->get($hostname, function ($response) use ($cb, $noncache, $hostname, $pool) {
186
            if (!isset($response['A'])) {
187
                if ($noncache) {
188
                    $cb(false);
189
                } else {
190
                    $pool->resolveCache->put($hostname, false, 5); // 5 - TTL of unsuccessful request
191
                }
192
                return;
193
            }
194
            if (!isset($response['A']) && !isset($response['AAAA'])) {
195
                $cb(false);
196
                return;
197
            }
198
            $addrs = [];
199
            $ttl   = 0;
200 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...
201
                foreach ($response['A'] as $r) {
202
                    $addrs[] = $r['ip'];
203
                    $ttl = $r['ttl'];
204
                }
205
            }
206 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...
207
                foreach ($response['AAAA'] as $r) {
208
                    $addrs[] = $r['ip'];
209
                    $ttl = $r['ttl'];
210
                }
211
            }
212
            if (sizeof($addrs) === 1) {
213
                $addrs = $addrs[0];
214
            }
215
            if ($noncache) {
216
                $cb($addrs);
217
            } else {
218
                $pool->resolveCache->put($hostname, $addrs, $ttl);
219
            }
220
        }, $noncache, $nameServers);
221
    }
222
223
    /**
224
     * Gets the host information
225
     * @param  string   $hostname Hostname
226
     * @param  callable $cb       Callback
227
     * @param  boolean  $noncache Noncache?
228
     * @param  array    $nameServers
0 ignored issues
show
Documentation introduced by
Should the type for parameter $nameServers not be false|array?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
229
     * @param  string   $proto
230
     * @callback $cb ( )
231
     * @return void
232
     */
233
    public function get($hostname, $cb, $noncache = false, $nameServers = false, $proto = 'udp') 
234
    {
235
        $pool = $this;
236
        if(!$nameServers){
237
            $nameServers = $this->nameServers;
238
        }
239 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...
240
            $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...
241
                $pool->get($hostname, $cb, $noncache, $nameServers, $proto);
242
            });
243
            return;
244
        }
245
        $nameServer = reset ($nameServers);
246
        $isIpv6 = filter_var($nameServer, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
247
        if($isIpv6){
248
            $nameServer = '[' . $nameServer . ']';
249
        }
250
        $onGetConnection = function ($conn) use ($cb, $hostname, $nameServers, $noncache, $pool, $proto) {
251
            if (!$conn || !$conn->isConnected()) {
252
                if($proto == 'udp'){
253
                    //Fail to  connect via udp, trying by tcp
254
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
255
                    return;
256
                }
257
                array_shift ($nameServers);
258
                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...
259
                    //Totally fail to resolve name
260
                    $cb(false);
261
                }else{
262
                    //Fail connect to curr Ns, but we can try another ns
263
                    $pool->get($hostname, $cb, $noncache, $nameServers, 'udp');
264
                }
265
            } else {
266
                $conn->get($hostname, function($response) use ($hostname, $cb, $proto, $noncache, $nameServers, $pool){
267
                    if($response === false && $proto == 'udp'){
268
                        //Fail to  connect via udp, trying by tcp
269
                        $pool->get($hostname, $cb, $noncache, $nameServers, 'tcp');
270
                    }else {
271
                        call_user_func($cb, $response);
272
                    }
273
                });
274
            }
275
		};
276
        @list($host, $type, $class) = explode(':', $hostname, 3);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
277
        if($type == 'AXFR'){
278
            $proto = 'tcp';
279
        }
280
        $this->getConnection($proto . '://' . $nameServer , $onGetConnection);
281
		return;
282
	}
283
}
284