Factory.php
| 1 | <?php |
| 2 | |
| 3 | namespace IAWPSCOPED\IPLib; |
| 4 | |
| 5 | use IAWPSCOPED\IPLib\Address\AddressInterface; |
| 6 | use IAWPSCOPED\IPLib\Range\Subnet; |
| 7 | use IAWPSCOPED\IPLib\Service\RangesFromBoundaryCalculator; |
| 8 | /** |
| 9 | * Factory methods to build class instances. |
| 10 | * @internal |
| 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)); |
| 36 | } |
| 37 | /** |
| 38 | * Parse an IP address string. |
| 39 | * |
| 40 | * @param string|mixed $address the address to parse |
| 41 | * @param int $flags A combination or zero or more flags |
| 42 | * |
| 43 | * @return \IPLib\Address\AddressInterface|null |
| 44 | * |
| 45 | * @see \IPLib\ParseStringFlag |
| 46 | * @since 1.17.0 |
| 47 | */ |
| 48 | public static function parseAddressString($address, $flags = 0) |
| 49 | { |
| 50 | $result = null; |
| 51 | if ($result === null) { |
| 52 | $result = Address\IPv4::parseString($address, $flags); |
| 53 | } |
| 54 | if ($result === null) { |
| 55 | $result = Address\IPv6::parseString($address, $flags); |
| 56 | } |
| 57 | return $result; |
| 58 | } |
| 59 | /** |
| 60 | * Convert a byte array to an address instance. |
| 61 | * |
| 62 | * @param int[]|array $bytes |
| 63 | * |
| 64 | * @return \IPLib\Address\AddressInterface|null |
| 65 | */ |
| 66 | public static function addressFromBytes(array $bytes) |
| 67 | { |
| 68 | $result = null; |
| 69 | if ($result === null) { |
| 70 | $result = Address\IPv4::fromBytes($bytes); |
| 71 | } |
| 72 | if ($result === null) { |
| 73 | $result = Address\IPv6::fromBytes($bytes); |
| 74 | } |
| 75 | return $result; |
| 76 | } |
| 77 | /** |
| 78 | * @deprecated since 1.17.0: use the parseRangeString() method instead. |
| 79 | * For upgrading: |
| 80 | * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag |
| 81 | * |
| 82 | * @param string|mixed $range |
| 83 | * @param bool $supportNonDecimalIPv4 |
| 84 | * |
| 85 | * @return \IPLib\Range\RangeInterface|null |
| 86 | * |
| 87 | * @see \IPLib\Factory::parseRangeString() |
| 88 | * @since 1.10.0 added the $supportNonDecimalIPv4 argument |
| 89 | */ |
| 90 | public static function rangeFromString($range, $supportNonDecimalIPv4 = \false) |
| 91 | { |
| 92 | return static::parseRangeString($range, $supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0); |
| 93 | } |
| 94 | /** |
| 95 | * Parse an IP range string. |
| 96 | * |
| 97 | * @param string $range |
| 98 | * @param int $flags A combination or zero or more flags |
| 99 | * |
| 100 | * @return \IPLib\Range\RangeInterface|null |
| 101 | * |
| 102 | * @see \IPLib\ParseStringFlag |
| 103 | * @since 1.17.0 |
| 104 | */ |
| 105 | public static function parseRangeString($range, $flags = 0) |
| 106 | { |
| 107 | $result = null; |
| 108 | if ($result === null) { |
| 109 | $result = Range\Subnet::parseString($range, $flags); |
| 110 | } |
| 111 | if ($result === null) { |
| 112 | $result = Range\Pattern::parseString($range, $flags); |
| 113 | } |
| 114 | if ($result === null) { |
| 115 | $result = Range\Single::parseString($range, $flags); |
| 116 | } |
| 117 | return $result; |
| 118 | } |
| 119 | /** |
| 120 | * @deprecated since 1.17.0: use the getRangeFromBoundaries() method instead. |
| 121 | * For upgrading: |
| 122 | * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag |
| 123 | * |
| 124 | * @param string|\IPLib\Address\AddressInterface|mixed $from |
| 125 | * @param string|\IPLib\Address\AddressInterface|mixed $to |
| 126 | * @param bool $supportNonDecimalIPv4 |
| 127 | * |
| 128 | * @return \IPLib\Address\AddressInterface|null |
| 129 | * |
| 130 | * @see \IPLib\Factory::getRangeFromBoundaries() |
| 131 | * @since 1.2.0 |
| 132 | * @since 1.10.0 added the $supportNonDecimalIPv4 argument |
| 133 | */ |
| 134 | public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = \false) |
| 135 | { |
| 136 | return static::getRangeFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0)); |
| 137 | } |
| 138 | /** |
| 139 | * Create the smallest address range that comprises two addresses. |
| 140 | * |
| 141 | * @param string|\IPLib\Address\AddressInterface|mixed $from |
| 142 | * @param string|\IPLib\Address\AddressInterface|mixed $to |
| 143 | * @param int $flags A combination or zero or more flags |
| 144 | * |
| 145 | * @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 |
| 146 | * |
| 147 | * @see \IPLib\ParseStringFlag |
| 148 | * @since 1.17.0 |
| 149 | */ |
| 150 | public static function getRangeFromBoundaries($from, $to, $flags = 0) |
| 151 | { |
| 152 | list($from, $to) = self::parseBoundaries($from, $to, $flags); |
| 153 | return $from === \false || $to === \false ? null : static::rangeFromBoundaryAddresses($from, $to); |
| 154 | } |
| 155 | /** |
| 156 | * @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead. |
| 157 | * For upgrading: |
| 158 | * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag |
| 159 | * |
| 160 | * @param string|\IPLib\Address\AddressInterface|mixed $from |
| 161 | * @param string|\IPLib\Address\AddressInterface|mixed $to |
| 162 | * @param bool $supportNonDecimalIPv4 |
| 163 | * |
| 164 | * @return \IPLib\Range\Subnet[]|null |
| 165 | * |
| 166 | * @see \IPLib\Factory::getRangesFromBoundaries() |
| 167 | * @since 1.14.0 |
| 168 | */ |
| 169 | public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = \false) |
| 170 | { |
| 171 | return static::getRangesFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0)); |
| 172 | } |
| 173 | /** |
| 174 | * Create a list of Range instances that exactly describes all the addresses between the two provided addresses. |
| 175 | * |
| 176 | * @param string|\IPLib\Address\AddressInterface $from |
| 177 | * @param string|\IPLib\Address\AddressInterface $to |
| 178 | * @param int $flags A combination or zero or more flags |
| 179 | * |
| 180 | * @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 |
| 181 | * |
| 182 | * @see \IPLib\ParseStringFlag |
| 183 | * @since 1.17.0 |
| 184 | */ |
| 185 | public static function getRangesFromBoundaries($from, $to, $flags = 0) |
| 186 | { |
| 187 | list($from, $to) = self::parseBoundaries($from, $to, $flags); |
| 188 | if ($from === \false || $to === \false || $from === null && $to === null) { |
| 189 | return null; |
| 190 | } |
| 191 | if ($from === null || $to === null) { |
| 192 | $address = $from ? $from : $to; |
| 193 | return array(new Subnet($address, $address, $address->getNumberOfBits())); |
| 194 | } |
| 195 | $numberOfBits = $from->getNumberOfBits(); |
| 196 | if ($to->getNumberOfBits() !== $numberOfBits) { |
| 197 | return null; |
| 198 | } |
| 199 | $calculator = new RangesFromBoundaryCalculator($numberOfBits); |
| 200 | return $calculator->getRanges($from, $to); |
| 201 | } |
| 202 | /** |
| 203 | * @param \IPLib\Address\AddressInterface|null $from |
| 204 | * @param \IPLib\Address\AddressInterface|null $to |
| 205 | * |
| 206 | * @return \IPLib\Range\RangeInterface|null |
| 207 | * |
| 208 | * @since 1.2.0 |
| 209 | */ |
| 210 | protected static function rangeFromBoundaryAddresses($from = null, $to = null) |
| 211 | { |
| 212 | if (!$from instanceof AddressInterface && !$to instanceof AddressInterface) { |
| 213 | $result = null; |
| 214 | } elseif (!$to instanceof AddressInterface) { |
| 215 | $result = Range\Single::fromAddress($from); |
| 216 | } elseif (!$from instanceof AddressInterface) { |
| 217 | $result = Range\Single::fromAddress($to); |
| 218 | } else { |
| 219 | $result = null; |
| 220 | $addressType = $from->getAddressType(); |
| 221 | if ($addressType === $to->getAddressType()) { |
| 222 | $cmp = \strcmp($from->getComparableString(), $to->getComparableString()); |
| 223 | if ($cmp === 0) { |
| 224 | $result = Range\Single::fromAddress($from); |
| 225 | } else { |
| 226 | if ($cmp > 0) { |
| 227 | list($from, $to) = array($to, $from); |
| 228 | } |
| 229 | $fromBytes = $from->getBytes(); |
| 230 | $toBytes = $to->getBytes(); |
| 231 | $numBytes = \count($fromBytes); |
| 232 | $sameBits = 0; |
| 233 | for ($byteIndex = 0; $byteIndex < $numBytes; $byteIndex++) { |
| 234 | $fromByte = $fromBytes[$byteIndex]; |
| 235 | $toByte = $toBytes[$byteIndex]; |
| 236 | if ($fromByte === $toByte) { |
| 237 | $sameBits += 8; |
| 238 | } else { |
| 239 | $differentBitsInByte = \decbin($fromByte ^ $toByte); |
| 240 | $sameBits += 8 - \strlen($differentBitsInByte); |
| 241 | break; |
| 242 | } |
| 243 | } |
| 244 | $result = static::parseRangeString($from->toString() . '/' . (string) $sameBits); |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | return $result; |
| 249 | } |
| 250 | /** |
| 251 | * @param string|\IPLib\Address\AddressInterface $from |
| 252 | * @param string|\IPLib\Address\AddressInterface $to |
| 253 | * @param int $flags |
| 254 | * |
| 255 | * @return \IPLib\Address\AddressInterface[]|null[]|false[] |
| 256 | */ |
| 257 | private static function parseBoundaries($from, $to, $flags = 0) |
| 258 | { |
| 259 | $result = array(); |
| 260 | foreach (array('from', 'to') as $param) { |
| 261 | $value = ${$param}; |
| 262 | if (!$value instanceof AddressInterface) { |
| 263 | $value = (string) $value; |
| 264 | if ($value === '') { |
| 265 | $value = null; |
| 266 | } else { |
| 267 | $value = static::parseAddressString($value, $flags); |
| 268 | if ($value === null) { |
| 269 | $value = \false; |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | $result[] = $value; |
| 274 | } |
| 275 | if ($result[0] && $result[1] && \strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) { |
| 276 | $result = array($result[1], $result[0]); |
| 277 | } |
| 278 | return $result; |
| 279 | } |
| 280 | } |
| 281 |