Network::getFirstIP()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace devtoolboxuk\internetaddress;
4
5
class Network implements \Iterator
6
{
7
    use IpTrait;
8
    /**
9
     * @var IP
10
     */
11
    private $ip;
12
    /**
13
     * @var IP
14
     */
15
    private $netmask;
16
    /**
17
     * @var int
18
     */
19
    private $position = 0;
20
21
22
    public function __construct(IP $ip, IP $netmask)
23
    {
24
        $this->setIP($ip);
25
        $this->setNetmask($netmask);
26
    }
27
28
    /**
29
     *
30
     * @return string
31
     */
32
    public function __toString()
33
    {
34
        return $this->getCIDR();
0 ignored issues
show
Bug introduced by
The method getCIDR() does not exist on devtoolboxuk\internetaddress\Network. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

34
        return $this->/** @scrutinizer ignore-call */ getCIDR();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
35
    }
36
37
    public function getWildcard()
38
    {
39
        return new IP(inet_ntop(~$this->getNetmask()->inAddr()));
40
    }
41
42
    /**
43
     * @return IP
44
     */
45
    public function getNetmask()
46
    {
47
        return $this->netmask;
48
    }
49
50
    /**
51
     * @param IP ip
52
     * @throws \Exception
53
     */
54
    public function setNetmask(IP $ip)
55
    {
56
        if (!preg_match('/^1*0*$/', $ip->toBin())) {
57
            throw new \Exception('Invalid Netmask address format');
58
        }
59
60
        if (isset($this->ip) && $ip->getVersion() !== $this->ip->getVersion()) {
61
            throw new \Exception('Netmask version is not same as IP version');
62
        }
63
64
        $this->netmask = $ip;
65
    }
66
67
    /**
68
     * @return Range
69
     * @throws \Exception
70
     */
71
    public function getHosts()
72
    {
73
        $firstHost = $this->getNetwork();
74
        $lastHost = $this->getBroadcast();
75
76
        if ($this->ip->getVersion() === IPv4::version) {
77
            if ($this->getBlockSize() > 2) {
78
                $firstHost = IP::parseBin(substr($firstHost->toBin(), 0, $firstHost->getMaxPrefixLength() - 1) . '1');
79
                $lastHost = IP::parseBin(substr($lastHost->toBin(), 0, $lastHost->getMaxPrefixLength() - 1) . '0');
80
            }
81
        }
82
83
        return new Range($firstHost, $lastHost);
0 ignored issues
show
Bug introduced by
The type devtoolboxuk\internetaddress\Range was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
84
    }
85
86
    public function getNetwork()
87
    {
88
        return new IP(inet_ntop($this->getIP()->inAddr() & $this->getNetmask()->inAddr()));
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
89
    }
90
91
    /**
92
     * @return IP
93
     */
94
    public function getIP()
95
    {
96
        return $this->ip;
97
    }
98
99
    /**
100
     * @param IP ip
101
     * @throws \Exception
102
     */
103
    public function setIP(IP $ip)
104
    {
105
        if (isset($this->netmask) && $this->netmask->getVersion() !== $ip->getVersion()) {
106
            throw new \Exception('IP version is not same as Netmask version');
107
        }
108
109
        $this->ip = $ip;
110
    }
111
112
    /**
113
     * @return IP
114
     * @throws \Exception
115
     */
116
    public function getBroadcast()
117
    {
118
        return new IP(inet_ntop($this->getNetwork()->inAddr() | ~$this->getNetmask()->inAddr()));
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
119
    }
120
121
    /**
122
     * @return int|string
123
     */
124
    public function getBlockSize()
125
    {
126
        $maxPrefixLength = $this->ip->getMaxPrefixLength();
127
        $prefixLength = $this->getPrefixLength();
128
129
        if ($this->ip->getVersion() === IP::v6) {
0 ignored issues
show
Bug introduced by
The constant devtoolboxuk\internetaddress\IP::v6 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
130
            return bcpow('2', (string)($maxPrefixLength - $prefixLength));
131
        }
132
133
        return pow(2, $maxPrefixLength - $prefixLength);
134
    }
135
136
    /**
137
     * @return int
138
     */
139
    public function getPrefixLength()
140
    {
141
        return self::netmask2prefix($this->getNetmask());
0 ignored issues
show
Bug Best Practice introduced by
The method devtoolboxuk\internetadd...twork::netmask2prefix() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
        return self::/** @scrutinizer ignore-call */ netmask2prefix($this->getNetmask());
Loading history...
142
    }
143
144
    /**
145
     * @param IP ip
146
     * @return int
147
     */
148
    public function netmask2prefix(IP $ip)
149
    {
150
        return strlen(rtrim($ip->toBin(), 0));
151
    }
152
153
    /**
154
     * @param IP|Network $exclude
155
     * @return Network[]
156
     * @throws \Exception
157
     */
158
    public function exclude($exclude)
159
    {
160
        $exclude = self::parse($exclude);
161
162
        if (strcmp($exclude->getFirstIP()->inAddr(), $this->getLastIP()->inAddr()) > 0
163
            || strcmp($exclude->getLastIP()->inAddr(), $this->getFirstIP()->inAddr()) < 0
164
        ) {
165
            throw new \Exception('Exclude subnet not within target network');
166
        }
167
168
        $networks = array();
169
170
        $newPrefixLength = $this->getPrefixLength() + 1;
171
        if ($newPrefixLength > $this->ip->getMaxPrefixLength()) {
172
            return $networks;
173
        }
174
175
        $lower = clone $this;
176
        $lower->setPrefixLength($newPrefixLength);
177
178
        $upper = clone $lower;
179
        $upper->setIP($lower->getLastIP()->next());
180
181
        while ($newPrefixLength <= $exclude->getPrefixLength()) {
182
            $range = new Range($lower->getFirstIP(), $lower->getLastIP());
183
            if ($range->contains($exclude)) {
184
                $matched = $lower;
185
                $unmatched = $upper;
186
            } else {
187
                $matched = $upper;
188
                $unmatched = $lower;
189
            }
190
191
            $networks[] = clone $unmatched;
192
193
            if (++$newPrefixLength > $this->getNetwork()->getMaxPrefixLength()) {
194
                break;
195
            }
196
197
            $matched->setPrefixLength($newPrefixLength);
198
            $unmatched->setPrefixLength($newPrefixLength);
199
            $unmatched->setIP($matched->getLastIP()->next());
200
        }
201
202
        sort($networks);
203
204
        return $networks;
205
    }
206
207
    public static function parse($data)
208
    {
209
        if (preg_match('~^(.+?)/(\d+)$~', $data, $matches)) {
210
            $ip = IP::parse($matches[1]);
211
            $netmask = self::prefix2netmask((int)$matches[2], $ip->getVersion());
212
        } elseif (strpos($data, ' ')) {
213
            list($ip, $netmask) = explode(' ', $data, 2);
214
            $ip = IP::parse($ip);
215
            $netmask = IP::parse($netmask);
216
        } else {
217
            $ip = IP::parse($data);
218
            $netmask = self::prefix2netmask($ip->getMaxPrefixLength(), $ip->getVersion());
219
        }
220
221
        return new self($ip, $netmask);
222
    }
223
224
    /**
225
     * @param int $prefixLength
226
     * @param string $version
227
     * @return IP
228
     * @throws \Exception
229
     */
230
    public static function prefix2netmask($prefixLength, $version)
231
    {
232
        if (!in_array($version, array(IPv4::version, IPv6::version))) {
233
            throw new \Exception("Wrong IP version");
234
        }
235
236
        $maxPrefixLength = $version === IPv4::version
237
            ? IPv4::length
238
            : IP::v6Length;
0 ignored issues
show
Bug introduced by
The constant devtoolboxuk\internetaddress\IP::v6Length was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
239
240
        if (!is_numeric($prefixLength)
0 ignored issues
show
introduced by
The condition is_numeric($prefixLength) is always true.
Loading history...
241
            || !($prefixLength >= 0 && $prefixLength <= $maxPrefixLength)
242
        ) {
243
            throw new \Exception('Invalid prefix length');
244
        }
245
246
        $binIP = str_pad(str_pad('', (int)$prefixLength, '1'), $maxPrefixLength, '0');
247
248
        return IP::parseBin($binIP);
249
    }
250
251
    /**
252
     * @return IP
253
     */
254
    public function getFirstIP()
255
    {
256
        return $this->getNetwork();
257
    }
258
259
    /**
260
     * @return IP
261
     * @throws \Exception
262
     */
263
    public function getLastIP()
264
    {
265
        return $this->getBroadcast();
266
    }
267
268
    public function setPrefixLength($prefixLength)
269
    {
270
        $this->setNetmask(self::prefix2netmask((int)$prefixLength, $this->ip->getVersion()));
271
    }
272
273
    /**
274
     * @param int $prefixLength
275
     * @return Network[]
276
     * @throws \Exception
277
     */
278
    public function moveTo($prefixLength)
279
    {
280
        $maxPrefixLength = $this->ip->getMaxPrefixLength();
281
282
        if ($prefixLength <= $this->getPrefixLength() || $prefixLength > $maxPrefixLength) {
283
            throw new \Exception('Invalid prefix length ');
284
        }
285
286
        $netmask = self::prefix2netmask($prefixLength, $this->ip->getVersion());
287
        $networks = array();
288
289
        $subnet = clone $this;
290
        $subnet->setPrefixLength($prefixLength);
291
292
        while ($subnet->ip->inAddr() <= $this->getLastIP()->inAddr()) {
293
            $networks[] = $subnet;
294
            $subnet = new self($subnet->getLastIP()->next(), $netmask);
295
        }
296
297
        return $networks;
298
    }
299
300
    /**
301
     * @return IP|mixed
302
     * @throws \Exception
303
     */
304
    public function current()
305
    {
306
        return $this->getFirstIP()->next($this->position);
307
    }
308
309
    /**
310
     * @return int|mixed
311
     */
312
    public function key()
313
    {
314
        return $this->position;
315
    }
316
317
    public function next()
318
    {
319
        ++$this->position;
320
    }
321
322
    public function rewind()
323
    {
324
        $this->position = 0;
325
    }
326
327
    /**
328
     * @return bool
329
     * @throws \Exception
330
     */
331
    public function valid()
332
    {
333
        return strcmp($this->getFirstIP()->next($this->position)->inAddr(), $this->getLastIP()->inAddr()) <= 0;
334
    }
335
336
    /**
337
     * @return int
338
     */
339
    public function count()
340
    {
341
        return (integer)$this->getBlockSize();
342
    }
343
344
}
345