• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlocati / ip-lib / 18010325950

25 Sep 2025 02:11PM UTC coverage: 99.003% (+0.02%) from 98.988%
18010325950

push

github

web-flow
Add PHPStan checks (#108)

76 of 77 new or added lines in 12 files covered. (98.7%)

1 existing line in 1 file now uncovered.

1192 of 1204 relevant lines covered (99.0%)

194.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.33
/src/Factory.php
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
    public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
34
    {
35
        return static::parseAddressString($address, 0 + ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) + ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0) + ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
608✔
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
    public static function parseAddressString($address, $flags = 0)
50
    {
51
        if (($result = Address\IPv4::parseString($address, $flags)) !== null) {
1,643✔
52
            return $result;
914✔
53
        }
54
        if (($result = Address\IPv6::parseString($address, $flags)) !== null) {
749✔
55
            return $result;
686✔
56
        }
57

58
        return null;
65✔
59
    }
60

61
    /**
62
     * Convert a byte array to an address instance.
63
     *
64
     * @param array<int|mixed> $bytes
65
     *
66
     * @return \IPLib\Address\AddressInterface|null
67
     */
68
    public static function addressFromBytes(array $bytes)
69
    {
70
        if (($result = Address\IPv4::fromBytes($bytes)) !== null) {
657✔
71
            return $result;
347✔
72
        }
73
        if (($result = Address\IPv6::fromBytes($bytes)) !== null) {
310✔
74
            return $result;
310✔
75
        }
76

NEW
77
        return null;
×
78
    }
79

80
    /**
81
     * @deprecated since 1.17.0: use the parseRangeString() method instead.
82
     * For upgrading:
83
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
84
     *
85
     * @param string|mixed $range
86
     * @param bool $supportNonDecimalIPv4
87
     *
88
     * @return \IPLib\Range\RangeInterface|null
89
     *
90
     * @see \IPLib\Factory::parseRangeString()
91
     * @since 1.10.0 added the $supportNonDecimalIPv4 argument
92
     */
93
    public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
94
    {
95
        return static::parseRangeString($range, $supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0);
685✔
96
    }
97

98
    /**
99
     * Parse an IP range string.
100
     *
101
     * @param string|mixed $range
102
     * @param int $flags A combination or zero or more flags
103
     *
104
     * @return \IPLib\Range\RangeInterface|null
105
     *
106
     * @see \IPLib\ParseStringFlag
107
     * @since 1.17.0
108
     */
109
    public static function parseRangeString($range, $flags = 0)
110
    {
111
        $result = Range\Subnet::parseString($range, $flags);
784✔
112
        if ($result === null) {
784✔
113
            $result = Range\Pattern::parseString($range, $flags);
155✔
114
        }
115
        if ($result === null) {
784✔
116
            $result = Range\Single::parseString($range, $flags);
59✔
117
        }
118

119
        return $result;
784✔
120
    }
121

122
    /**
123
     * @deprecated since 1.17.0: use the getRangeFromBoundaries() method instead.
124
     * For upgrading:
125
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
126
     *
127
     * @param string|\IPLib\Address\AddressInterface|mixed $from
128
     * @param string|\IPLib\Address\AddressInterface|mixed $to
129
     * @param bool $supportNonDecimalIPv4
130
     *
131
     * @return \IPLib\Range\RangeInterface|null
132
     *
133
     * @see \IPLib\Factory::getRangeFromBoundaries()
134
     * @since 1.2.0
135
     * @since 1.10.0 added the $supportNonDecimalIPv4 argument
136
     */
137
    public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
138
    {
139
        return static::getRangeFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
35✔
140
    }
141

142
    /**
143
     * Create the smallest address range that comprises two addresses.
144
     *
145
     * @param string|\IPLib\Address\AddressInterface|mixed $from
146
     * @param string|\IPLib\Address\AddressInterface|mixed $to
147
     * @param int $flags A combination or zero or more flags
148
     *
149
     * @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
150
     *
151
     * @see \IPLib\ParseStringFlag
152
     * @since 1.17.0
153
     */
154
    public static function getRangeFromBoundaries($from, $to, $flags = 0)
155
    {
156
        list($from, $to) = self::parseBoundaries($from, $to, $flags);
44✔
157

158
        return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
44✔
159
    }
160

161
    /**
162
     * @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead.
163
     * For upgrading:
164
     * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
165
     *
166
     * @param string|\IPLib\Address\AddressInterface|mixed $from
167
     * @param string|\IPLib\Address\AddressInterface|mixed $to
168
     * @param bool $supportNonDecimalIPv4
169
     *
170
     * @return \IPLib\Range\Subnet[]|null
171
     *
172
     * @see \IPLib\Factory::getRangesFromBoundaries()
173
     * @since 1.14.0
174
     */
175
    public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
176
    {
177
        return static::getRangesFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
34✔
178
    }
179

180
    /**
181
     * Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
182
     *
183
     * @param string|\IPLib\Address\AddressInterface|mixed $from
184
     * @param string|\IPLib\Address\AddressInterface|mixed $to
185
     * @param int $flags A combination or zero or more flags
186
     *
187
     * @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
188
     *
189
     * @see \IPLib\ParseStringFlag
190
     * @since 1.17.0
191
     */
192
    public static function getRangesFromBoundaries($from, $to, $flags = 0)
193
    {
194
        list($from, $to) = self::parseBoundaries($from, $to, $flags);
34✔
195
        if ($from === false || $to === false || ($from === null && $to === null)) {
34✔
196
            return null;
5✔
197
        }
198
        if ($from === null || $to === null) {
29✔
199
            $address = $from ? $from : $to;
2✔
200
            /** @var AddressInterface $address */
201

202
            return array(new Subnet($address, $address, $address->getNumberOfBits()));
2✔
203
        }
204
        $numberOfBits = $from->getNumberOfBits();
27✔
205
        if ($to->getNumberOfBits() !== $numberOfBits) {
27✔
206
            return null;
1✔
207
        }
208
        $calculator = new RangesFromBoundaryCalculator($numberOfBits);
26✔
209

210
        return $calculator->getRanges($from, $to);
26✔
211
    }
212

213
    /**
214
     * @param \IPLib\Address\AddressInterface|null $from
215
     * @param \IPLib\Address\AddressInterface|null $to
216
     *
217
     * @return \IPLib\Range\RangeInterface|null
218
     *
219
     * @since 1.2.0
220
     */
221
    protected static function rangeFromBoundaryAddresses($from = null, $to = null)
222
    {
223
        if (!$from instanceof AddressInterface && !$to instanceof AddressInterface) {
41✔
224
            $result = null;
2✔
225
        } elseif (!$to instanceof AddressInterface) {
39✔
226
            $result = Range\Single::fromAddress($from);
2✔
227
        } elseif (!$from instanceof AddressInterface) {
39✔
228
            $result = Range\Single::fromAddress($to);
2✔
229
        } else {
230
            $result = null;
37✔
231
            $addressType = $from->getAddressType();
37✔
232
            if ($addressType === $to->getAddressType()) {
37✔
233
                $cmp = strcmp($from->getComparableString(), $to->getComparableString());
36✔
234
                if ($cmp === 0) {
36✔
235
                    $result = Range\Single::fromAddress($from);
11✔
236
                } else {
237
                    if ($cmp > 0) {
25✔
238
                        list($from, $to) = array($to, $from);
×
239
                    }
240
                    $fromBytes = $from->getBytes();
25✔
241
                    $toBytes = $to->getBytes();
25✔
242
                    $numBytes = count($fromBytes);
25✔
243
                    $sameBits = 0;
25✔
244
                    for ($byteIndex = 0; $byteIndex < $numBytes; $byteIndex++) {
25✔
245
                        $fromByte = $fromBytes[$byteIndex];
25✔
246
                        $toByte = $toBytes[$byteIndex];
25✔
247
                        if ($fromByte === $toByte) {
25✔
248
                            $sameBits += 8;
24✔
249
                        } else {
250
                            $differentBitsInByte = decbin($fromByte ^ $toByte);
25✔
251
                            $sameBits += 8 - strlen($differentBitsInByte);
25✔
252
                            break;
25✔
253
                        }
254
                    }
255
                    $result = static::parseRangeString($from->toString() . '/' . (string) $sameBits);
25✔
256
                }
257
            }
258
        }
259

260
        return $result;
41✔
261
    }
262

263
    /**
264
     * @param string|\IPLib\Address\AddressInterface|mixed $from
265
     * @param string|\IPLib\Address\AddressInterface|mixed $to
266
     * @param int $flags
267
     *
268
     * @return \IPLib\Address\AddressInterface[]|null[]|false[]
269
     */
270
    private static function parseBoundaries($from, $to, $flags = 0)
271
    {
272
        $result = array();
78✔
273
        foreach (array('from', 'to') as $param) {
78✔
274
            $value = $$param;
78✔
275
            if (!($value instanceof AddressInterface)) {
78✔
276
                $value = is_object($value) && method_exists($value, '__toString') || is_scalar($value) ? (string) $value : '';
69✔
277
                if ($value === '') {
69✔
278
                    $value = null;
8✔
279
                } else {
280
                    $value = static::parseAddressString($value, $flags);
65✔
281
                    if ($value === null) {
65✔
282
                        $value = false;
6✔
283
                    }
284
                }
285
            }
286
            $result[] = $value;
78✔
287
        }
288
        if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) {
78✔
289
            $result = array($result[1], $result[0]);
51✔
290
        }
291

292
        return $result;
78✔
293
    }
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc