Factory.php 11 KB

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