Completed
Push — master ( b37ac4...882bc0 )
by Michele
11s
created

Factory::rangeFromBoundaries()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 3
nc 4
nop 3
crap 3
1
<?php
2
3
namespace IPLib;
4
5
use IPLib\Address\AddressInterface;
6
use IPLib\Range\Subnet;
7
use IPLib\Service\RangesFromBounradyCalculator;
8
9
/**
10
 * Factory methods to build class instances.
11
 */
12
class Factory
13
{
14
    /**
15
     * Parse an IP address string.
16
     *
17
     * @param string $address the address to parse
18
     * @param bool $mayIncludePort set to false to avoid parsing addresses with ports
19
     * @param bool $mayIncludeZoneID set to false to avoid parsing IPv6 addresses with zone IDs (see RFC 4007)
20
     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
21
     *
22
     * @return \IPLib\Address\AddressInterface|null
23
     */
24 1167 View Code Duplication
    public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
25
    {
26 1167
        $result = null;
27 1167
        if ($result === null) {
28 1167
            $result = Address\IPv4::fromString($address, $mayIncludePort, $supportNonDecimalIPv4);
29
        }
30 1167
        if ($result === null) {
31 500
            $result = Address\IPv6::fromString($address, $mayIncludePort, $mayIncludeZoneID);
32
        }
33
34 1167
        return $result;
35
    }
36
37
    /**
38
     * Convert a byte array to an address instance.
39
     *
40
     * @param int[]|array $bytes
41
     *
42
     * @return \IPLib\Address\AddressInterface|null
43
     */
44 561
    public static function addressFromBytes(array $bytes)
45
    {
46 561
        $result = null;
47 561
        if ($result === null) {
48 561
            $result = Address\IPv4::fromBytes($bytes);
49
        }
50 561
        if ($result === null) {
51 272
            $result = Address\IPv6::fromBytes($bytes);
52
        }
53
54 561
        return $result;
55
    }
56
57
    /**
58
     * Parse an IP range string.
59
     *
60
     * @param string $range
61
     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
62
     *
63
     * @return \IPLib\Range\RangeInterface|null
64
     */
65 639 View Code Duplication
    public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
66
    {
67 639
        $result = null;
68 639
        if ($result === null) {
69 639
            $result = Range\Subnet::fromString($range, $supportNonDecimalIPv4);
70
        }
71 639
        if ($result === null) {
72 117
            $result = Range\Pattern::fromString($range, $supportNonDecimalIPv4);
73
        }
74 639
        if ($result === null) {
75 38
            $result = Range\Single::fromString($range, $supportNonDecimalIPv4);
76
        }
77
78 639
        return $result;
79
    }
80
81
    /**
82
     * Create the smallest address range that comprises two addresses.
83
     *
84
     * @param string|\IPLib\Address\AddressInterface $from
85
     * @param string|\IPLib\Address\AddressInterface $to
86
     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
87
     *
88
     * @return \IPLib\Range\RangeInterface|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
89
     */
90 42
    public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
91
    {
92 42
        list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
93
94 42
        return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
95
    }
96
97
    /**
98
     * Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
99
     *
100
     * @param string|\IPLib\Address\AddressInterface $from
101
     * @param string|\IPLib\Address\AddressInterface $to
102
     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
103
     *
104
     * @return \IPLib\Range\Subnet[]|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
105
     */
106 33
    public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
107
    {
108 33
        list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
109 33
        if (($from === false || $to === false) || ($from === null && $to === null)) {
110 5
            return null;
111
        }
112 28
        if ($from === null || $to === null) {
113 2
            $address = $from ? $from : $to;
114
115 2
            return array(new Subnet($address, $address, $address->getNumberOfBits()));
116
        }
117 26
        $numberOfBits = $from->getNumberOfBits();
118 26
        if ($to->getNumberOfBits() !== $numberOfBits) {
119 1
            return null;
120
        }
121 25
        $calculator = new RangesFromBounradyCalculator($numberOfBits);
122
123 25
        return $calculator->getRanges($from, $to);
124
    }
125
126
    /**
127
     * @param \IPLib\Address\AddressInterface $from
128
     * @param \IPLib\Address\AddressInterface $to
129
     *
130
     * @return \IPLib\Range\RangeInterface|null
131
     */
132 39
    protected static function rangeFromBoundaryAddresses(AddressInterface $from = null, AddressInterface $to = null)
133
    {
134 39
        if ($from === null && $to === null) {
135 2
            $result = null;
136 37
        } elseif ($to === null) {
137 2
            $result = Range\Single::fromAddress($from);
138 37
        } elseif ($from === null) {
139 2
            $result = Range\Single::fromAddress($to);
140
        } else {
141 35
            $result = null;
142 35
            $addressType = $from->getAddressType();
143 35
            if ($addressType === $to->getAddressType()) {
144 34
                $cmp = strcmp($from->getComparableString(), $to->getComparableString());
145 34
                if ($cmp === 0) {
146 11
                    $result = Range\Single::fromAddress($from);
147
                } else {
148 23
                    if ($cmp > 0) {
149
                        list($from, $to) = array($to, $from);
150
                    }
151 23
                    $fromBytes = $from->getBytes();
152 23
                    $toBytes = $to->getBytes();
153 23
                    $numBytes = count($fromBytes);
154 23
                    $sameBits = 0;
155 23
                    for ($byteIndex = 0; $byteIndex < $numBytes; $byteIndex++) {
156 23
                        $fromByte = $fromBytes[$byteIndex];
157 23
                        $toByte = $toBytes[$byteIndex];
158 23
                        if ($fromByte === $toByte) {
159 22
                            $sameBits += 8;
160
                        } else {
161 23
                            $differentBitsInByte = decbin($fromByte ^ $toByte);
162 23
                            $sameBits += 8 - strlen($differentBitsInByte);
163 23
                            break;
164
                        }
165
                    }
166 23
                    $result = static::rangeFromString($from->toString(true) . '/' . (string) $sameBits);
167
                }
168
            }
169
        }
170
171 39
        return $result;
172
    }
173
174
    /**
175
     * @param string|\IPLib\Address\AddressInterface $from
176
     * @param string|\IPLib\Address\AddressInterface $to
177
     * @param bool $supportNonDecimalIPv4
178
     *
179
     * @return \IPLib\Address\AddressInterface[]|null[]|false[]
180
     */
181 75
    private static function parseBoundaries($from, $to, $supportNonDecimalIPv4 = false)
182
    {
183 75
        $result = array();
184 75
        foreach (array('from', 'to') as $param) {
185 75
            $value = $$param;
186 75
            if (!($value instanceof AddressInterface)) {
187 66
                $value = (string) $value;
188 66
                if ($value === '') {
189 8
                    $value = null;
190
                } else {
191 62
                    $value = static::addressFromString($value, true, true, $supportNonDecimalIPv4);
192 62
                    if ($value === null) {
193 6
                        $value = false;
194
                    }
195
                }
196
            }
197 75
            $result[] = $value;
198
        }
199 75
        if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) {
200 48
            $result = array($result[1], $result[0]);
201
        }
202
203 75
        return $result;
204
    }
205
}
206