AbstractRange.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <?php
  2. namespace IPLib\Range;
  3. use IPLib\Address\AddressInterface;
  4. use IPLib\Address\IPv4;
  5. use IPLib\Address\IPv6;
  6. use IPLib\Address\Type as AddressType;
  7. use IPLib\Factory;
  8. use IPLib\Service\BinaryMath;
  9. use OutOfBoundsException;
  10. /**
  11. * Base class for range classes.
  12. */
  13. abstract class AbstractRange implements RangeInterface
  14. {
  15. /**
  16. * {@inheritdoc}
  17. *
  18. * @see \IPLib\Range\RangeInterface::getRangeType()
  19. */
  20. public function getRangeType()
  21. {
  22. /** @var \IPLib\Range\Pattern|\IPLib\Range\Subnet $this */
  23. // @phpstan-ignore varTag.nativeType
  24. if ($this->rangeType === null) {
  25. $addressType = $this->getAddressType();
  26. if ($addressType === AddressType::T_IPv6 && Subnet::get6to4()->containsRange($this)) {
  27. $fromAddress = $this->fromAddress;
  28. /** @var IPv6 $fromAddress */
  29. $toAddress = $this->toAddress;
  30. /** @var IPv6 $toAddress */
  31. $range = Factory::getRangeFromBoundaries($fromAddress->toIPv4(), $toAddress->toIPv4());
  32. /** @var RangeInterface $range */
  33. $this->rangeType = $range->getRangeType();
  34. } else {
  35. switch ($addressType) {
  36. case AddressType::T_IPv4:
  37. $defaultType = IPv4::getDefaultReservedRangeType();
  38. $reservedRanges = IPv4::getReservedRanges();
  39. break;
  40. case AddressType::T_IPv6:
  41. $defaultType = IPv6::getDefaultReservedRangeType();
  42. $reservedRanges = IPv6::getReservedRanges();
  43. break;
  44. }
  45. $rangeType = null;
  46. foreach ($reservedRanges as $reservedRange) {
  47. $rangeType = $reservedRange->getRangeType($this);
  48. if ($rangeType !== null) {
  49. break;
  50. }
  51. }
  52. $this->rangeType = $rangeType === null ? $defaultType : $rangeType;
  53. }
  54. }
  55. return $this->rangeType === false ? null : $this->rangeType;
  56. }
  57. /**
  58. * {@inheritdoc}
  59. *
  60. * @see \IPLib\Range\RangeInterface::getAddressAtOffset()
  61. */
  62. public function getAddressAtOffset($n)
  63. {
  64. if (is_int($n)) {
  65. $positive = $n >= 0;
  66. } elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') {
  67. $n = $s;
  68. $positive = $n[0] !== '-';
  69. } else {
  70. return null;
  71. }
  72. if ($positive) {
  73. $start = Factory::parseAddressString($this->getComparableStartString());
  74. /** @var \IPLib\Address\AddressInterface $start */
  75. $address = $start->getAddressAtOffset($n);
  76. } else {
  77. $end = Factory::parseAddressString($this->getComparableEndString());
  78. /** @var \IPLib\Address\AddressInterface $end */
  79. $nPlus1 = is_int($n) ? $n + 1 : BinaryMath::getInstance()->add1ToIntegerString($n);
  80. $address = $end->getAddressAtOffset($nPlus1);
  81. }
  82. if ($address === null) {
  83. return null;
  84. }
  85. return $this->contains($address) ? $address : null;
  86. }
  87. /**
  88. * {@inheritdoc}
  89. *
  90. * @see \IPLib\Range\RangeInterface::contains()
  91. */
  92. public function contains(AddressInterface $address)
  93. {
  94. $result = false;
  95. if ($address->getAddressType() === $this->getAddressType()) {
  96. $cmp = $address->getComparableString();
  97. $from = $this->getComparableStartString();
  98. if ($cmp >= $from) {
  99. $to = $this->getComparableEndString();
  100. if ($cmp <= $to) {
  101. $result = true;
  102. }
  103. }
  104. }
  105. return $result;
  106. }
  107. /**
  108. * {@inheritdoc}
  109. *
  110. * @see \IPLib\Range\RangeInterface::containsRange()
  111. */
  112. public function containsRange(RangeInterface $range)
  113. {
  114. $result = false;
  115. if ($range->getAddressType() === $this->getAddressType()) {
  116. $myStart = $this->getComparableStartString();
  117. $itsStart = $range->getComparableStartString();
  118. if ($itsStart >= $myStart) {
  119. $myEnd = $this->getComparableEndString();
  120. $itsEnd = $range->getComparableEndString();
  121. if ($itsEnd <= $myEnd) {
  122. $result = true;
  123. }
  124. }
  125. }
  126. return $result;
  127. }
  128. /**
  129. * {@inheritdoc}
  130. *
  131. * @see \IPLib\Range\RangeInterface::split()
  132. */
  133. public function split($networkPrefix, $forceSubnet = false)
  134. {
  135. $networkPrefix = (int) $networkPrefix;
  136. $myNetworkPrefix = $this->getNetworkPrefix();
  137. if ($networkPrefix === $myNetworkPrefix) {
  138. return array(
  139. $forceSubnet ? $this->asSubnet() : $this,
  140. );
  141. }
  142. if ($networkPrefix < $myNetworkPrefix) {
  143. throw new OutOfBoundsException("The value of the \$networkPrefix parameter can't be smaller than the network prefix of the range ({$myNetworkPrefix})");
  144. }
  145. $startIp = $this->getStartAddress();
  146. $maxPrefix = $startIp::getNumberOfBits();
  147. if ($networkPrefix > $maxPrefix) {
  148. throw new OutOfBoundsException("The value of the \$networkPrefix parameter can't be larger than the maximum network prefix of the range ({$maxPrefix})");
  149. }
  150. switch ($startIp->getAddressType()) {
  151. case AddressType::T_IPv4:
  152. $one = IPv4::fromBytes(array(0, 0, 0, 1));
  153. break;
  154. case AddressType::T_IPv6:
  155. $one = IPv6::fromBytes(array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1));
  156. break;
  157. }
  158. /** @var \IPLib\Address\AddressInterface $one */
  159. $delta = $one->shift($networkPrefix - $maxPrefix);
  160. $result = array();
  161. while (true) {
  162. $range = Subnet::parseString("{$startIp}/{$networkPrefix}");
  163. /** @var Subnet $range */
  164. if (!$forceSubnet && $this instanceof Pattern) {
  165. $range = $range->asPattern() ?: $range;
  166. }
  167. $result[] = $range;
  168. $startIp = $startIp->add($delta);
  169. if ($startIp === null || !$this->contains($startIp)) {
  170. break;
  171. }
  172. }
  173. return $result;
  174. }
  175. }