src/Range.php
<?php
namespace IPTools;
use IPTools\Exception\RangeException;
use ReturnTypeWillChange;
/**
* @author Safarov Alisher <alisher.safarov@outlook.com>
* @link https://github.com/S1lentium/IPTools
*/
class Range implements \Iterator, \Countable
{
use PropertyTrait;
/**
* @var IP
*/
private $firstIP;
/**
* @var IP
*/
private $lastIP;
/**
* @var int
*/
private $position = 0;
/**
* @param IP $firstIP
* @param IP $lastIP
* @throws RangeException
*/
public function __construct(IP $firstIP, IP $lastIP)
{
$this->setFirstIP($firstIP);
$this->setLastIP($lastIP);
}
/**
* @param string $data
* @return Range
*/
public static function parse($data)
{
if (strpos($data,'/') || strpos($data,' ')) {
$network = Network::parse($data);
$firstIP = $network->getFirstIP();
$lastIP = $network->getLastIP();
} elseif (strpos($data, '*') !== false) {
$firstIP = IP::parse(str_replace('*', '0', $data));
$lastIP = IP::parse(str_replace('*', '255', $data));
} elseif (strpos($data, '-')) {
list($first, $last) = explode('-', $data, 2);
$firstIP = IP::parse($first);
$lastIP = IP::parse($last);
} else {
$firstIP = IP::parse($data);
$lastIP = clone $firstIP;
}
return new self($firstIP, $lastIP);
}
/**
* @param IP|Network|Range $find
* @return bool
* @throws RangeException
*/
public function contains($find)
{
if ($find instanceof IP) {
$within = (strcmp($find->inAddr(), $this->firstIP->inAddr()) >= 0)
&& (strcmp($find->inAddr(), $this->lastIP->inAddr()) <= 0);
} elseif ($find instanceof Range || $find instanceof Network) {
/**
* @var Network|Range $find
*/
$within = (strcmp($find->getFirstIP()->inAddr(), $this->firstIP->inAddr()) >= 0)
&& (strcmp($find->getLastIP()->inAddr(), $this->lastIP->inAddr()) <= 0);
} else {
throw new RangeException('Invalid type');
}
return $within;
}
/**
* @param IP $ip
* @throws RangeException
*/
public function setFirstIP(IP $ip)
{
if ($this->lastIP && strcmp($ip->inAddr(), $this->lastIP->inAddr()) > 0) {
throw new RangeException('First IP is grater than second');
}
$this->firstIP = $ip;
}
/**
* @param IP $ip
* @throws RangeException
*/
public function setLastIP(IP $ip)
{
if ($this->firstIP && strcmp($ip->inAddr(), $this->firstIP->inAddr()) < 0) {
throw new RangeException('Last IP is less than first');
}
$this->lastIP = $ip;
}
/**
* @return IP
*/
public function getFirstIP()
{
return $this->firstIP;
}
/**
* @return IP
*/
public function getLastIP()
{
return $this->lastIP;
}
/**
* @return Network[]
*/
public function getNetworks()
{
$span = $this->getSpanNetwork();
$networks = array();
if ($span->getFirstIP()->inAddr() === $this->firstIP->inAddr()
&& $span->getLastIP()->inAddr() === $this->lastIP->inAddr()
) {
$networks = array($span);
} else {
if ($span->getFirstIP()->inAddr() !== $this->firstIP->inAddr()) {
$excluded = $span->exclude($this->firstIP->prev());
foreach ($excluded as $network) {
if (strcmp($network->getFirstIP()->inAddr(), $this->firstIP->inAddr()) >= 0) {
$networks[] = $network;
}
}
}
if ($span->getLastIP()->inAddr() !== $this->lastIP->inAddr()) {
if (!$networks) {
$excluded = $span->exclude($this->lastIP->next());
} else {
$excluded = array_pop($networks);
$excluded = $excluded->exclude($this->lastIP->next());
}
foreach ($excluded as $network) {
$networks[] = $network;
if ($network->getLastIP()->inAddr() === $this->lastIP->inAddr()) {
break;
}
}
}
}
return $networks;
}
/**
* @return Network
*/
public function getSpanNetwork()
{
$xorIP = IP::parseInAddr($this->getFirstIP()->inAddr() ^ $this->getLastIP()->inAddr());
preg_match('/^(0*)/', $xorIP->toBin(), $match);
$prefixLength = strlen($match[1]);
$ip = IP::parseBin(str_pad(substr($this->getFirstIP()->toBin(), 0, $prefixLength), $xorIP->getMaxPrefixLength(), '0'));
return new Network($ip, Network::prefix2netmask($prefixLength, $ip->getVersion()));
}
/**
* @return IP
*/
#[ReturnTypeWillChange]
public function current()
{
return $this->firstIP->next($this->position);
}
/**
* @return int
*/
#[ReturnTypeWillChange]
public function key()
{
return $this->position;
}
/**
* @return void
*/
#[ReturnTypeWillChange]
public function next()
{
++$this->position;
}
/**
* @return void
*/
#[ReturnTypeWillChange]
public function rewind()
{
$this->position = 0;
}
/**
* @return bool
*/
#[ReturnTypeWillChange]
public function valid()
{
return strcmp($this->firstIP->next($this->position)->inAddr(), $this->lastIP->inAddr()) <= 0;
}
/**
* @return int
*/
#[ReturnTypeWillChange]
public function count()
{
return (integer)bcadd(bcsub($this->lastIP->toLong(), $this->firstIP->toLong()), 1);
}
}