AbstractRange.php 5.5 KB

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