| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- <?php
- namespace IPLib\Range;
- use IPLib\Address\AddressInterface;
- use IPLib\Address\IPv4;
- use IPLib\Address\IPv6;
- use IPLib\Address\Type as AddressType;
- use IPLib\ParseStringFlag;
- use IPLib\Service\BinaryMath;
- /**
- * Represents an address range in pattern format (only ending asterisks are supported).
- *
- * @example 127.0.*.*
- * @example ::/8
- */
- class Pattern extends AbstractRange
- {
- /**
- * Starting address of the range.
- *
- * @var \IPLib\Address\AddressInterface
- */
- protected $fromAddress;
- /**
- * Final address of the range.
- *
- * @var \IPLib\Address\AddressInterface
- */
- protected $toAddress;
- /**
- * Number of ending asterisks.
- *
- * @var int
- */
- protected $asterisksCount;
- /**
- * The type of the range of this IP range.
- *
- * @var int|false|null false if this range crosses multiple range types, null if yet to be determined
- *
- * @since 1.5.0
- */
- protected $rangeType;
- /**
- * Initializes the instance.
- *
- * @param \IPLib\Address\AddressInterface $fromAddress
- * @param \IPLib\Address\AddressInterface $toAddress
- * @param int $asterisksCount
- */
- public function __construct(AddressInterface $fromAddress, AddressInterface $toAddress, $asterisksCount)
- {
- $this->fromAddress = $fromAddress;
- $this->toAddress = $toAddress;
- $this->asterisksCount = $asterisksCount;
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::__toString()
- */
- public function __toString()
- {
- return $this->toString();
- }
- /**
- * @deprecated since 1.17.0: use the parseString() method instead.
- * For upgrading:
- * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
- *
- * @param string|mixed $range
- * @param bool $supportNonDecimalIPv4
- *
- * @return static|null
- *
- * @see \IPLib\Range\Pattern::parseString()
- * @since 1.10.0 added the $supportNonDecimalIPv4 argument
- */
- public static function fromString($range, $supportNonDecimalIPv4 = false)
- {
- return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
- }
- /**
- * Try get the range instance starting from its string representation.
- *
- * @param string|mixed $range
- * @param int $flags A combination or zero or more flags
- *
- * @return static|null
- *
- * @since 1.17.0
- * @see \IPLib\ParseStringFlag
- */
- public static function parseString($range, $flags = 0)
- {
- if (!is_string($range) || strpos($range, '*') === false) {
- return null;
- }
- if ($range === '*.*.*.*') {
- return new static(IPv4::parseString('0.0.0.0'), IPv4::parseString('255.255.255.255'), 4);
- }
- if ($range === '*:*:*:*:*:*:*:*') {
- return new static(IPv6::parseString('::'), IPv6::parseString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
- }
- $matches = null;
- if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
- $asterisksCount = strlen($matches[1]) >> 1;
- if ($asterisksCount > 0) {
- $missingDots = 3 - substr_count($range, '.');
- if ($missingDots > 0) {
- $range .= str_repeat('.*', $missingDots);
- $asterisksCount += $missingDots;
- }
- }
- $fromAddress = IPv4::parseString(str_replace('*', '0', $range), $flags);
- if ($fromAddress === null) {
- return null;
- }
- $fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount);
- $otherBytes = array_fill(0, $asterisksCount, 255);
- $toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes));
- return new static($fromAddress, $toAddress, $asterisksCount);
- }
- if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
- $asterisksCount = strlen($matches[1]) >> 1;
- $fromAddress = IPv6::parseString(str_replace('*', '0', $range));
- if ($fromAddress === null) {
- return null;
- }
- $fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount);
- $otherWords = array_fill(0, $asterisksCount, 0xffff);
- $toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords));
- return new static($fromAddress, $toAddress, $asterisksCount);
- }
- return null;
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::toString()
- */
- public function toString($long = false)
- {
- if ($this->asterisksCount === 0) {
- return $this->fromAddress->toString($long);
- }
- switch (true) {
- case $this->fromAddress instanceof \IPLib\Address\IPv4:
- $chunks = explode('.', $this->fromAddress->toString());
- $chunks = array_slice($chunks, 0, -$this->asterisksCount);
- $chunks = array_pad($chunks, 4, '*');
- $result = implode('.', $chunks);
- break;
- case $this->fromAddress instanceof \IPLib\Address\IPv6:
- if ($long) {
- $chunks = explode(':', $this->fromAddress->toString(true));
- $chunks = array_slice($chunks, 0, -$this->asterisksCount);
- $chunks = array_pad($chunks, 8, '*');
- $result = implode(':', $chunks);
- } elseif ($this->asterisksCount === 8) {
- $result = '*:*:*:*:*:*:*:*';
- } else {
- $bytes = $this->toAddress->getBytes();
- $bytes = array_slice($bytes, 0, -$this->asterisksCount * 2);
- $bytes = array_pad($bytes, 16, 1);
- $address = IPv6::fromBytes($bytes);
- $before = substr($address->toString(false), 0, -strlen(':101') * $this->asterisksCount);
- $result = $before . str_repeat(':*', $this->asterisksCount);
- }
- break;
- default:
- throw new \Exception('@todo'); // @codeCoverageIgnore
- }
- return $result;
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getAddressType()
- */
- public function getAddressType()
- {
- return $this->fromAddress->getAddressType();
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getStartAddress()
- */
- public function getStartAddress()
- {
- return $this->fromAddress;
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getEndAddress()
- */
- public function getEndAddress()
- {
- return $this->toAddress;
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getComparableStartString()
- */
- public function getComparableStartString()
- {
- return $this->fromAddress->getComparableString();
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getComparableEndString()
- */
- public function getComparableEndString()
- {
- return $this->toAddress->getComparableString();
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::asSubnet()
- * @since 1.8.0
- */
- public function asSubnet()
- {
- return new Subnet($this->getStartAddress(), $this->getEndAddress(), $this->getNetworkPrefix());
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::asPattern()
- */
- public function asPattern()
- {
- return $this;
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getSubnetMask()
- */
- public function getSubnetMask()
- {
- if ($this->getAddressType() !== AddressType::T_IPv4) {
- return null;
- }
- switch ($this->asterisksCount) {
- case 0:
- $bytes = array(255, 255, 255, 255);
- break;
- case 4:
- $bytes = array(0, 0, 0, 0);
- break;
- default:
- $bytes = array_pad(array_fill(0, 4 - $this->asterisksCount, 255), 4, 0);
- break;
- }
- return IPv4::fromBytes($bytes);
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
- */
- public function getReverseDNSLookupName()
- {
- return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName();
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getSize()
- */
- public function getSize()
- {
- $fromAddress = $this->fromAddress;
- $maxPrefix = $fromAddress::getNumberOfBits();
- $prefix = $this->getNetworkPrefix();
- return pow(2, $maxPrefix - $prefix);
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getExactSize()
- */
- public function getExactSize()
- {
- $fromAddress = $this->fromAddress;
- $maxPrefix = $fromAddress::getNumberOfBits();
- $prefix = $this->getNetworkPrefix();
- return BinaryMath::getInstance()->pow2string($maxPrefix - $prefix);
- }
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::getNetworkPrefix()
- */
- public function getNetworkPrefix()
- {
- switch ($this->getAddressType()) {
- case AddressType::T_IPv4:
- return 8 * (4 - $this->asterisksCount);
- case AddressType::T_IPv6:
- return 16 * (8 - $this->asterisksCount);
- }
- }
- }
|