2022-09-13 13:13:11 +00:00
|
|
|
<?php
|
|
|
|
// IPAddressRange.php
|
|
|
|
// Created: 2021-04-26
|
2023-01-01 19:00:00 +00:00
|
|
|
// Updated: 2023-01-01
|
2022-09-13 13:13:11 +00:00
|
|
|
|
|
|
|
namespace Index\Net;
|
|
|
|
|
|
|
|
use InvalidArgumentException;
|
|
|
|
use JsonSerializable;
|
|
|
|
use Stringable;
|
|
|
|
use Index\IEquatable;
|
|
|
|
|
|
|
|
final class IPAddressRange implements JsonSerializable, Stringable, IEquatable {
|
|
|
|
private IPAddress $base;
|
|
|
|
private int $mask;
|
|
|
|
|
|
|
|
public function __construct(IPAddress $base, int $mask) {
|
|
|
|
$this->base = $base;
|
|
|
|
$this->mask = $mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBaseAddress(): IPAddress {
|
|
|
|
return $this->base;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getMask(): int {
|
|
|
|
return $this->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCIDR(): string {
|
|
|
|
return ((string)$this->base) . '/' . $this->getMask();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function __toString(): string {
|
|
|
|
return $this->getCIDR();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function equals(mixed $other): bool {
|
|
|
|
return $other instanceof IPAddressRange
|
|
|
|
&& $this->mask === $other->mask
|
|
|
|
&& $this->base->equals($other->base);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function match(IPAddress $address): bool {
|
|
|
|
$width = $this->base->getWidth();
|
|
|
|
if($address->getWidth() !== $width)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if($this->mask < 1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
$base = $this->base->getRaw();
|
|
|
|
$match = $address->getRaw();
|
|
|
|
$remaining = $this->mask;
|
|
|
|
|
|
|
|
for($i = 0; $i < $width && $remaining > 0; ++$i) {
|
|
|
|
$b = ord($base[$i]);
|
|
|
|
$m = ord($match[$i]);
|
|
|
|
|
|
|
|
$remainingMod = $remaining % 8;
|
|
|
|
if($remainingMod) {
|
|
|
|
$mask = 0xFF << (8 - $remainingMod);
|
|
|
|
$b &= $mask;
|
|
|
|
$m &= $mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
if($b !== $m)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
$remaining -= 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function jsonSerialize(): mixed {
|
|
|
|
return $this->getCIDR();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function __serialize(): array {
|
|
|
|
return [
|
|
|
|
'b' => $this->base->getRaw(),
|
|
|
|
'm' => $this->mask,
|
|
|
|
];
|
|
|
|
}
|
|
|
|
public function __unserialize(array $serialized): void {
|
|
|
|
$this->base = new IPAddress($serialized['b']);
|
|
|
|
$this->mask = $serialized['m'];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function parse(string $cidr): IPAddressRange {
|
|
|
|
$parts = explode('/', $cidr, 2);
|
|
|
|
if(empty($parts[0]) || empty($parts[1]))
|
|
|
|
throw new InvalidArgumentException('$cidr is not a valid CIDR range.');
|
2023-01-01 19:00:00 +00:00
|
|
|
return new static(IPAddress::parse($parts[0]), (int)$parts[1]);
|
2022-09-13 13:13:11 +00:00
|
|
|
}
|
|
|
|
}
|