Issues (37)

src/Factory.php (11 issues)

1
<?php
2
3
namespace IPLib;
4
5
use IPLib\Address\AddressInterface;
6
use IPLib\Range\Subnet;
7
use IPLib\Service\RangesFromBoundaryCalculator;
8
9
/**
10
 * Factory methods to build class instances.
11
 */
12
class Factory
13
{
14
    /**
15
     * @deprecated since 1.17.0: use the parseAddressString() method instead.
16
     * For upgrading:
17
     * - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
18
     * - if $mayIncludeZoneID is true, use the ParseStringFlag::MAY_INCLUDE_ZONEID flag
19
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
20
     *
21
     * @param string|mixed $address
22
     * @param bool $mayIncludePort
23
     * @param bool $mayIncludeZoneID
24
     * @param bool $supportNonDecimalIPv4
25
     *
26
     * @return \IPLib\Address\AddressInterface|null
27
     *
28
     * @see \IPLib\Factory::parseAddressString()
29
     * @since 1.1.0 added the $mayIncludePort argument
30
     * @since 1.3.0 added the $mayIncludeZoneID argument
31
     * @since 1.10.0 added the $supportNonDecimalIPv4 argument
32
     */
33 598
    public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
34
    {
35 598
        return static::parseAddressString($address, 0 + ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) + ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0) + ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
36
    }
37
38
    /**
39
     * Parse an IP address string.
40
     *
41
     * @param string|mixed $address the address to parse
42
     * @param int $flags A combination or zero or more flags
43
     *
44
     * @return \IPLib\Address\AddressInterface|null
45
     *
46
     * @see \IPLib\ParseStringFlag
47
     * @since 1.17.0
48
     */
49 1620
    public static function parseAddressString($address, $flags = 0)
50
    {
51 1620
        $result = null;
52 1620
        if ($result === null) {
0 ignored issues
show
The condition $result === null is always true.
Loading history...
53 1620
            $result = Address\IPv4::parseString($address, $flags);
54
        }
55 1620
        if ($result === null) {
56 735
            $result = Address\IPv6::parseString($address, $flags);
57
        }
58
59 1620
        return $result;
60
    }
61
62
    /**
63
     * Convert a byte array to an address instance.
64
     *
65
     * @param int[]|array $bytes
66
     *
67
     * @return \IPLib\Address\AddressInterface|null
68
     */
69 644
    public static function addressFromBytes(array $bytes)
70
    {
71 644
        $result = null;
72 644
        if ($result === null) {
0 ignored issues
show
The condition $result === null is always true.
Loading history...
73 644
            $result = Address\IPv4::fromBytes($bytes);
74
        }
75 644
        if ($result === null) {
76 300
            $result = Address\IPv6::fromBytes($bytes);
77
        }
78
79 644
        return $result;
80
    }
81
82
    /**
83
     * @deprecated since 1.17.0: use the parseRangeString() method instead.
84
     * For upgrading:
85
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
86
     *
87
     * @param string|mixed $range
88
     * @param bool $supportNonDecimalIPv4
89
     *
90
     * @return \IPLib\Range\RangeInterface|null
91
     *
92
     * @see \IPLib\Factory::parseRangeString()
93
     * @since 1.10.0 added the $supportNonDecimalIPv4 argument
94
     */
95 672
    public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
96
    {
97 672
        return static::parseRangeString($range, $supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0);
98
    }
99
100
    /**
101
     * Parse an IP range string.
102
     *
103
     * @param string $range
104
     * @param int $flags A combination or zero or more flags
105
     *
106
     * @return \IPLib\Range\RangeInterface|null
107
     *
108
     * @see \IPLib\ParseStringFlag
109
     * @since 1.17.0
110
     */
111 771
    public static function parseRangeString($range, $flags = 0)
112
    {
113 771
        $result = null;
114 771
        if ($result === null) {
0 ignored issues
show
The condition $result === null is always true.
Loading history...
115 771
            $result = Range\Subnet::parseString($range, $flags);
116
        }
117 771
        if ($result === null) {
118 155
            $result = Range\Pattern::parseString($range, $flags);
119
        }
120 771
        if ($result === null) {
121 59
            $result = Range\Single::parseString($range, $flags);
122
        }
123
124 771
        return $result;
125
    }
126
127
    /**
128
     * @deprecated since 1.17.0: use the getRangeFromBoundaries() method instead.
129
     * For upgrading:
130
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
131
     *
132
     * @param string|\IPLib\Address\AddressInterface|mixed $from
133
     * @param string|\IPLib\Address\AddressInterface|mixed $to
134
     * @param bool $supportNonDecimalIPv4
135
     *
136
     * @return \IPLib\Address\AddressInterface|null
137
     *
138
     * @see \IPLib\Factory::getRangeFromBoundaries()
139
     * @since 1.2.0
140
     * @since 1.10.0 added the $supportNonDecimalIPv4 argument
141
     */
142 35
    public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
143
    {
144 35
        return static::getRangeFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::getRangeF..._MAYBE_NON_DECIMAL : 0) returns the type IPLib\Range\Pattern|IPLi...ngle|IPLib\Range\Subnet which is incompatible with the documented return type IPLib\Address\AddressInterface|null.
Loading history...
145
    }
146
147
    /**
148
     * Create the smallest address range that comprises two addresses.
149
     *
150
     * @param string|\IPLib\Address\AddressInterface|mixed $from
151
     * @param string|\IPLib\Address\AddressInterface|mixed $to
152
     * @param int $flags A combination or zero or more flags
153
     *
154
     * @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
155
     *
156
     * @see \IPLib\ParseStringFlag
157
     * @since 1.17.0
158
     */
159 44
    public static function getRangeFromBoundaries($from, $to, $flags = 0)
160
    {
161 44
        list($from, $to) = self::parseBoundaries($from, $to, $flags);
162
163 44
        return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
0 ignored issues
show
$from of type void is incompatible with the type IPLib\Address\AddressInterface|null expected by parameter $from of IPLib\Factory::rangeFromBoundaryAddresses(). ( Ignorable by Annotation )

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

163
        return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses(/** @scrutinizer ignore-type */ $from, $to);
Loading history...
$to of type void is incompatible with the type IPLib\Address\AddressInterface|null expected by parameter $to of IPLib\Factory::rangeFromBoundaryAddresses(). ( Ignorable by Annotation )

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

163
        return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, /** @scrutinizer ignore-type */ $to);
Loading history...
The condition $to === false is always false.
Loading history...
164
    }
165
166
    /**
167
     * @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead.
168
     * For upgrading:
169
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
170
     *
171
     * @param string|\IPLib\Address\AddressInterface|mixed $from
172
     * @param string|\IPLib\Address\AddressInterface|mixed $to
173
     * @param bool $supportNonDecimalIPv4
174
     *
175
     * @return \IPLib\Range\Subnet[]|null
176
     *
177
     * @see \IPLib\Factory::getRangesFromBoundaries()
178
     * @since 1.14.0
179
     */
180 34
    public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
181
    {
182 34
        return static::getRangesFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
183
    }
184
185
    /**
186
     * Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
187
     *
188
     * @param string|\IPLib\Address\AddressInterface $from
189
     * @param string|\IPLib\Address\AddressInterface $to
190
     * @param int $flags A combination or zero or more flags
191
     *
192
     * @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
193
     *
194
     * @see \IPLib\ParseStringFlag
195
     * @since 1.17.0
196
     */
197 34
    public static function getRangesFromBoundaries($from, $to, $flags = 0)
198
    {
199 34
        list($from, $to) = self::parseBoundaries($from, $to, $flags);
200 34
        if ($from === false || $to === false || ($from === null && $to === null)) {
0 ignored issues
show
The condition $from === null is always false.
Loading history...
The condition $to === false is always false.
Loading history...
The condition $from === false is always true.
Loading history...
201 5
            return null;
202
        }
203 29
        if ($from === null || $to === null) {
204 2
            $address = $from ? $from : $to;
205
206 2
            return array(new Subnet($address, $address, $address->getNumberOfBits()));
207
        }
208 27
        $numberOfBits = $from->getNumberOfBits();
209 27
        if ($to->getNumberOfBits() !== $numberOfBits) {
210 1
            return null;
211
        }
212 26
        $calculator = new RangesFromBoundaryCalculator($numberOfBits);
213
214 26
        return $calculator->getRanges($from, $to);
215
    }
216
217
    /**
218
     * @param \IPLib\Address\AddressInterface|null $from
219
     * @param \IPLib\Address\AddressInterface|null $to
220
     *
221
     * @return \IPLib\Range\RangeInterface|null
222
     *
223
     * @since 1.2.0
224
     */
225 41
    protected static function rangeFromBoundaryAddresses($from = null, $to = null)
226
    {
227 41
        if (!$from instanceof AddressInterface && !$to instanceof AddressInterface) {
228 2
            $result = null;
229 39
        } elseif (!$to instanceof AddressInterface) {
230 2
            $result = Range\Single::fromAddress($from);
0 ignored issues
show
It seems like $from can also be of type null; however, parameter $address of IPLib\Range\Single::fromAddress() does only seem to accept IPLib\Address\AddressInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

230
            $result = Range\Single::fromAddress(/** @scrutinizer ignore-type */ $from);
Loading history...
231 39
        } elseif (!$from instanceof AddressInterface) {
232 2
            $result = Range\Single::fromAddress($to);
233
        } else {
234 37
            $result = null;
235 37
            $addressType = $from->getAddressType();
236 37
            if ($addressType === $to->getAddressType()) {
237 36
                $cmp = strcmp($from->getComparableString(), $to->getComparableString());
238 36
                if ($cmp === 0) {
239 11
                    $result = Range\Single::fromAddress($from);
240
                } else {
241 25
                    if ($cmp > 0) {
242
                        list($from, $to) = array($to, $from);
243
                    }
244 25
                    $fromBytes = $from->getBytes();
245 25
                    $toBytes = $to->getBytes();
246 25
                    $numBytes = count($fromBytes);
247 25
                    $sameBits = 0;
248 25
                    for ($byteIndex = 0; $byteIndex < $numBytes; $byteIndex++) {
249 25
                        $fromByte = $fromBytes[$byteIndex];
250 25
                        $toByte = $toBytes[$byteIndex];
251 25
                        if ($fromByte === $toByte) {
252 24
                            $sameBits += 8;
253
                        } else {
254 25
                            $differentBitsInByte = decbin($fromByte ^ $toByte);
255 25
                            $sameBits += 8 - strlen($differentBitsInByte);
256 25
                            break;
257
                        }
258
                    }
259 25
                    $result = static::parseRangeString($from->toString() . '/' . (string) $sameBits);
260
                }
261
            }
262
        }
263
264 41
        return $result;
265
    }
266
267
    /**
268
     * @param string|\IPLib\Address\AddressInterface $from
269
     * @param string|\IPLib\Address\AddressInterface $to
270
     * @param int $flags
271
     *
272
     * @return \IPLib\Address\AddressInterface[]|null[]|false[]
273
     */
274 78
    private static function parseBoundaries($from, $to, $flags = 0)
275
    {
276 78
        $result = array();
277 78
        foreach (array('from', 'to') as $param) {
278 78
            $value = $$param;
279 78
            if (!($value instanceof AddressInterface)) {
280 69
                $value = (string) $value;
281 69
                if ($value === '') {
282 8
                    $value = null;
283
                } else {
284 65
                    $value = static::parseAddressString($value, $flags);
285 65
                    if ($value === null) {
286 6
                        $value = false;
287
                    }
288
                }
289
            }
290 78
            $result[] = $value;
291
        }
292 78
        if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) {
293 51
            $result = array($result[1], $result[0]);
294
        }
295
296 78
        return $result;
297
    }
298
}
299