Completed
Push — master ( 918974...334de5 )
by Michele
15s queued 10s
created

Subnet::getReverseDNSLookupName()   B

Complexity

Conditions 7
Paths 24

Size

Total Lines 36

Duplication

Lines 12
Ratio 33.33 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 12
loc 36
ccs 0
cts 0
cp 0
rs 8.4106
c 0
b 0
f 0
cc 7
nc 24
nop 0
crap 56
1
<?php
2
3
namespace IPLib\Range;
4
5
use IPLib\Address\AddressInterface;
6
use IPLib\Address\IPv4;
7
use IPLib\Address\IPv6;
8
use IPLib\Address\Type as AddressType;
9
use IPLib\Factory;
10
11
/**
12
 * Represents an address range in subnet format (eg CIDR).
13
 *
14
 * @example 127.0.0.1/32
15
 * @example ::/8
16
 */
17
class Subnet extends AbstractRange
18
{
19
    /**
20
     * Starting address of the range.
21
     *
22
     * @var \IPLib\Address\AddressInterface
23
     */
24
    protected $fromAddress;
25
26
    /**
27
     * Final address of the range.
28
     *
29
     * @var \IPLib\Address\AddressInterface
30
     */
31
    protected $toAddress;
32
33
    /**
34
     * Number of the same bits of the range.
35
     *
36
     * @var int
37
     */
38
    protected $networkPrefix;
39
40
    /**
41
     * The type of the range of this IP range.
42
     *
43
     * @var int|null
44
     */
45
    protected $rangeType;
46
47
    /**
48
     * The 6to4 address IPv6 address range.
49
     *
50
     * @var self|null
51
     */
52
    private static $sixToFour;
53
54
    /**
55
     * Initializes the instance.
56
     *
57
     * @param \IPLib\Address\AddressInterface $fromAddress
58
     * @param \IPLib\Address\AddressInterface $toAddress
59
     * @param int $networkPrefix
60
     *
61
     * @internal
62 512
     */
63
    public function __construct(AddressInterface $fromAddress, AddressInterface $toAddress, $networkPrefix)
64 512
    {
65 512
        $this->fromAddress = $fromAddress;
66 512
        $this->toAddress = $toAddress;
67 512
        $this->networkPrefix = $networkPrefix;
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     *
73
     * @see \IPLib\Range\RangeInterface::__toString()
74 44
     */
75
    public function __toString()
76 44
    {
77
        return $this->toString();
78
    }
79
80
    /**
81
     * Try get the range instance starting from its string representation.
82
     *
83
     * @param string|mixed $range
84
     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
85
     *
86
     * @return static|null
87 625
     */
88
    public static function fromString($range, $supportNonDecimalIPv4 = false)
89 625
    {
90 3
        if (!is_string($range)) {
91
            return null;
92 622
        }
93 622
        $parts = explode('/', $range);
94 103
        if (count($parts) !== 2) {
95
            return null;
96 519
        }
97 519
        $address = Factory::addressFromString($parts[0], true, true, $supportNonDecimalIPv4);
98 3
        if ($address === null) {
99
            return null;
100 516
        }
101 2
        if (!preg_match('/^[0-9]{1,9}$/', $parts[1])) {
102
            return null;
103 514
        }
104 514
        $networkPrefix = (int) $parts[1];
105 514
        $addressBytes = $address->getBytes();
106 514
        $totalBytes = count($addressBytes);
107 514
        $numDifferentBits = $totalBytes * 8 - $networkPrefix;
108 2
        if ($numDifferentBits < 0) {
109
            return null;
110 512
        }
111 512
        $numSameBytes = $networkPrefix >> 3;
112 512
        $sameBytes = array_slice($addressBytes, 0, $numSameBytes);
113 512
        $differentBytesStart = ($totalBytes === $numSameBytes) ? array() : array_fill(0, $totalBytes - $numSameBytes, 0);
114 512
        $differentBytesEnd = ($totalBytes === $numSameBytes) ? array() : array_fill(0, $totalBytes - $numSameBytes, 255);
115 512
        $startSameBits = $networkPrefix % 8;
116 368
        if ($startSameBits !== 0) {
117 368
            $varyingByte = $addressBytes[$numSameBytes];
118 368
            $differentBytesStart[0] = $varyingByte & bindec(str_pad(str_repeat('1', $startSameBits), 8, '0', STR_PAD_RIGHT));
119
            $differentBytesEnd[0] = $differentBytesStart[0] + bindec(str_repeat('1', 8 - $startSameBits));
120
        }
121 512
122 512
        return new static(
123 512
            Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart)),
124 512
            Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd)),
125
            $networkPrefix
126
        );
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     *
132
     * @see \IPLib\Range\RangeInterface::toString()
133 279
     */
134
    public function toString($long = false)
135 279
    {
136
        return $this->fromAddress->toString($long) . '/' . $this->networkPrefix;
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     *
142
     * @see \IPLib\Range\RangeInterface::getAddressType()
143 472
     */
144
    public function getAddressType()
145 472
    {
146
        return $this->fromAddress->getAddressType();
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     *
152
     * @see \IPLib\Range\RangeInterface::getStartAddress()
153 61
     */
154
    public function getStartAddress()
155 61
    {
156
        return $this->fromAddress;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     *
162
     * @see \IPLib\Range\RangeInterface::getEndAddress()
163 5
     */
164
    public function getEndAddress()
165 5
    {
166
        return $this->toAddress;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     *
172
     * @see \IPLib\Range\RangeInterface::getComparableStartString()
173 467
     */
174
    public function getComparableStartString()
175 467
    {
176
        return $this->fromAddress->getComparableString();
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     *
182
     * @see \IPLib\Range\RangeInterface::getComparableEndString()
183 467
     */
184
    public function getComparableEndString()
185 467
    {
186
        return $this->toAddress->getComparableString();
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     *
192
     * @see \IPLib\Range\RangeInterface::asSubnet()
193 72
     */
194
    public function asSubnet()
195 72
    {
196 1
        return $this;
197
    }
198
199 72
    /**
200
     * Get the pattern (asterisk) representation (if applicable) of this range.
201
     *
202
     * @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
203
     */
204
    public function asPattern()
205
    {
206
        $address = $this->getStartAddress();
207 236
        $networkPrefix = $this->getNetworkPrefix();
208
        switch ($address->getAddressType()) {
209 236 View Code Duplication
            case AddressType::T_IPv4:
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...
210
                return $networkPrefix % 8 === 0 ? new Pattern($address, $address, 4 - $networkPrefix / 8) : null;
211 View Code Duplication
            case AddressType::T_IPv6:
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...
212
                return $networkPrefix % 16 === 0 ? new Pattern($address, $address, 8 - $networkPrefix / 16) : null;
213
        }
214
    }
215
216
    /**
217 56
     * Get the 6to4 address IPv6 address range.
218
     *
219 56
     * @return self
220 56
     */
221 56
    public static function get6to4()
222 56
    {
223 18
        if (self::$sixToFour === null) {
224 38
            self::$sixToFour = self::fromString('2002::/16');
225 38
        }
226
227
        return self::$sixToFour;
228
    }
229
230
    /**
231
     * Get subnet prefix.
232
     *
233
     * @return int
234 19
     */
235
    public function getNetworkPrefix()
236 19
    {
237 1
        return $this->networkPrefix;
238
    }
239 18
240 18
    /**
241 18
     * {@inheritdoc}
242 10
     *
243 10
     * @see \IPLib\Range\RangeInterface::getSubnetMask()
244
     */
245 18
    public function getSubnetMask()
246 13
    {
247
        if ($this->getAddressType() !== AddressType::T_IPv4) {
248 18
            return null;
249
        }
250 18
        $bytes = array();
251
        $prefix = $this->getNetworkPrefix();
252
        while ($prefix >= 8) {
253
            $bytes[] = 255;
254
            $prefix -= 8;
255
        }
256
        if ($prefix !== 0) {
257
            $bytes[] = bindec(str_pad(str_repeat('1', $prefix), 8, '0'));
258
        }
259
        $bytes = array_pad($bytes, 4, 0);
260
261
        return IPv4::fromBytes($bytes);
262
    }
263
264
    /**
265
     * {@inheritdoc}
266
     *
267
     * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
268
     */
269
    public function getReverseDNSLookupName()
270
    {
271
        switch ($this->getAddressType()) {
272 View Code Duplication
            case AddressType::T_IPv4:
1 ignored issue
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...
273
                $unitSize = 8; // bytes
274
                $maxUnits = 4;
275
                $isHex = false;
276
                $rxUnit = '\d+';
277
                break;
278 View Code Duplication
            case AddressType::T_IPv6:
1 ignored issue
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...
279
                $unitSize = 4; // nibbles
280
                $maxUnits = 32;
281
                $isHex = true;
282
                $rxUnit = '[0-9A-Fa-f]';
283
                break;
284
        }
285
        $totBits = $unitSize * $maxUnits;
2 ignored issues
show
Bug introduced by
The variable $unitSize does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $maxUnits does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
286
        $prefixUnits = (int) ($this->networkPrefix / $unitSize);
287
        $extraBits = ($totBits - $this->networkPrefix) % $unitSize;
288
        if ($extraBits !== 0) {
289
            $prefixUnits += 1;
290
        }
291
        $numVariants = 1 << $extraBits;
292
        $result = array();
293
        $unitsToRemove = $maxUnits - $prefixUnits;
294
        $initialPointer = preg_replace("/^(({$rxUnit})\.){{$unitsToRemove}}/", '', $this->getStartAddress()->getReverseDNSLookupName());
1 ignored issue
show
Bug introduced by
The variable $rxUnit does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
295
        $chunks = explode('.', $initialPointer, 2);
296
        for ($index = 0; $index < $numVariants; $index++) {
297
            if ($index !== 0) {
298
                $chunks[0] = $isHex ? dechex(1 + hexdec($chunks[0])) : (string) (1 + (int) $chunks[0]);
1 ignored issue
show
Bug introduced by
The variable $isHex does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
299
            }
300
            $result[] = implode('.', $chunks);
301
        }
302
303
        return $result;
304
    }
305
}
306