Factory.php 7.5 KB

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